Creating a COM object in ASM

Creating a COM object in ASM

Copyright © Dec 27, 2000 by Ernest Murphy ernie@surfree.com For educational use only. All commercial use only by written license.  

IUnknown * pUnkOuter, //Pointer to outer object when part of an

// aggregate REFIID riid,

Reference to the interface identifier

Oid** ppvObject); //Address of output variable that receives

The interface pointer requested in riid

HRESULT LockServer(BOOL fLock);

Increments or decrements the lock count

LockServer keeps the class factory instanced (helpful if one has to make numerous object copies). CreateInstance is the workhorse here, it is used…   This indirection is useful if the class needs some special initiation. IClassFactory is necessary to handle…

Declare the ClassFactory object structure

ClassFactoryObject STRUCT

LpVtbl DWORD 0 ; function table pointer

NRefCount DWORD 0 ; object and reference count

ClassFactoryObject ENDS

 

Declare the MyCom object structure

MyComObject STRUCT

LpVtbl DWORD 0 ; function table pointer

NRefCount DWORD 0 ; reference count

NValue DWORD 0 ; interface private data

MyComObject ENDS

The first point is I have great latitude in defining this structure. The only element that the COM contract imposes on it is that it contain a DWORD…   MyCom, a Simple Interface

MyCom.idl : IDL source for MyCom.dll

//

This file will be processed by the MIDL tool to

Produce the type library (MyCom.tlb) and marshalling code

import "oaidl.idl";

import "ocidl.idl";

[

object,

uuid(F8CE5E41-1135-11d4-A324-0040F6D487D9),

helpstring("IMyCom Interface"),

pointer_default(unique)

]

Interface IMyCom : IUnknown

[propget, helpstring("property Value")] HRESULT Value([out, retval] long *pVal); [propput, helpstring("property Value")]

Library MyComLib

{

importlib("stdole32.tlb");

importlib("stdole2.tlb");

[

uuid(F8CE5E43-1135-11d4-A324-0040F6D487D9),

helpstring("MyCom Class")

]

Coclass MyCom

[default] interface IMyCom; }; };

GetValue PROTO :DWORD, :DWORD

SetValue PROTO :DWORD, :DWORD

RaiseValue PROTO :DWORD, :DWORD

BIG difference... but for a simple reason. Interfaces written for type libraries are as general as can be, and are directed at clients such as…   To create the type lib, use MIDL on a command line like so:

MIDL MyCom.idl

 

This produces several output files which you can mostly ignore, and most importantly MyCom.tlb, our type library. This library should be added to the dll resource file with

 

Typelib MyCom.tlb

Making it the first resource element is important, as later on we will be using the LoadTypeLib API function to extract this library, and this…    

HKEY_CLASSES_ROOTCMyCom

(Default) "CMyCom simple client"

HKEY_CLASSES_ROOTCMyComCLSID

(Default) "{A21A8C43-1266-11D4-A324-0040F6D487D9}"

 

HKEY_CLASSES_ROOTCLSID{A21A8C43-1266-11D4-A324-0040F6D487D9}

(Default) "CMyCom simple client"

HKEY_CLASSES_ROOTCLSID{A21A8C43-1266-11D4-A324-0040F6D487D9}CMyCom

(Default) "CMyCom"

HKEY_CLASSES_ROOTCLSID{A21A8C43-1266-11D4-A324-0040F6D487D9}InprocServer32

(Default) "C:MASM32MYCOMMYCOM.DLL"

ThreadingModel "Single"

 

HKEY_CLASSES_ROOTTypeLib{A21A8C42-1266-11D4-A324-0040F6D487D9}

(Default) (value not set)

HKEY_CLASSES_ROOTTypeLib{A21A8C42-1266-11D4-A324-0040F6D487D9}1.0

(Default) "MyCom 1.0 Type Library"

HKEY_CLASSES_ROOTTypeLib{A21A8C42-1266-11D4-A324-0040F6D487D9}1.0

(Default) (value not set)

HKEY_CLASSES_ROOTTypeLib{A21A8C42-1266-11D4-A324-0040F6D487D9}1.0win32

(Default) " C:masm32COMMyCom MYCOM.DLL"

HKEY_CLASSES_ROOTTypeLib{A21A8C42-1266-11D4-A324-0040F6D487D9}1.0FLAGS

(Default) "O"

HKEY_CLASSES_ROOTTypeLib{A21A8C42-1266-11D4-A324-0040F6D487D9}1.0HELPDIR

  One key value here is variable, that is the path and name of the server dll…  

AddRef_MC proc this_:DWORD

Mov eax, this_

Inc (MyComObject ptr [eax]).nRefCount

Mov eax, (MyComObject ptr [eax]).nRefCount

Ret ; note we return the object count

AddRef_MC endp

AddRef is a bit unusual in that it does not return a HRESULT (failure code), instead it returns the object count. The return value is undefined in…   Release not only has to decrement the object count, but when this count reaches zero it must both delete the object,…

Release_MC proc this_:DWORD

Mov eax, this_

Dec (MyComObject ptr [eax]).nRefCount

Mov eax, (MyComObject ptr [eax]).nRefCount

.IF (eax == 0)

The reference count has dropped to zero

No one holds reference to the object

So let's delete it

Invoke CoTaskMemFree, this_

Dec MyCFObject.nRefCount

xor eax, eax ; clear eax (count = 0)

ENDIF

Ret ; note we return the object count

Release_MC endp

 

MyCom is also a trivial interface to implement. The MyCom object has an extra member where the 'value' property is held.

 

GetValue proc this_:DWORD, pval:DWORD

Mov eax, this_

Mov eax, (MyComObject ptr [eax]).nValue

Mov edx, pval

Mov [edx], eax

Xor eax, eax ; return S_OK

Ret

GetValue endp

SetValue proc this_:DWORD, val:DWORD

Mov eax, this_

Mov edx, val

Mov (MyComObject ptr [eax]).nValue, edx

Xor eax, eax ; return S_OK

Ret

SetValue endp

RaiseValue PROC this_:DWORD, val:DWORD

Mov eax, this_

Mov edx, val

Add (MyComObject ptr [eax]).nValue, edx

Xor eax, eax ; return S_OK

Ret

RaiseValue ENDP

  MyCom.dll, the server code …

Option Explicit

Private MC As New MyCom

Private Sub Command1_Click()

MC.Raise (Text2) Text1 = MC.Value

End Sub

 

Private Sub Form_Load()

Set MC = New MyCom

MC.Value = 100

Text1 = MC.Value

End Sub

Now you can run the application and test the server by clicking the Raise button. Do be careful, there is no error checking to see if you put a…   Note the sample program available for download is somewhat more complex, as it creates two copies of the server object…