can anyone help me with the IDispatch interface? My code runs fine except it doesn't return S_OK from IDispatch::Invoke, possibly because I haven't initialised the variant structures (should I have)? The invoke doesn't have any effect on the host program either. More specifically I want to write a COM add-in for Word 2000. The code is below.

;Com Add-in for Word 2000 (DLL file)
.386
.model flat, stdcall
option casemap:none ; case sensitive
.NOLIST ; keep the list file small, don't add the std libs to it.
;--------------------------------------------------------------------------

; please make sure your paths are the same has mine.

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\advapi32.inc
include \masm32\include\oleaut32.inc
include \masm32\include\ole32.inc

include \masm32\COM\include\oaidl.inc
include \masm32\COM\include\L.inc


includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\advapi32.lib
includelib \masm32\lib\oleaut32.lib
includelib \masm32\lib\ole32.lib

;--------------------------------------------------------------------------
.LISTALL ; NOW let's list everything

; build with these settings: \masm32\bin\ml /c /Fl /Sn /coff %1.asm
; /Fl /Sn will create a small listing without all the include files
; (note placement of .NOLIST and .LISTALL bracketing the include area)

; IMyCom Interface
;------------------------------------------------------------------------------------
; public IUnknown

sIID_IMyCom TEXTEQU <{0B65AD801h, 0ABAFh, 11D0h, \
{0BBh, 8Bh, 0h, 0A0h, 0C9h, 0Fh, 27h, 44h}}>
sLIBID_MyComLib TEXTEQU <{0A21A8C43H, 01266H, 011D4H, \
{0A3H, 024H, 000H, 040H, 0F6H, 0D4H, 087H, 0D9H}}>
sCLSID_MyCom TEXTEQU <{0A21A8C42H, 01266H, 011D4H, \
{0A3H, 024H, 000H, 040H, 0F6H, 0D4H, 087H, 0D9H}}>

_vtIMyCom MACRO CastName:REQ
; IMyCom methods
_vtIDispatch CaseName
&CastName&_OnConnection comethod5 ?
&CastName&_OnDisconnection comethod3 ?
&CastName&_AddInsUpdate comethod2 ?
&CastName&_StartupComplete comethod2 ?
&CastName&_BeginShutdown comethod2 ?
&CastName&_GetProperty comethod4 ?
&CastName&_PutProperty comethod4 ?
&CastName&_CallMethod comethod6 ?
ENDM

IMyCom STRUCT
_vtIMyCom IMyCom
IMyCom ENDS

;--------------------------------------------------------------------------
; .dll exports
DllRegisterServer PROTO
DllCanUnloadNow PROTO
DllGetClassObject PROTO :DWORD, :DWORD, :DWORD
DllUnregisterServer PROTO

; our interface implimentations
QueryInterface_CF PROTO :DWORD, :DWORD, :DWORD
AddRef_CF PROTO :DWORD
Release_CF PROTO :DWORD
QueryInterface_MC PROTO :DWORD, :DWORD, :DWORD
AddRef_MC PROTO :DWORD
Release_MC PROTO :DWORD

CreateInstance PROTO :DWORD, :DWORD, :DWORD, :DWORD
OnConnection PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
OnDisconnection PROTO :DWORD, :DWORD, :DWORD
OnAddInsUpdate PROTO :DWORD, :DWORD
OnStartupComplete PROTO :DWORD, :DWORD
OnBeginShutdown PROTO :DWORD, :DWORD
GetProperty PROTO :DWORD, :DWORD, :DWORD, :DWORD
PutProperty PROTO :DWORD, :DWORD, :DWORD, :DWORD
CallMethod PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
;internal helper functions
CreateMyComObject PROTO
GuardedDeleteKey PROTO :DWORD, :DWORD

;------------------------------------------------------------------------
; declare the ClassFactory object structure
ClassFactoryObject STRUCT ; this_
; interface object
lpVtbl DWORD 0
; object data
nRefCount DWORD 1
ClassFactoryObject ENDS

; declare the MyCom object structure
MyComObject STRUCT ; this_
; interface object
lpVtbl DWORD 0
; object data
nRefCount DWORD 1
nValue DWORD 0
MyComObject ENDS

;-----------------------------------------------------------------------------
.data

; here are the actual vtables for the interfaces we need to support.
vtIClassFactory IClassFactory < QueryInterface_CF, \
AddRef_CF, \
Release_CF, \
CreateInstance, \
LockServer >

vtIMyCom IMyCom < QueryInterface_MC,\
AddRef_MC,\
Release_MC,\
GetTypeInfoCount,\
GetTypeInfo,\
GetIDsOfNames,\
Invoker,\
OnConnection,\
OnDisconnection,\
OnAddInsUpdate,\
OnStartupComplete,\
OnBeginShutdown,\
GetProperty,\
PutProperty,\
CallMethod >

; we only need a single class factory object, so let's just allow
; for it in our .data area and not bother instancing it every time.

MyCFObject ClassFactoryObject< OFFSET vtIClassFactory, \
0 >


; define the interface's IID

IID_IMyCom GUID sIID_IMyCom
CLSID_MyCom GUID sCLSID_MyCom
TYPELIB_MyCom GUID sLIBID_MyComLib
IID_IUnknown GUID sIID_IUnknown
IID_IClassFactory GUID sIID_IClassFactory
IID_NULL GUID sIID_NULL
IID_IDispatch GUID sIID_IDispatch

g_hDllMain DWORD 0 ; hInstance for the dll (process)

; strings needed to register the component

hIDispatch DWORD 0
dwDispID DWORD 0
appinf DWORD 0
dispparams DISPPARAMS <>
dispparamsb DISPPARAMS <>
vtCmdBars VARIANT <0,0,0,0>
vtCmdBar VARIANT <0,0,0,0>
vtParam VARIANT <0,0,0,0>
vtParam2 VARIANT <0,0,0,0>

error1 BYTE "error in GetIDsOfNames",0
error2 BYTE "error in Invoke",0
error3 BYTE "IDispatch Interface error",0
toolbars wchar L(<CommandBars\0>)
adds wchar L(<Item\0>)
mybarr wchar L(<Standard\0>)
pmybarr EQU OFFSET mybarr
szSampleDesc1 DWORD 0h
szSampleDesc2 BYTE "Japanese Dictionary for Word 2000/2002", 0
szSampleDesc3 BYTE "Japanese.Dictionary", 0
szSampleDesc4 DWORD 3h
szSampleDesc5 BYTE "OnConnection", 0
szSampleDesc6 BYTE "OnDisconnection", 0
szSampleDesc7 BYTE "OnAddInsUpdate", 0
szSampleDesc8 BYTE "OnStartupComplete", 0
szSampleDesc9 BYTE "OnBeginShutdown", 0

szInprocServer32 BYTE "InprocServer32", 0
szSampleProgID BYTE "Software\Microsoft\Office\Word\Addins\Japan.dict", 0
szSampleProgID2 BYTE "Japan.dict", 0
szProgID BYTE "Japan.dict", 0
szCommandLineSafe BYTE "CommandLineSafe", 0
szDescription BYTE "Description", 0
szFriendlyName BYTE "FriendlyName", 0
szLoadBehaviour BYTE "LoadBehavior", 0
szThreadModel BYTE "ThreadingModel", 0
szThreadType BYTE "Both",0
szCLSID BYTE "CLSID" ,0

;--------------------------------------------------------------------------
.code

;--------------------------------------------------------------------------
DllMain PROC hModule:HANDLE, dwReason:DWORD, lpReserved:DWORD
.IF dwReason == DLL_PROCESS_ATTACH
mov eax, hModule
mov g_hDllMain, eax
mov eax, TRUE
.ELSEIF
mov eax, FALSE
.ENDIF
ret
DllMain Endp

;--------------------------------------------------------------------------
DllCanUnloadNow PROC
.IF (MyCFObject.nRefCount == 0)
mov eax, TRUE
.ELSEIF
mov eax, FALSE
.ENDIF
ret
DllCanUnloadNow Endp

;--------------------------------------------------------------------------
DllGetClassObject PROC pCLSID:DWORD, pIID:DWORD, ppv:DWORD
LOCAL hr :DWORD
LOCAL pFactory :DWORD
; compare the clsid given us to the one we can build
; we can ONLY build a Class Factory object to build a IID_IMyCom object
; all others get rejected
invoke IsEqualGUID, pCLSID, addr CLSID_MyCom
.IF (eax == TRUE)
; get a pointer to our static object
; (static = no constructor or alloc methods needed)
; (member values are set at compile time)
mov eax, OFFSET MyCFObject
; use it to get the requested pointer
invoke QueryInterface_CF, eax, pIID, ppv
; use the QI hresult to determine the sucess of this procedure
mov hr, eax
; and release our helper pointer
invoke Release_CF, OFFSET MyCFObject
.ELSE
mov hr, CLASS_E_CLASSNOTAVAILABLE
.ENDIF
mov eax, hr ; pass back the hresult
ret
DllGetClassObject Endp

;--------------------------------------------------------------------------
DllRegisterServer PROC
LOCAL hKey :DWORD
LOCAL hKey2 :DWORD
LOCAL hKey3 :DWORD
LOCAL hKey4 :DWORD
LOCAL sBuf :BYTE
LOCAL wsBuf :WORD
LOCAL psBuf :DWORD
LOCAL pwsBuf :DWORD
LOCAL pti :DWORD

; Register Com Add-in
invoke RegCreateKey, HKEY_CURRENT_USER, ADDR szSampleProgID, ADDR hKey4
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke RegSetValueEx, hKey4, ADDR szCommandLineSafe, NULL, REG_DWORD, ADDR szSampleDesc1, 4
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke lstrlen, ADDR szSampleDesc2
invoke RegSetValueEx, hKey4, ADDR szDescription, NULL, REG_SZ, ADDR szSampleDesc2, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke lstrlen, ADDR szSampleDesc3
invoke RegSetValueEx, hKey4, ADDR szFriendlyName, NULL, REG_SZ, ADDR szSampleDesc3, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke RegSetValueEx, hKey4, ADDR szLoadBehaviour, NULL, REG_DWORD, ADDR szSampleDesc4, 4
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

; assign the pointers to the local text buffers
lea eax, sBuf
mov psBuf, eax
lea eax, wsBuf
mov pwsBuf, eax
; Create HKEY_CLASSES_ROOT\progid\CLSID
invoke RegCreateKey, HKEY_CLASSES_ROOT, ADDR szSampleProgID2, ADDR hKey
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
invoke lstrlen, ADDR szSampleDesc2
invoke RegSetValue,hKey, NULL, REG_SZ, ADDR szSampleDesc2, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; Create HKEY_CLASSES_ROOT\%szProgID%\CLSID
invoke RegCreateKey, hKey, ADDR szCLSID, ADDR hKey2
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; convert the CLSID_MyCom number to a string
invoke StringFromGUID2, ADDR CLSID_MyCom, pwsBuf, MAX_PATH
invoke WideCharToMultiByte, CP_ACP, 0, pwsBuf, -1,
psBuf, MAX_PATH, NULL, NULL
invoke lstrlen, psBuf
invoke RegSetValue, hKey2, NULL, REG_SZ, psBuf, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke RegCloseKey, hKey
invoke RegCloseKey, hKey2
; Create HKEY_CLASSES_ROOT\CLSID
invoke RegCreateKey, HKEY_CLASSES_ROOT, ADDR szCLSID, ADDR hKey
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; Create HKEY_CLASSES_ROOT\CLSID\%GUID%
invoke RegCreateKey, hKey, psBuf, ADDR hKey2
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; put in sample description value into CLSID\GUID key
invoke lstrlen, ADDR szSampleDesc2
invoke RegSetValue, hKey2, NULL, REG_SZ, ADDR szSampleDesc2, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; get our path ..
invoke GetModuleFileName,g_hDllMain, psBuf, MAX_PATH
.IF (eax == 0)
mov eax, 1
jmp return
.ENDIF
; Create HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32
invoke RegCreateKey, hKey2, ADDR szInprocServer32, ADDR hKey3
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; set key value to the path obtained above
invoke RegSetValue, hKey3, NULL, REG_SZ, psBuf, MAX_PATH
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
; set the threading type value
invoke lstrlen, ADDR szThreadType
invoke RegSetValueEx, hKey3, ADDR szThreadModel, 0, REG_SZ, ADDR szThreadType, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
invoke RegCloseKey, hKey3
; Create HKEY_CLASSES_ROOT\CLSID\%GUID%\%szProgID%
invoke RegCreateKey, hKey2, ADDR szProgID, ADDR hKey3
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
invoke lstrlen, ADDR szSampleProgID
invoke RegSetValue, hKey3, NULL, REG_SZ, ADDR szSampleProgID, eax
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
invoke RegCloseKey, hKey3
.IF (hKey != 0)
invoke RegCloseKey, hKey
.ENDIF
.IF (hKey2 != 0)
invoke RegCloseKey, hKey2
.ENDIF
.IF (hKey3 != 0)
invoke RegCloseKey, hKey3
.ENDIF
.IF (hKey4 != 0)
invoke RegCloseKey, hKey4
.ENDIF

; sBuf still has our dll pathname, lets use it to
; get the class type library
Invoke MultiByteToWideChar, CP_ACP, 0, psBuf, -1, pwsBuf, MAX_PATH
.IF !eax
mov eax, S_FALSE
jmp return
.ENDIF
lea eax, pti
Invoke LoadTypeLib, pwsBuf, eax
.IF (eax != ERROR_SUCCESS)
mov eax, S_FALSE
jmp return
.ENDIF
; and register the type lib
Invoke RegisterTypeLib, pti, pwsBuf, NULL
.IF (eax != ERROR_SUCCESS)
mov eax, S_FALSE
jmp return
.ELSE
; release the type info pointer we got
; NOTE: the 3 following lines could be substuited with
; the coinvoke macro like so:
; coinvoke pti, IUnknown, Release
mov eax, pti
mov eax,
invoke (IUnknown PTR ).IUnknown_Release, pti
.ENDIF
xor eax, eax ; mov eax, S_OK
return:
ret
DllRegisterServer ENDP

; -------------------------------------------------------------------------
DllUnregisterServer PROC
LOCAL hSubkey:DWORD
LOCAL hSubkey2:DWORD
LOCAL sBuf :BYTE
LOCAL psBuf :DWORD
LOCAL wsBuf :WORD
LOCAL pwsBuf :DWORD

lea eax, sBuf
mov psBuf, eax
lea eax, wsBuf
mov pwsBuf, eax
invoke StringFromGUID2, ADDR CLSID_MyCom, pwsBuf, MAX_PATH
invoke WideCharToMultiByte, CP_ACP, 0, pwsBuf, -1,
psBuf, MAX_PATH, NULL, NULL

invoke GuardedDeleteKey, HKEY_CURRENT_USER, ADDR szSampleProgID
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke GuardedDeleteKey, HKEY_CLASSES_ROOT, ADDR szSampleProgID2
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF

invoke RegOpenKey, HKEY_CLASSES_ROOT, ADDR szCLSID, ADDR hSubkey
invoke GuardedDeleteKey, hSubkey, psBuf
.IF (eax != ERROR_SUCCESS)
jmp return
.ENDIF
invoke RegCloseKey, hSubkey
xor eax, eax ; mov eax, S_OK
return:
ret
DllUnregisterServer ENDP

;--------------------------------------------------------------------------
GuardedDeleteKey PROC hKey:DWORD, lpszSubKey:DWORD
LOCAL szSubKeyName :TCHAR
LOCAL hSubkey :DWORD

; check to see if the key to be deleted still has subkey,
; if not delete it, otherwise delete the subkey
invoke RegOpenKey, hKey, lpszSubKey, ADDR hSubkey
.IF (eax != ERROR_SUCCESS)
mov eax, REGDB_E_INVALIDVALUE
jmp return
.ENDIF
KillNextSubkey:
; check for a subkey
invoke RegEnumKey, hSubkey, 0, ADDR szSubKeyName, MAX_PATH+1
.IF (eax != ERROR_NO_MORE_ITEMS) ; IF SubKeys exist...
; delete the subkey thru recusion
invoke GuardedDeleteKey, hSubkey, ADDR szSubKeyName
jmp KillNextSubkey
.ELSE
.ENDIF
; no more Subkeys, delete the specfied hey.
invoke RegCloseKey, hSubkey
invoke RegDeleteKey, hKey, lpszSubKey
.IF (eax == ERROR_SUCCESS)
xor eax, eax ; mov eax, S_OK
.ENDIF
return:
ret
GuardedDeleteKey ENDP

;--------------------------------------------------------------------------
QueryInterface_CF PROC this_:DWORD, pRIID:DWORD, ppv:DWORD
; QueryInterface for the ClassFactory Interface
LOCAL Match:DWORD
; check against the two interfaces we support
invoke IsEqualGUID, pRIID, addr IID_IUnknown
mov Match, eax
invoke IsEqualGUID, pRIID, addr IID_IClassFactory
or eax, Match
.IF (eax == TRUE) ; then asking for either IUnknown or IClassFactory
; both of which we are & support
mov eax, this_
mov edx, ppv
mov , eax ; so we point to ourselves
invoke AddRef_CF, eax ; inc the ref count for the new pointer *we* created
xor eax, eax ; and signal all is well (same as 'ret S_OK')
jmp return
.ENDIF
NoInterface:
mov , NULL ; good practice, always NULL bad pointers
; in case client doesn't check the SCODE in hResult
mov eax, E_NOINTERFACE ; and signal interface is not supported here
return:
ret
QueryInterface_CF endp

;-----------------------------------------------------------------------------
AddRef_CF proc this_:DWORD
inc MyCFObject.nRefCount
mov eax, MyCFObject.nRefCount
ret ; note we return the object count
AddRef_CF endp

;-----------------------------------------------------------------------------
Release_CF proc this_:DWORD
; Remember, we don't have a 'real' constuctor for this
; object. Neither do we have a real destructor.
dec MyCFObject.nRefCount
mov eax, MyCFObject.nRefCount
ret ; note we return the object count
Release_CF endp

;-----------------------------------------------------------------------------
CreateInstance PROC this_:DWORD, pUnknownOuter:DWORD, iid:DWORD, ppv:DWORD
LOCAL pMyObject:DWORD
LOCAL hr:DWORD
.IF pUnknownOuter != NULL
; we don't support aggregation (it's too aggravating)
mov eax, CLASS_E_NOAGGREGATION
.ELSE
invoke CreateMyComObject
mov pMyObject, eax
.IF eax == NULL
; Create failed
mov eax, E_OUTOFMEMORY
.ELSE
; we have a valid pointer & new object
; record we sucessfully made a new object
inc MyCFObject.nRefCount
; see if we can get the requested interface
invoke QueryInterface_MC, pMyObject, iid, ppv
mov hr, eax
invoke Release_MC, pMyObject
.ENDIF
.ENDIF
mov eax, hr
ret
CreateInstance ENDP

;-----------------------------------------------------------------------------
LockServer PROC pif:DWORD, bLockServer:DWORD
; minor cheat here, we reuse the object count to also
; count locks, because BOTH must be zero to unload the dll
; (the only way to delete our static ClassFactory object)
.IF bLockServer == TRUE
inc MyCFObject.nRefCount
.ELSE
dec MyCFObject.nRefCount
.ENDIF
mov eax, S_OK
ret
LockServer ENDP

;-----------------------------------------------------------------------------
CreateMyComObject PROC
LOCAL pNewObject:DWORD
; make a new MyCom object
invoke CoTaskMemAlloc, sizeof MyComObject
mov pNewObject, eax
.IF (eax != NULL)
; we got our memory
; initialize the new object
mov edx, pNewObject
mov (MyComObject PTR ).lpVtbl, OFFSET vtIMyCom
mov (MyComObject PTR ).nRefCount, 1
mov (MyComObject PTR ).nValue, 0
.ENDIF
mov eax, pNewObject ; restore our memory pointer
ret
CreateMyComObject endp

;-----------------------------------------------------------------------------
QueryInterface_MC PROC this_:DWORD, pRIID:DWORD, ppv:DWORD
; QueryInterface for the IMyCom Interface
LOCAL Match :DWORD
LOCAL ppvt :DWORD
; check against the two interfaces we support
invoke IsEqualGUID, pRIID, addr IID_IUnknown
mov Match, eax
invoke IsEqualGUID, pRIID, addr IID_IMyCom
or eax, Match
.IF (eax == TRUE) ; then asking for either IUnknown or IMyCom
; both of which we are & support
mov eax, this_
mov edx, ppv
mov , eax ; so we point to ourselves
mov ppvt, eax
invoke AddRef_MC, ppvt ; inc the ref count for the new pointer *we* created
mov eax, S_OK ; and signal all is well
jmp return
.ENDIF
; no other interfaces supported
NoInterface:
xor eax, eax ; eax = NULL
mov edx, ppv ; good practice, always NULL bad pointers
mov , eax ; in case client doesn't check the SCODE in hResult
mov eax, E_NOINTERFACE ; and signal interface is not supported here
return:
ret
QueryInterface_MC endp
;-----------------------------------------------------------------------------
AddRef_MC proc this_:DWORD
push ebx
push esi
push edi
mov eax, this_
inc (MyComObject ptr ).nRefCount
mov eax, (MyComObject ptr ).nRefCount
pop edi
pop esi
pop ebx
ret ; note we return the object count
AddRef_MC endp
;-----------------------------------------------------------------------------
Release_MC proc this_:DWORD
push ebx
push esi
push edi
mov eax, this_
dec (MyComObject ptr ).nRefCount
mov eax, (MyComObject ptr ).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
pop edi
pop esi
pop ebx
ret ; note we return the object count
Release_MC endp
;-----------------------------------------------------------------------------
OnConnection proc this_:DWORD, application:DWORD, connectmode:DWORD, addininst:DWORD, custom:DWORD
push ebx
push esi
push edi
mov eax, this_
invoke MessageBox, 0, ADDR szSampleDesc5, 0, MB_OK or MB_ICONWARNING
mov eax, application
mov appinf, eax
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
OnConnection endp
;-----------------------------------------------------------------------------
OnDisconnection proc this_:DWORD, removemode:DWORD, custom:DWORD
push ebx
push esi
push edi
mov eax, this_
invoke MessageBox, 0, ADDR szSampleDesc6, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
OnDisconnection endp
;-----------------------------------------------------------------------------
OnAddInsUpdate proc this_:DWORD, custom:DWORD
push ebx
push esi
push edi
mov eax, this_
invoke MessageBox, 0, ADDR szSampleDesc7, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
OnAddInsUpdate endp
;-----------------------------------------------------------------------------
OnStartupComplete proc this_:DWORD,custom:DWORD
push ebx
push esi
push edi
mov eax, this_
invoke MessageBox, 0, ADDR szSampleDesc8, 0, MB_OK or MB_ICONWARNING

invoke GetProperty, this_, appinf, addr toolbars, addr vtCmdBars

mov vtParam2.vt, VT_BSTR
invoke SysAllocString, addr mybarr
mov vtParam2.bstrVal, eax

invoke CallMethod, this_, vtCmdBars.pdispVal, ADDR adds, ADDR vtCmdBar, 1, ADDR vtParam2

mov eax, S_OK
pop edi
pop esi
pop ebx
ret
OnStartupComplete endp
;-----------------------------------------------------------------------------
OnBeginShutdown proc this_:DWORD, custom:DWORD
push ebx
push esi
push edi
mov eax, this_
invoke MessageBox, 0, ADDR szSampleDesc9, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
OnBeginShutdown endp
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
GetTypeInfoCount proc this_:DWORD, removemode:DWORD, custom:DWORD
push ebx
push esi
push edi
mov eax, this_
;invoke MessageBox, 0, ADDR szSampleDesc2, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
GetTypeInfoCount endp
;-----------------------------------------------------------------------------
GetTypeInfo proc this_:DWORD
push ebx
push esi
push edi
mov eax, this_

mov eax, S_OK
pop edi
pop esi
pop ebx
ret
GetTypeInfo endp
;-----------------------------------------------------------------------------
GetIDsOfNames proc this_:DWORD
push ebx
push esi
push edi
mov eax, this_
;invoke MessageBox, 0, ADDR szSampleDesc2, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
GetIDsOfNames endp
;-----------------------------------------------------------------------------
Invoker proc this_:DWORD
push ebx
push esi
push edi
mov eax, this_
;invoke MessageBox, 0, ADDR szSampleDesc2, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
Invoker endp
;-----------------------------------------------------------------------------
GetProperty proc this_:DWORD, pDisp:DWORD, pszName:DWORD, pvResult:DWORD
push ebx
push esi
push edi
mov eax, this_

coinvoke pDisp, IUnknown, QueryInterface, addr IID_IDispatch, addr hIDispatch
test eax, 80000000h
jz noerror3
invoke MessageBox, 0, addr error3, 0, MB_OK or MB_ICONWARNING
noerror3:

coinvoke hIDispatch, IDispatch, GetIDsOfNames, addr IID_NULL, addr pszName, 1, LOCALE_USER_DEFAULT, ADDR dwDispID
test eax, 80000000h
jz noerror
invoke MessageBox, 0, addr error1, 0, MB_OK or MB_ICONWARNING
noerror:

coinvoke hIDispatch, IDispatch, Invoker, dwDispID, addr IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD or DISPATCH_PROPERTYGET, addr dispparams, pvResult, NULL, NULL
test eax, 80000000h
jz noerror2
invoke MessageBox, 0, addr error2, 0, MB_OK or MB_ICONWARNING
noerror2:

mov eax, S_OK
pop edi
pop esi
pop ebx
ret
GetProperty endp
;-----------------------------------------------------------------------------
PutProperty proc this_:DWORD, pDisp:DWORD, pszName:DWORD, pvValue:DWORD
push ebx
push esi
push edi
mov eax, this_
;invoke MessageBox, 0, ADDR szSampleDesc2, 0, MB_OK or MB_ICONWARNING
mov eax, S_OK
pop edi
pop esi
pop ebx
ret
PutProperty endp
;-----------------------------------------------------------------------------
CallMethod proc this_:DWORD, pDisp:DWORD, pszName:DWORD, pvResult:DWORD, cArgs:DWORD, rgVarParams:DWORD
push ebx
push esi
push edi
mov eax, this_


mov dispparamsb.rgdispidNamedArgs, 0
mov dispparamsb.cNamedArgs, 0

mov eax, rgVarParams
mov dispparamsb.rgvarg, eax
mov eax, cArgs
mov dispparamsb.cArgs, ax

coinvoke pDisp, IDispatch, GetIDsOfNames, addr IID_NULL, addr pszName, 1, LOCALE_USER_DEFAULT, ADDR dwDispID
test eax, 80000000h
jz noerror4
invoke MessageBox, 0, addr error1, 0, MB_OK or MB_ICONWARNING
noerror4:

coinvoke pDisp, IDispatch, Invoker, dwDispID, addr IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD or DISPATCH_PROPERTYGET, addr dispparamsb, pvResult, NULL, NULL
test eax, 80000000h
jz noerror5
invoke MessageBox, 0, addr error2, 0, MB_OK or MB_ICONWARNING
noerror5:

mov eax, S_OK
pop edi
pop esi
pop ebx
ret
CallMethod endp
;-----------------------------------------------------------------------------

End DllMain
Posted on 2001-11-22 23:29:25 by Spider
Giving your code the 30 sec review, it looks OK so far, So...

You shouldn't have to do anything with the params TO the invoke here, as you're clearly not using any.

How are you calling this code? Are you trying to go right from Word? I suggest something simpler, like C or asm to debug it. You never know just how word is going about it.

Other things that can cause trouble is the typelib either not compiling or not linking into the resources correctly. But it seems you're not up to that yet, just trying to get the code block to execute.

I'll be back around Sat, lemmy know how it's going.
Posted on 2001-11-23 00:17:39 by Ernie
Spider,

check your DISPPARAMS structure definition. It should look like:



DISPPARAMS STRUCT DWORD
rgvarg LPVARIANT ?
rgdispidNamedArgs LPDISPID ?
cArgs DWORD ?
cNamedArgs DWORD ?
DISPPARAMS ENDS


Ernies CoLib had a bug here.

japheth
Posted on 2001-11-23 05:01:15 by japheth