by Artaxerxes on Mon Nov 24, 2008 8:22 am
Well... don't know how much you read already, but In COM events work sort of like this: my app defines and declares a class (actually an interface definition, not a class) in its typelibrary (for each object that has events), without implementing it. The member-function of such a 'class' have the prototype of the events I'd like to expose. An app that wants to get those events, must provide an implementation for that class and hand me a (pointer to) an object instance of that class. So instead of getting function pointers, I get an object, with its members being the events to trigger. So all my app has to do is call those member functions. Now that might sound simple, but in fact, since UOAI and your program implementing the event-object might be two different applications, "calling" the member on the object you passed me, means I'm calling a function in another app. Now, that is actually what COM does for me: it sets up a duplicate object in my program, with memberfunctions that will call the remote object's corresponding members through Inter Process Communication (mostly through windows messages). However, for scripting languages another facility is provided: calls can be made through the IDispatch interface. If every interface in my app is derived from IDispatch (has its first member-functions the same as IDispatch) then a scripting language can call all members through IDispatch and does not have to know all other members. This is convenient, since f.e. if I call UOAI.LaunchClient in vbScript, then vbScript has no idea at what offset LaunchClient is in my interface, so it cant call it directly. VbScript then simply assumes that the UOAI class (interface) starts with IDispatch-functions, of which it does know the offsets, and simply calls: IDispatch.GetIDsOfNames(... "LaunchClient" ) which looks up an ID (dispid) for the "LaunchClient" method and then it can call that method through IDispatch.Invoke( ... dispid ... ).
Now, in visual basic 6, that's also the only way that events are supported: vb generates an object for me sticking to the event-class (interface) I defined, but it only supports calls through the IDispatch methods, not direct access to all members. So when triggering an event I have to call those members on the remote object by calling its IDispatch.Invoke(...event's dispid...). For performance reasons, instead of looking up that dispID each time (which requires text-comparisons and thus is slow), I predefine the DispIDs for the members in my typelibrary (which Visual Basic can read). So I have f.e. member-function with ID 0 on the UOAICallback class (interface) is the OnClientStart event, so if a client is detected and my program has an eventobject of that class (which it will if your application setup eventhandlers) then it will call eventobject.Invoke(... ID: 0 ...).
The problem is that even though C# sets up the eventobject correctly and passes it to my app successfully, the member-functions in the eventobject I got from C# does not use the right DISPIDs. So, even though visual basic seems to use the correct DISPIDs, it seems like some information is missing in my typelibrary to tell C# that those DISPIDs have to be used.
Now, I can solve this by calling the IDispatch.GetIDsOfNames(...) function, looking up the dispids C# uses, instead of using the predefined dispids. This will require text-lookup, so a performance loss, but if I can do this at the point where i get the eventobject (so each time you do "UOAI.onClientStart+=new ...delegate...(eventhandler_func);"), I would minimize performance issues (compared to looking up the dispid each time I invoke the event).
I think, however, that I know what information is missing in my typelibrary: i think the event-interfaces (classes) have to be marked with the "oleautomation" attribute to get C# to set-up the IDispatch-interface correctly (including the correct DISPIDs), but I haven't tried that yet.
Sorry for the extremely long post... I hope it was clear in some way, if not plz ask!