C# event handler cu system reflection

Salut,

Am incercat de curand sa apelez niste metode si sa citesc niste atribute dintr-un COM library cu reflection.
Ideea e ca vreau sa fac subscribe la niste evenimente si nu mi-a iesit.

A lucrat careva cu system.reflection?

Ps: o sa pun maine dimineata un exemplu de cod.

Pana cand nu pui un exemplu sa vedem ce ai facut, nu avem cum sa ne dam seama. Tee pot ajuta eu daca pui aici niste cod concret.

Am asa:
Console application cu urmatoarele:

Using COMlib;

namespace test
{
    class Program
    {
         private static void kxc_operationComplete(object result)
         {

          }
          
          static void Main(string[] args)
          {
               COMclass kxc = new COMclass();
               kxc.Init();
               kxc.OperationComplete += kxc_operationComplete;
               kxc.Operate();
          }
    }

}

A luat cam aiurea codul:
Ideea e urmatoarea: eventul de operationComplete arunca Object result. Si eu am nevoie de acel result.
Si operationcomplete se triggeruieste dupe apelul functiei Operate().

Eu am inceput asa:

//Aduc tipul din active x
var COMtype = Type.GetTypeFromCLSID(Guid.Parse(CLSID.objcode), true);
//Instantiez obiectul
object COMobj = Activator.CreateInstance(COMtype);
//Apelez metoda de init
COMtype.InvokeMember(“Init”, BindingFlags.InvokeMethod, null, COMobj, null);

Ca sa fac un handler vad ca trebuie sa aduc eventul urmarit din com class, numai ca daca ii dau asa:
EventInfo ei = COMtype.GetEvent(“OperationComplete”);

nu imi aduce nimic.

OK, problema e clasica pentru interoperabilitate cu COM.

Solutia este clasa statica ComEventsHelper, cu cele doua metode ale sale, Combine (adauga event handler) si Remove (sterge event handler). Sper ca, folosind aceasta informatie, te descurci mai departe.

Motivul pentru care nu functioneaza Reflection cum ai vrea tu este ca obiectele COM nu sunt obiecte standard .NET. Ceea ce vezi tu ca si event handling este mai mult eye-candy pentru developeri, insa, in spate, lucrurile nu functioneaza in acest fel. Compilatorul face multe modificari, si, drept rezultat, ceea ce tie la compile time iti functioneaza, nu iti va mai exista la run-time, prin urmare Reflection nu le va mai vedea.

Ca regula generala, incearca sa eviti late binding catre obiecte COM if you can avoid it. Clasele wrapper pe care .NET le genereaza automat sunt acolo cu un scop.

1 Like

Salut,

Multumesc pentru raspuns.
M-am uitat la functia mentionata de tine si nu prea inteleg cum sa fac legaturile.
public static void Combine (object rcw, Guid iid, int dispid, Delegate d);

rcw - este target object, adica COMobject
Guid - il am din Guid.Parse
dispid - este
Si nu stiu cum sa leg de eventul “operationComplete”.

Daca ma poti indruma ar fi perfect.

Clasele wrapper sunt cele apar direct in COM, cand adaug o referinta?

Mersi.

Am incercat asa, dar tot nu merge:

var method = Type.GetType(“test.Program”).GetMethod(“kxc_operationComplete”);
var tArgs = new List();
foreach(var param in method.GetParameters())
tArgs.Add(param.ParameterType);
tArgs.Add(method.ReturnType);
var delDeclType = Expression.GetDelegateType(tArgs.ToArray);
Delegate d = Delegate.CreateDelegate(delDeclType, method);
ComEventsHelper.Combine(COMobj, guid, 1, d);

Si la combine bubuie.

Apropos de ComEventsHelper: nu stiu de unde ai gasit clasa asta, ca nu cred ca multa lume din univers stie de ea.

Mersi.

Da, si sunt “eye-candy”, adica nu exista concret sub forma care o vezi tu din editorul de text.

Maaaaaaaama… reinventezi putin roata.

Pentru ca sa dai un delegate, poti folosi:

ComEventsHelper.Combine(COMobj, guid, 1, kxc_operationComplete); ?

Sau, daca nu realizeaza ce tip implicit de delegate sa foloseasca, mai degraba:

ComEventsHelper.Combine(COMobj, guid, 1, new Action<Object>(kxc_operationComplete)); ?

Tu ai facut urmatoarele in codul tau: ai analizat tipul in care te afli, ai luat MethodInfo pentru kxc_operationComplete, ai adaugat atat parametri cat si return type-ul intr-o lista, dupa care ai cerut sa ti se dea un Func cu argumentele acelea, din care ti-ai obtinut o instanta de delegate static, pe care ai trimis-o mai departe.

Nu imi dau seama cum arata obiectele doar din ce ai scris aici, asa ca nu imi dau seama daca e bine, dar sunt tentat sa cred ca nu. In orice caz, cu siguranta e un mod foarte neoptim de a scrie.

Asa este. Si nul dintre telurile mele in viata este ca sa existe cat mai putini care sa fie nevoiti sa auda de ea. Tu, ca si mine, ai fost ghinionist, insa poti propovadui si tu mai departe programatorilor tineri sa nu se atinga de nimic ce contine “COM” daca pot sa rezolve altfel :slight_smile: Glumesc, evident, insa COM chiar nu e un lucru usor.

3 Likes

Mersi mult. Revin maine cu testul. Seara buna!

Update test:
Exception thrown: ‘System.Runtime.InteropServices.COMException’ in mscorlib.dll
Additional information: Exception from HRESULT: 0x80040200

ce nu inteleg eu: cum se mapeaza eventul OperationComplete al clasei COMobj cu metoda kxc_operationComplete din clasa Program.

So, exceptia este buna, asta inseamna ca ai reusit sa faci legatura, si si sa chemi ceva din COM.

Partea proasta este ca, din acest moment, you are on your own, in sensul ca trebuie sa te asiguri ca ceea ce chemi tu actually poate executa. HRESULT-ul pe care il ai indica o problema in codul executat din COM, sau o problema de inregistrare.

Event system-ul din COM este foarte asemanator cu conceptul de duplex RPC channel. Explicatia sistemului este foarte laborioasa, asa ca mai degraba te invit sa citesti mai multe la acest link.

Ca o explicatie scurta, .NET implementeaza obiecte numite “event sinks”, care, in spate, functioneaza ca si echivalentul lor in C++. Acestea sunt urmarite de CLR si, gratie clasei pe care ti-am dat-o, te poti atasa la evenimentele lor. Practic, clasa functioneazsa ca un wrapper .NET peste ce ar trebui sa faci in C++ ca sa consumi evenimente COM.

Cateva sfaturi legat de ce poti face mai departe:

  1. Incearca sa prinzi exceptia cu un debugger (Visual Studio?) atasat. De regula, proprietatea InnerException iti poate da mai multe detalii. Daca aceasta este null, atunci cel putin poti vedea ce help link are si poate vezi mai departe; de asemenea, verifica intotdeauna stack trace-ul, poate exceptia vine dintr-un loc in care nu te astepti.

  2. Odata ce stii ce interfata este apelata, incearca sa o apelezi manual ca si cum ar fi singura viaranta (cu wrapper, exact ca in codul care l-ai dat ca exemplu). Daca primesti acelasi tip de exceptie, atunci stii ca tu ti-ai facut treaba.

Mult succes!

2 Likes

Salut,
Mersi mult de ajutor.
Revin cu detalii.