Accessing COM Objects from Assembly

Accessing COM Objects from Assembly

Ernest Murphy ernie@surfree.com   Revised Dec 26 2000 for inclusion as part of MASM32

IUnknown STRUCT DWORD

IUnknown methods IUnknown_QueryInterface QueryInterface_Pointer ? IUnknown_AddRef AddRef_Pointer ? IUnknown_Release Release_Pointer ?

IUnknown ENDS

That's it, just 12 bytes long. It holds 3 DWORD pointers to the procedures that actually implement the methods. It is the infamous… QueryInterface_Pointer typedef ptr QueryInterface_Proto

AddRef_Pointer typedef ptr AddRef_Proto Release_Pointer typedef ptr Release_Proto

 

Finally, we define the function prototypes as follows:

 

QueryInterface_Proto typedef PROTO :DWORD, :DWORD, :DWORD

AddRef_Pointer typedef PROTO :DWORD Release_Pointer typedef PROTO :DWORD

In keeping with the MASM32 practice of "loose" type checking, function parameters are just defined as DWORDs. Lots of work to set things…   One rather big compilation on defining an interface: MASM cannot resolve forward references like this, so we have to…

And use it to find the interface structure

mov edx, [eax]

Push the function parameters onto the stack

Push OFFSET ppv2

Push OFFSET IID_ISomeOtherInterface

Push dword ppv

And then call that method

call dword ptr [eax + 0]

 

This may be accomplished using the built-in MASM 'invoke' macro as such:

 

Get pointer to the object mov eax, ppv

And use it to find the interface structure

mov edx, [eax]

; and then call that method
invoke (IUnknown PTR [edx]).IUnknown_QueryInterface, ppv,

ADDR IID_SomeOtherInterface, ADDR ppv_new

I hope you find this as wonderfully simple as I do.   Note we must pass in the pointer we used, this lets the interface know which object (literally "this"…

Coinvoke MACRO

;

Invokes an arbitrary COM interface

;

Revised 12/29/00 to check for edx as a param and force compilation error

; (thanks to Andy Car for a how-to suggestion)

Revised 7/18/00 to pass pointer in edx (not eax) to avoid confusion with

; parmas passed with ADDR (Jeremy Collake's excellent suggestion)

Revised 5/4/00 for member function name decoration

See http://ourworld.compuserve.com/homepages/ernies_world/coinvoke.htm

;

PInterface pointer to a specific interface instance

Interface the Interface's struct typedef

Function which function or method of the interface to perform

Args all required arguments

; (type, kind and count determined by the function)

;

Coinvoke MACRO pInterface:REQ, Interface:REQ, Function:REQ, args:VARARG

LOCAL istatement, arg

FOR arg, <args> ;; run thru args to see if edx is lurking in there

IFIDNI <&arg>, <edx>

.ERR <edx is not allowed as a coinvoke parameter>

ENDIF

ENDM

istatement CATSTR <invoke (Interface PTR[edx]).&Interface>,<_>,<&Function, pInterface>

IFNB <args> ;; add the list of parameter arguments if any

istatement CATSTR istatement, <, >, <&args>

ENDIF

Mov edx, pInterface

mov edx, [edx]

Istatement

ENDM

;---------------------------------------------------------------------


Thus, the same QueryInterface method as before can be invoked in a single line:


coinvoke ppv ,IUnknown, QueryInterface, ADDR IID_SomeOtherInterface,

ADDR ppnew

Note that now the name decoration is done for us by the macro.   The only 'gotcha' (well, the most obvious) is that no parameters to a COM call should be passed in edx as this…