News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

COM Events

Started by mabdelouahab, April 16, 2015, 05:50:24 PM

Previous topic - Next topic

mabdelouahab

Hi everyone
I just wanted to know how to deal with COM Object
I made a simple example by VB, Then I made another example by MASM32, linking programs..
The purpose of this work is to know how to link with the Com Object with Events.
It didn't work with me.
To see what happens I added some modifications to procedures "QueryInterface,AddRef,Release,..."
When I call the procedure "FindConnectionPoint.Advise" I found that the Class send requests to QueryInterface
What is exactly required to success of the procedure Advise?

dedndave

Quote from: mabdelouahab on April 16, 2015, 05:50:24 PM
I just wanted to know how to deal with COM Object

the answer to that is quite involved, to say the least
furthermore, to set off reading about COM is difficult
they use a lot of terminology that doesn't make sense unless you already know about COM   :lol:

i'll try to cover a few points to get you started....

first, before using COM in a program, you must CoInitialize (there are different versions of this in some cases)
when done, CoUninitialize must be called
i generally put these at the beginning and end of the program
start:  INVOKE  CoInitialize,NULL

;rest of program

        INVOKE  CoUninitialize
        INVOKE  ExitProcess,0


next, access to functions are implemented through what are essentially "vector tables"
by now, i'm sure you're familiar with PROTOtypes
well - the vectors in a COM interface table each have different prototypes, in a manner of speaking
qWord has provided us with a very nice macro to help us
there are others, but i like qWord's because using it is a little easier to understand
;METHOD macro by qWord

METHOD  MACRO   name,args:VARARG
        LOCAL   _type1,_type2
        _type1  TYPEDEF PROTO args
        _type2  TYPEDEF PTR _type1
        EXITM   <name _type2 ?>
        ENDM


now, in the past, i have used an IDispatch interface, so i already have the interface structures made
in this case, it's for the IShellDispatch interface
IShellDispatch is an IDispatch interface
and, IDispatch interface is an IUnkown interface
that probably seems very confusing - lol

i think it's safe to say that most COM interfaces are of the type IUnknown
that means, among other things, that the first few functions are of a common set
notice that qWord's METHOD macro is used to prototype a structure of vectors
;IUnknown interface vTable (structure)

IUnknown STRUCT
  METHOD(QueryInterface, _this:LPVOID,riid:LPVOID,ppvObj:LPVOID)
  METHOD(AddRef,         _this:LPVOID)
  METHOD(Release,        _this:LPVOID)
IUnknown ENDS


now, to make more sense of what i was saying before,
the IDispatch interface can be made by simply referencing IUnknown
;IDispatch interface vTable (structure)

IDispatch STRUCT
    IUnknown                 <>
  METHOD(GetTypeInfoCount, _this:LPVOID,pctinfo:LPVOID)
  METHOD(GetTypeInfo,      _this:LPVOID,iTInfo:UINT,lcid:LCID,ppTInfo:LPVOID)
  METHOD(GetIDsOfNames,    _this:LPVOID,riid:LPVOID,rgszNames:LPOLESTR,cNames:UINT,lcid:LCID,rgDispId:LPVOID)
  METHOD(dInvoke,          _this:LPVOID,dispIdMember:DWORD,riid:LPVOID,lcid:LCID,wFlags:DWORD,pDispParams:LPVOID,pVarResult:LPVOID,pExcepInfo:LPVOID,puArgErr:LPVOID)
IDispatch ENDS

all IDispatch interfaces share these common elements

IShellDispatch may now reference IDispatch
in the case of VARIANT structures, i have broken them down into individual DWORD's
this makes functions with VARIANT arguments easier to use in assembly language
;IShellDispatch interface vTable (structure)
;       vDir VARIANT struct = vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD
;vRootFolder VARIANT struct = vtRoot:DWORD,vR0:DWORD,vstrRoot:LPVOID,vRV0:DWORD

IShellDispatch STRUCT
    IDispatch                <>
  METHOD(Application,      _this:LPVOID,ppid:LPVOID)
  METHOD(Parent,           _this:LPVOID,ppid:LPVOID)
  METHOD(NameSpace,        _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD,ppsdf:LPVOID)
  METHOD(BrowseForFolder,  _this:LPVOID,Hwnd:DWORD,lpTitle:LPVOID,Options:DWORD,vtRoot:DWORD,vR0:DWORD,vstrRoot:LPVOID,vRV0:DWORD,ppsdf:LPVOID)
  METHOD(Windows,          _this:LPVOID,ppid:LPVOID)
  METHOD(Open,             _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD)
  METHOD(Explore,          _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD)
  METHOD(MinimizeAll,      _this:LPVOID)
  METHOD(UndoMinimizeALL,  _this:LPVOID)
  METHOD(FileRun,          _this:LPVOID)
  METHOD(CascadeWindows,   _this:LPVOID)
  METHOD(TileVertically,   _this:LPVOID)
  METHOD(TileHorizontally, _this:LPVOID)
  METHOD(ShutdownWindows,  _this:LPVOID)
  METHOD(Suspend,          _this:LPVOID)
  METHOD(EjectPC,          _this:LPVOID)
  METHOD(SetTime,          _this:LPVOID)
  METHOD(TrayProperties,   _this:LPVOID)
  METHOD(Help,             _this:LPVOID)
  METHOD(FindFiles,        _this:LPVOID)
  METHOD(FindComputer,     _this:LPVOID)
  METHOD(RefreshMenu,      _this:LPVOID)
  METHOD(ControlPanelItem, _this:LPVOID,bstrDir:LPVOID)
IShellDispatch ENDS


in many cases - using the features provided requires accessing yet another interface level
for example, if we want to use an IShellDispatch::NameSpace Folder object, we need another interface
it, too, is an IDispatch interface, so....
;IShellDispatch::NameSpace Folder object interface vTable (structure)
;   vItem VARIANT struct = vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD
;vOptions VARIANT struct = vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD

Folder STRUCT
    IDispatch            <>
  METHOD(getTitle,     _this:LPVOID,pbs:LPVOID)
  METHOD(Application,  _this:LPVOID,ppid:LPVOID)
  METHOD(Parent,       _this:LPVOID,ppid:LPVOID)
  METHOD(ParentFolder, _this:LPVOID,ppsf:LPVOID)
  METHOD(Items,        _this:LPVOID,ppid:LPVOID)
  METHOD(ParseName,    _this:LPVOID,bstrName:LPVOID,ppid:LPVOID)
  METHOD(NewFolder,    _this:LPVOID,bstrName:LPVOID,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
  METHOD(MoveHere,     _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
  METHOD(CopyHere,     _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
  METHOD(GetDetailsOf, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,iColumn:DWORD,pbs:LPVOID)
Folder ENDS


a note about GUID's (including CLSID's and IID's)
there is a structure defined in Hutch's windows.inc, called "GUID"
        .DATA
        ALIGN   4

CLSID_Shell         GUID <13709620h,0C279h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>
IID_IShellDispatch  GUID <0D8F015C0h,0C278h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>

IID_Folder          GUID <0BBCBDE60h,0C3FFh,11CEh,<83h,50h,44h,45h,53h,54h,0,0>>
IID_FolderItem      GUID <0FAC32C80h,0CBE4h,11CEh,<83h,50h,44h,45h,53h,54h,0,0>>
IID_FolderItems     GUID <744129E0h,0CBE5h,11CEh,<83h,50h,44h,45h,53h,54h,0,0>>
IID_IShellFolder    GUID <214E6h,0,0,<0C0h,0,0,0,0,0,0,46h>>


i'll let you chew on all that before we actually use the interfaces   :P

dedndave

a simplified example of using a COM interface...

1) CoInitialize
2) CoCreateInstance
this gives you an instance of the interface, in the form of a pointer to a vector table
you store this pointer and use it whenever a call is made
3) call the function or "method"
you pass a pointer, and it's contents - lol
4) Release - when you're done with the instance, release it
5) CoUninitialize

now, that's a simple example - in most cases, it's much more involved
we want to keep it simple to help you understand the sequence of events

        .DATA?
        ALIGN   4

pvShell    LPVOID ?

        .CODE

Start:  INVOKE  CoInitialize,NULL

;notice that pvShell is filled by this call
;normally, we would examine the return value to verify success before proceeding
;again, we're going to keep it simple

    INVOKE  CoCreateInstance,offset CLSID_Shell,NULL,CLSCTX_INPROC_SERVER,offset IID_IShellDispatch,offset pvShell

;now, to make a call, we need the "THIS" pointer content
;in this example, we make a NameSpace call that actually creates another interface instance (pvFolder)
;we aren't going to use that interface (or release it) - we just want to demonstrate an interface invoke

        mov     edx,pvShell
        mov     ecx,[edx]
        INVOKE  IShellDispatch.NameSpace[ecx],edx,VT_BSTR,0,offset bstrPathTarget,0,offset pvFolder

;when we're done using the interface, we Release it (this is a standard IUnknown procedure)

        mov     edx,pvShell
        mov     ecx,[edx]
        INVOKE  IShellDispatch.Release[ecx],edx

;now, we want to exit, so....

        INVOKE  CoUninitialize
        INVOKE  ExitProcess,0

mabdelouahab

Hello my teacher dave.
I thank you for the valuable information
But my question is much simpler
Why not work with me  Advise correctly; IConnectionPoint.Advise doesn't work with me

In the first of the program I call the method "Class1. IsMe2(a As String)" without EventsŲŒ And then I called it with Events
the method "Class1. IsMe2(a As String)" will show a message and then launch Events
Public Sub IsMe2(a As String)
    MsgBox "Is Me sec[" & a & " ]"
    RaiseEvent Ev01
End Sub

To link events, I made mrthod AdviseEvents, which links ConnectionPoint then call Advise to correlate Events
I have the problem of expressing the problem

dedndave

i've never played with connection points
it will take me some time to do some reading and understand it better   :t

https://msdn.microsoft.com/en-us/library/windows/desktop/ms694318%28v=vs.85%29.aspx

i should be able to get back to this later today

mabdelouahab

I'm sorry... ; I forgot to add REG.BAT :biggrin:
To work my attachment, open "\vbexmple" and then run Reg.bat  to register the Library
Then execute the example in  "\TestEvents"

Zen

#6
Hi, MABDELOUAHAB,
I read through the posts in this thread, and I read through the source code that you provided.
Unfortunately, I'm not experienced in programming with Visual Basic, so, I didn't understand the VB source code.
But, neither prj001 nor prj008 (in the vbexample directory) launched on my machine. And, there is no compiled executable in the TestEvents directory (although there is a compiled Obj file).
I don't quite see the point of this whole exercise, though. The Visual Basic language and, presumably, the VB compiler, are not really adequate for handling COM objects. COM evolved from C++, and, it's fairly complicated with Visual Studio, using the C++ language. I have heard unsubstantiated rumors, though, that it is possible to instantiate a COM object and execute its methods in a Visual Basic program. But, you're doing it the hard way.
Based on what I've seen with the example you've provided, I can't even tell if the vbexamples, prj001 and prj008 have linked correctly. Nothing happens when I try to run these programs.
If it were me, I'd attempt writing the whole project in MASM assembly language first. At least the MASM Forum members could understand the source code, and diagnose the problem.
In reading through the assembly language portion of your project,...I can see that you've got problems. Maybe you should rethink the whole approach.
MASM Forum member, japheth, is famous for his assembly language versions of simple COM servers and various other COM components. In fact, he used to have a website dedicated to COM and assembly language,...and a number of other very useful utilities,...but, his website no longer exists. I have a number of his example projects that I can upload here if you are interested. He structured his COM interfaces in an entirely different way than you did,
...although you do seem to understand the principles.
Here is MSDNs section about: Component Object Model (COM)
...And you might find this helpful: Translating to Visual Basic (Translating COM Object Syntax for Programming Languages)
Zen

mabdelouahab

Hi Zen;
prj001 is ActiveX EXE Component; not executable, but it  wait until the the creation of objects (Jast an TypeLibrary), Just registered by REG.Bat
prj001 Contains an object CLASS1 implement 2 dispatch _Class1 (Default) and __Class1( sink objects)
If I wanted that I only work on CLASS1 without events I've been using (masm) "CreateClass Proc", Then I call any method of CLASS1 like "Class1. IsMe2" ;******************************************** Class1. IsMe2(a As String)
lea ecx, bs
push ecx
        mov edx, Inst_Class1
        push edx   
        mov edx,[edx]   
        CALL dword ptr [edx+40]  ;IsMe2

But if I wanted to work on CLASS1 withe Events It must be create an interface "__Class1 struct" with "CreateInterface proc" then advise it with IConnectionPoint.Advise For the host Object call procedures of this interface when an events is raised

in my attachment , do not use anything in " "\vbexmple"",  just use REG to register the typelibrary
then return to the masm programe "\TestEvents", and run it to see what hapen 

dedndave

a lot of material to cover - i'm afraid i'm a bit overwhelmed - lol

however, i did disassemble the prj008.exe file
and, i used the resource hacker to examine the resources

much of the COM code resides in the MSVBVM60.DLL file   :(

Zen

MABDELOUAHAB,
This is your 'reg' file. Tell me what is supposed to happen when I execute this file.
prj001.exe /RegServer
...You know,...the way you've structured this whole project is just annoying. Do you really think I want to register a COM server (that doesn't work) on my machine ???
Regsvr32
How to register an ActiveX control (.ocx) manually
Zen

mabdelouahab

Zen
prj001.exe is just an example, Give me any example of a Com Typelibrary contains an Object with Events I'm working on it, My goal is to know how it works IConnectionPoint.Advise.

Zen

MABDELOUAHAB,
Do you know what the OLE/COM Object Viewer is ? When you have correctly registered a COM server, the type library, and the COM class, and the various COM interfaces are displayed in the OLE/COM Object Viewer. If I used the Regsvr32 utility to register your COM component, and it worked correctly (which it doesn't), your COM component could be accessed by the OLE/COM Object Viewer and the information about your type library and any registered COM classes and interfaces would be displayed on the  OLE/COM Object Viewer interface window.
Zen

Zen

MABDELOUAHAB,
Maybe this will help. It's the example I mentioned in my original post, contributed by Japheth. It's written completely in MASM assembly language.
Zen

mabdelouahab

Quote from: Zen on April 18, 2015, 06:12:00 AM
MABDELOUAHAB,
Maybe this will help. It's the example I mentioned in my original post, contributed by Japheth. It's written completely in MASM assembly language.

Zen I Thanks for the help but the example does not contain any events.
I found another very nice example  in MASM, Contains an object IMyCom2, store Value and read it (get_Value/set_Value), and then fire an event XMax if the value  is greater than 100
I worked with him without Events, But when I want to link to the event, gave me the same problem in the first example

CreateClass OK
IMyCom2. Set_Value 107 OK
IMyCom2. Get_Value m_Value=107 OK Press any key to continue ...

QueryInterface IConnectionPointContainer OK
FindConnectionPoint OK
_QIf<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<0ECC8691B,0C1DBh,04DC0h<085h,05Eh,065h,0F6h,0C5h,051h,0AFh,049h>>_QIf
_QIf<000000003,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<00000001B,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<000000000,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf2
AddRef
_QIf<000000018,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<0334D391F,00E79h,03B15h<0C9h,0FFh,0EAh,0C6h,05Dh,0D0h,07Ch,042h>>_QIf
_QIf<000000040,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<0334D391F,00E79h,03B15h<0C9h,0FFh,0EAh,0C6h,05Dh,0D0h,07Ch,042h>>_QIf
_QIf<094EA2B94,0E9CCh,049E0h<0C0h,0FFh,0EEh,064h,0CAh,08Fh,05Bh,090h>>_QIf
_QIf<0334D391F,00E79h,03B15h<0C9h,0FFh,0EAh,0C6h,05Dh,0D0h,07Ch,042h>>_QIf
_QIf<077DD1250,0139Ch,02BC3h<0BDh,095h,090h,00Ah,0CEh,0D6h,01Bh,0E5h>>_QIf
_QIf<000000019,00000h,00000h<0C0h,000h,000h,000h,000h,000h,000h,046h>>_QIf
_QIf<04C1E39E1,0E3E3h,04296h<0AAh,086h,0ECh,093h,08Dh,089h,06Eh,092h>>_QIf
Release

Does one tell me what is the meaning of the values of the riid sent to queryinterface Or what should I do to connect events ( MyEventClass  in this example)
Example needs some files, I've sent with attachments (com.zip)
Don't forget reg.bat in "\AsmEvent"

adeyblue

You probably need to implement IDispatch.Invoke. In the first example, Invoke is called with the DispId of the Ev1 function, but since Invoke returns E_NOTIMPL, nothing else happens*

It can be implemented using Microsoft's helper function, but it needs an ITypeInfo for an implementation of the interface. So the ASM app would need its own typelibrary as well as the one in the VB app.

It can be implemented manually but that involves looking up the function to call from the DispId, unpacking the parameters from the VARIANT array, calling the function, then cleaning up afterwards.

* I translated your ASM app to C++, and that's what happened