Ok.. In short, does anyone have any experience in working with PASCAL calling convenetions with COM interfaces?

I've been having bitter luck with M$ Excel's automation interfaces. I have sample code, i have the typelib converted (thanx to Japheth), I have the IDL.. it should be a walk in the coding-park. But its not.

I managed to get the Excell server running, and added a new workbook. But that is it. Trying to 'GET' any cell data is not working at all, and i have no clue what the M$ HLL Apps do behinde the sceens (will explain in a bit).
void CExcelCtrlDlg::OnButton1() 

{
int i,j;
CString ssbuf;
COleVariant x;
COleVariant y;
COleVariant data;
COleVariant ItemId;
_Application pApp;
_Application pXlsApp;
Workbooks pXlsBooks;
Range pXlsRange;

pXlsApp.CreateDispatch("Excel.Application");
pXlsApp.SetVisible(TRUE);

LPDISPATCH pWkBooks = pXlsApp.GetWorkbooks();
pXlsBooks.Add();

LPDISPATCH pCells = pXlsApp.GetCells();
pXlsRange.AttachDispatch(pCells,TRUE);

[b] y = (short)1;
x = (short)1;
ItemId = pXlsRange.GetItem(y,x);
pApp.AttachDispatch(ItemId.pdispVal,TRUE);
ssbuf = pApp.GetValue();p[/b]
...
}


Here is my ASM version. Note that _Application, Workbooks, and Range above are wrapper classes to the interfaces (of the same name) im accessing direclty below:
       mov vb, TRUE       

;HRESULT Visible([in, lcid] long lcid, [in] VARIANT_BOOL RHS);
invoke vf(pIExcelApp, _Application ,put_Visible), NULL, vb

;HRESULT Workbooks([out, retval] Workbooks** RHS);
invoke vf(pIExcelApp, _Application ,get_Workbooks), addr WBKS

;HRESULT Add( [in, optional] VARIANT Template, [in, lcid] long lcid, [out, retval] Workbook** RHS);
mov VTEMP, $NEW( VRNT )
invoke vf(WBKS, Workbooks ,Add_), VARIANT PTR [eax].VRNT.Var, NULL, addr WBK
DESTROY VTEMP

;LPDISPATCH pCells = pXlsApp.GetCells();
invoke vf(pIExcelApp, _Application ,get_Cells), addr RNG

This is what DOES work, beyond it fails:
       ;ItemId = pXlsRange.GetItem((short) y,(short) x);	

.data
VVA VARIANT <2,0,0,0,<2>>
VVB VARIANT <2,0,0,0,<4>>
VVC VARIANT <0,0,0,0,<0>>
ddata db 0ch,20h,0ch,20h,0
.code

mov ebx, esp
PrintHex esp
[b] invoke vf(RNG, IRange, get_Item), addr lpVar, VVA, VVB, addr VVC[/b] ;; PROBLEM HERE
PrintHex eax
PrintHex esp
mov esp, ebx


There problem, as im tracking it, is that there is 'optional' VARIANT arguments, thus the STDCALL doesnt work if you dont provide all arguments, so im assuming its taking on a PASCAL calling convention. Here is my reasons:
1) The C++ Wrapper interface defines such:
VARIANT Range::GetItem(const VARIANT& RowIndex, const VARIANT& ColumnIndex)

{
VARIANT result;
static BYTE parms[] =
VTS_VARIANT VTS_VARIANT;
InvokeHelper(0xaa, DISPATCH_PROPERTYGET, VT_VARIANT, (void*)&result, parms,
&RowIndex, &ColumnIndex);
return result;
}
Where InvokeHelper is in the CoreSDK as:
	void AFX_CDECL InvokeHelper(DISPID dwDispID, WORD wFlags,

VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ...);
2) The stack is NOT being cleane up even after a suscessful call (no crashes, hresult == 0). This is why im looking at the ESP and manually fixing it.

The other problem is this Byte Array busines "params", which like wsprintf, indicates how many params are to follow. The reason its a problem is the transcribed typelib has no such indications of any of this:
;HRESULT _stdcall Item( [in] VARIANT RowIndex, [in, optional] VARIANT ColumnIndex, 

; [in, lcid] long lcid, [out, retval] VARIANT* RHS);
STDMETHOD get_Item , :VARIANT, :VARIANT, :SDWORD, :ptr VARIANT

I know its a problem, cause follwing the transcribed type lib to It crashes internally, cause the first Variant (RowIndex) pushes its variant type first (2), followed by 12 other bytes for it. OllyDbg'n this i traced it thru and found out its trying to used this first push as a pInterface and when accessing address '00000002' its crashes. So the typelib it wrong (not your fault Japheth!). I checked VC++'s typelib tool and it gave the EXACTLY SAME MISLEADING INFORMATION!. So i manually buggered around with the params and tryied to follow (as best as i can) to the wrapper definition and discovered that its more like:
STDMETHOD	get_Item	, :ptr ReturnVar, lpByteArgList:DWORD, :VARIANT , :VARIANT 

When i try this, it doesnt crash, and returns a VARIANT NULL by the ReturnVal pointer param!!!
HRESULT is = 00000000h! So things are better, but not great cause im still getting a null back! I should be getting a Variant=Dispatch with a pInterface in its argument!

So im stumped at this point. Im trying to 'find the caramel secret' that MS had done.

It makes sence to me that the parm ordering for PASCAL type com calls would be:
0) lpTHIS intefvace << Always true
1) Return Value <<< This is confirmed (outputs Variant NULL)
2) AgrumentByteList
3) Optional Arguments

Please Please Please Please Please Give any advice you can. This is driving me nuts, as im sure you for trying to read all this and take it in. I would be glad to clarify anything that is not clear to you above.
In the mean time, I dont expect many to respond, so im going to consult my texts on COM, but i doubt they will discuss the details im looking for (since they are all geared to MS VC++ and alike :rolleyes: )

THanx for reading and putting up with me,
:alright:
NaN
Posted on 2002-12-29 11:50:12 by NaN
NaN,

Im not so good at english so it may cost me a week or so to translate your novel. But 2 things I have understood so far:

- be careful if an argument is of VARIANT type. If it is a true VARIANT (not a "ptr VARIANT"), you may need to push 4 DWORD on the stack, the arguments size is 16 bytes!

- optional parameters are valid for IDispatch::Invoke only. If you call the function directly throu vtable, you must supply all parameters. The optional ones, it they are VARIANTS, should have a special type/content if they arent supplied (think its VT_EMPTY or VT_ERROR, cant remember exacly).
Posted on 2002-12-29 12:41:40 by japheth
How many "optional" arguments? Can you pass a NULL for each optional arg you don't want to use? The callee should check the optional arg list to see which ones are used, no? So, if you want to pass arg 2 out of three, shouldn't you pass NULL, Something, NULL and the callee will sort that out?

Me thinks NaN is going to pull the rest of the hair outta the head tring to work with this. :grin:
Posted on 2002-12-29 12:45:51 by Gunner

- optional parameters are valid for IDispatch::Invoke only. If you call the function directly throu vtable, you must supply all parameters. The optional ones, it they are VARIANTS, should have a special type/content if they arent supplied (think its VT_EMPTY or VT_ERROR, cant remember exacly).


Ok.. this fits with what I've learnt (and expected, up to now). So there is two questions then. Does anyone know what goes on inside the InvokeHelper API?? Is there a Link between this mysterious LCID:SDWORD param and the bytestring 'PARAMS' used to indicate the # of arguments used.

Secondly, why is the vtable interface then not cleaning up the stack? And why is it writing over the first (RolIndex = VT_I2) to the first VARAINT instead of reading it.

If you like, i can zip up my information, and sample code.
Thanks for your initial thoughts... i look forward to when you 'transcribe my novel ;) ', and have more to say...
:alright:
NaN
Posted on 2002-12-29 12:52:06 by NaN

How many "optional" arguments? Can you pass a NULL for each optional arg you don't want to use? The callee should check the optional arg list to see which ones are used, no? So, if you want to pass arg 2 out of three, shouldn't you pass NULL, Something, NULL and the callee will sort that out?

Me thinks NaN is going to pull the rest of the hair outta the head tring to work with this. :grin:


I have.. this has been the most challenging project i've even worked on. Dam M$ and their screwball ways ;)

The Autocad interfaces were a walk in the park compared to this. It just doens make sence, and nothing is painting the complete 'correct' picture. Im getting the feeling to use excel interfaces you have to use the DISPACTH Inovke methods explicitly as the above wrapper class method does.

:nan:
Posted on 2002-12-29 12:55:53 by NaN
Here is the example C++ im working with. Its commented in Russian, but C++ language speaks for itself anyways... You can see the wrapper classses and how its being used
Posted on 2002-12-29 12:58:04 by NaN
Originally posted by NaN
[...]
Where InvokeHelper is in the CoreSDK as:
	void AFX_CDECL InvokeHelper(DISPID dwDispID, WORD wFlags,

VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ...);


Are you sure it's in the coreSDK? I couldn't find it and when I traced the code you were talking about in the C++ example it traced through COleDispatchDriver::InvokeHelper in the MFC source code. So I don't think it's an API, just an MFC helper method. Also, AFX_CDECL gives the suspicion that the function is just a normal C function (using C calling convention), with ... to indicate a variable number of parameters. The usage of va_list/va_start/va_end inside that function confirms this.

Hope this helps & good luck ;)

Thomas
Posted on 2002-12-29 13:25:38 by Thomas
You are correct.. I did a 'FindFile' search in the CoreSDK directory... but i didnt pay attention to which dir it pulled it out of.. i simply opened it in UltraEdit and Searched for the 'InvokeHelper' in the file. This leads directly to what your mentioned. (my bad).

Ok. So its MFC that this example is using. So i should ignore it and try and find a pure C++ example then? With out MFC, to see how to properly use these interfaces? Cause im still puzzled why the interface writes 'out' to an 'in' param (RowIndex:VARIANT).

Thank you for correcting me here.. im going to abandon all trust in this source..
:alright:
NaN
Posted on 2002-12-29 13:53:29 by NaN
Here is some knowledge base infor i just dug up..
How do I pass optional parameters?

Some methods have "optional" parameters. In Visual Basic, you can casually omit them when calling the method. However, when calling with Visual C++ you have to pass a special VARIANT whose .vt field is VT_ERROR, and .scode field is DISP_E_PARAMNOTFOUND. That is:
// VARIANT used in place of optional-parameters.
VARIANT varOpt;
varOpt.vt = VT_ERROR;
varOpt.scode = DISP_E_PARAMNOTFOUND;

This is really what Visual Basic is doing behind-the-scenes.


And...
      LRESULT CClientDlg::OnOK(WORD wNotifyCode, WORD wID,

HWND hWndCtl, BOOL& bHandled)
{
IDispatch* pDisp; // Main IDispatch pointer.
CLSID clsid; // Holds CLSID of server
DISPID dispID; // Temporary variable to hold DISPIDs.
HRESULT hr; // General error/result holder.

hr = CLSIDFromProgID(L"Excel.Application", &clsid);
if(FAILED(hr)){
MessageBox("Excel is not installed.");
return 0;}

hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pDisp);
if(FAILED(hr)){

MessageBox("Couldn't start Excel.");
return 0;}

OLECHAR *szVisible = L"Visible";
DISPPARAMS dispParams = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
CComVariant vrTrue = VARIANT_TRUE;

pDisp->GetIDsOfNames(IID_NULL, &szVisible, 1,
LOCALE_USER_DEFAULT, &dispID);

dispParams.cArgs = 1;
dispParams.rgvarg = &vrTrue;
dispParams.cNamedArgs = 1;
dispParams.rgdispidNamedArgs = &dispidNamed;

// Set 'Visible' property to true.
pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT | DISPATCH_METHOD, &dispParams,
NULL, NULL, NULL);

MessageBox("We're done.", "Finish", MB_SETFOREGROUND);

// Release the IDispatch pointer before exit
pDisp->Release();
return 0;


Im getting the feeling that the only way around this mess is using the pDisp->Invoke method :rolleyes: . It will be a lot of extra work, but i guess if i wrap it up in a MASM oop class for reuse it will pay off...

Feeling defeated..
:nAn:
Posted on 2002-12-29 14:52:04 by NaN
I think the problem is interface "Range" or "IRange", which you are using. Both are marked as "dispatchable", but not as "dual". So you cant be sure that there is a valid vtable at all. And if not, you have to call it throu IDispatch:Invoke (which I suggest is exactlly what Invokehelper does). To ensure that I would stop just before the call and look at the vtable of IRange. The first 7 entries are the IDispatch interface, no doubt. But what does follow?

So you will have to code an Invokehelper vor IRange as well. Heres an example (1 parameter only):



local dispparams:DISPPARAMS
local rgvarg[1]:VARIANT
local varResult:VARIANT
;--------------------------------- set method/property ID (here "Visible")
mov dwIDs, 22Eh
;--------------------------------- init result variant
invoke VariantInit, addr varResult
;--------------------------------- set dispparams

lea eax, rgvarg
mov dispparams.rgvarg, eax
mov dispparams.cArgs, 1

mov dwPropPutId, DISPID_PROPERTYPUT ;alway needed for put property
lea eax, dwPropPutId
mov dispparams.rgdispidNamedArgs, eax
mov dispparams.cNamedArgs, 1

;--------------------------------- set property via invoke

invoke vf(pDispatch, IDispatch, Invoke_), dwIDs, addr IID_NULL, LOCALE_SYSTEM_DEFAULT,\
DISPATCH_PROPERTYPUT, addr dispparams, addr varResult, NULL, NULL
.if (eax != S_OK)
Posted on 2002-12-29 14:53:56 by japheth
I tried using IDispatch::Invoke and it worked.
Putting number 555 in first cell of a new worksheet.
Quite sure now that vtable calls doesnt work for interface Range.



invoke vf(pApp, _Application, get_Workbooks), addr pWorkbooks
.if (eax == S_OK)
mov var1.vt, VT_ERROR
mov var1.scode, DISP_E_PARAMNOTFOUND
invoke vf(pWorkbooks, Workbooks, Add_), var1, 0, addr pWorkbook
.if (eax == S_OK)
invoke vf(pApp, _Application, get_Cells), addr pCells
.if (eax == S_OK)
mov var1.vt, VT_I4
mov var1.lVal, 555
invoke put_Item@Range, pCells, 1, 1, var1
invoke vf(pCells, IUnknown, Release)
.endif
invoke vf(pWorkbook, IUnknown, Release)
.endif
invoke vf(pWorkbooks, IUnknown, Release)
.endif


and thats the "invoke wrapper":


put_Item@Range proc pDispatch: LPDISPATCH, dwRow:DWORD, dwCol:DWORD, varProp:VARIANT

local dwPropPutId:DWORD
local dispparams:DISPPARAMS
local rgvarg[3]:VARIANT

lea eax, rgvarg
mov dispparams.rgvarg, eax
mov dispparams.cArgs, 3
mov dispparams.cNamedArgs, 1

mov dwPropPutId, DISPID_PROPERTYPUT
lea eax, dwPropPutId
mov dispparams.rgdispidNamedArgs, eax

mov ax, varProp.vt
mov rgvarg[0*sizeof VARIANT].vt, ax
mov eax, varProp.lVal
mov rgvarg[0*sizeof VARIANT].lVal, eax

mov rgvarg[1*sizeof VARIANT].vt, VT_I4
mov eax, dwCol
mov rgvarg[1*sizeof VARIANT].lVal, eax

mov rgvarg[2*sizeof VARIANT].vt, VT_I4
mov eax, dwRow
mov rgvarg[2*sizeof VARIANT].lVal, eax
invoke vf(pDispatch, IDispatch, Invoke_), DISPID_ITEM, addr IID_NULL, LOCALE_SYSTEM_DEFAULT,\
DISPATCH_PROPERTYPUT, addr dispparams, NULL, NULL, NULL
ret
put_Item@Range endp


the parameters for invoke must be in REVERSE order!!!
And PROPERTYPUT needs one named argument!!!

generating such wrapper code for dispatchonly interfaces would be a nice feature for comview.
But Im afraid then COM would be too easy, just for lamers :)

Japheth.
Posted on 2002-12-29 16:17:57 by japheth
Well i feel like a lamer ;)

Thanks (once again) for your help..

At least i know *someone* got it to work.. so it must be possible.
Funny you spoke of the addon for COMView, cause i was thinking of writing a class with the OOP to do all this, much like the MFC class generated by its wizard.

Anywho, thanx again...
:alright:
NaN
Posted on 2002-12-29 17:36:05 by NaN
Thanx again! Im back on the ball with this Invoke_ business. I managed to do some basic stuff now!

So now to make my Excel wrapper class to make the automation simpler...

If you are interested in this when im done, you are welcome to it for you help!

:alright:
NaN
Posted on 2002-12-29 21:17:47 by NaN
its quite funny: looking for IRange in generated excel.inc doesnt find any reference as "retval" parameter. Im quite sure that there exists a way of avoiding this Invoke_ stuff by somehow transforming a dispatchonly "Range" object to an vtable "IRange" object, since if not these "Ixxxx" interfaces would make no sense.
Posted on 2002-12-29 23:14:28 by japheth
I dont mind the extra work, if it means it *will work* ;)

And to that end here is some code, hopefully as repayment for you help.

I made my own "InvokeHelper".

MakeInvokeString PROTO C cNum:DWORD, args:VARARG

;--------------------------------------------------------------+
; Note: VT_BSTR_BLOB are not supported types!!
; VT_VECTOR
; VT_ARRAY
; VT_BYREF
; VT_RESERVED
; VT_ILLEGAL
; VT_ILLEGALMASKED
; VT_TYPEMASK
;--------------------------------------------------------------+
MakeInvokeString PROC C USES esi edi ebx cNum:DWORD, args:VARARG

invoke GetProcessHeap ;; Get the process heap pointer in EAX
mov edx, cNum ;; Get EDX = number of byte params
add edx, 9 ;; Align to next power of 4, with 4 byte header
and edx, 0FFFFFFFCh ;; Trim off any exess
invoke HeapAlloc, eax, NULL, edx ;; Use it to allocate
mov edx, cNum ;; Get the total count
mov edi, eax ;; Save hMem in edi
mov ebx, edi ;; Save hMem in ebx as well
mov [edi], edx ;; Store the count in the header
add edi, 4 ;; Set edi to start of string of bytes
lea esi, args ;; Get the arguments pointer
xor ecx, ecx ;; Set counter to zero
@@:
cmp ecx, edx ;; Is ecx == Count
je @F ;; Yes, end and tag on null
mov eax, [esi] ;; EAX == VTS type
mov [edi], al ;; copy byte into string
inc edi ;; EDI points to next byte location
add esi, 4 ;; ESI points to next argument
inc ecx ;; Counter is increased
jmp @B ;;
@@:
xor eax, eax ;; eax = 0
mov [edi], al ;; tack on null at the end
mov eax, ebx ;; Return the starting pointer to the memory
ret
MakeInvokeString ENDP
InvokeHelp PROTO C pDispatch: LPDISPATCH, w_Did:DWORD, dw_dFlag:DWORD, w_oVT_TYPE:WORD, lpResult:DWORD, lpParam:DWORD, :VARARG


; All Qword types must be passed as a pointer
InvokeHelp PROC C uses edi esi ebx pDispatch: LPDISPATCH, w_Did:DWORD, dw_dFlag:DWORD, w_oVT_TYPE:WORD, lpResult:DWORD, lpParam:DWORD, args:VARARG
; InvokeHelper(0xc5, DISPATCH_PROPERTYGET, VT_DISPATCH, (void*)&result, parms,

local dwPropPutId :DWORD
local dispparams :DISPPARAMS
local paramcount :DWORD
local lpVars :DWORD
local varReturn :VARIANT


; Get the length of the argument param list
;------------------------------------------+
mov esi, lpParam ;; Get the byte string address
mov ecx, [esi] ;; Get the number of arguments in ECX
mov paramcount, ecx ;; Save it for later
add esi, 4 ;; Move to the argument byte string <<<<<<<<<<<<[esi]

; Allocate Heap Memory for an array of Variants
;----------------------------------------------+
xor eax, eax ;; EAX = 0
mov lpVars, eax ;; Ensure lpVars == Null for later
mov dispparams.cArgs, eax ;; Indicate 0 arguments (for now)
invoke GetProcessHeap ;; Get the process heap pointer in EAX
mov edx, paramcount ;; EDX == Param count
.if(edx != NULL) ;; if EDX != NULL
mov dispparams.cArgs, edx ;; Indicate 'n' arguments
shl edx, 4 ;; ECX = ECX * 16 (Size of Variant)
invoke HeapAlloc, eax, NULL, edx ;; Use it to allocate Variant memory
mov edi, eax ;; EDI == Variants on the heap <<<<<<<<<<<<<<<<<[edi]
mov lpVars, edi ;; Save it for l8r (active value)


; Populate the Variants with the required data
;---------------------------------------------+
@@:
invoke VariantInit, edi ;; Initialize the Variant at EDI
mov ebx, paramcount ;; EBX = paramsleft
xor eax, eax ;; EAX = 0
cmp ebx, eax ;; if EBX = 0
je @F ;; Then done, jump out
dec ebx ;; EBX--
mov paramcount, ebx ;; Paramsleft--
mov edx, ebx ;; EDX = EBX
shl ebx, 2 ;; EBX = Count * sizeof DWORD
lea ecx, args ;; ECX = args address
add ebx, ecx ;; EBX == param offset for Variant data <<<<<<<[ebx]

add edx, esi ;; EDX == lpByteString + ByteCountOffset <<<<<<[edx]
mov al, [edx] ;; AL == corresponding VT_ TYPE byte

mov [edi].VARIANT.vt, ax ;; Set the Variant Type
.if ( eax == VT_NULL)
nop
.elseif( eax == VT_BOOL)
mov eax, [ebx] ;; Get Param Data
and eax, 1 ;; Trim out any fat
mov [edi].VARIANT.boolVal, ax ;; Set Bool
.elseif(( eax == VT_I1 ) || ( eax == VT_UI1))
mov eax, [ebx] ;; Get the param data
and eax, 0FFh ;; Trim out any fat
mov [edi].VARIANT.bVal, al ;; Set Byte
.elseif(( eax == VT_I2 ) || ( eax == VT_UI2))
mov eax, [ebx] ;; Get the param data
and eax, 0FFFFh ;; Trim out any fat
mov [edi].VARIANT.uiVal, ax ;; Set the word
.elseif(( eax == VT_R8 ) || ( eax == VT_CY ) || (eax == VT_DATE))
mov eax, [ebx] ;; the pointer
mov ebx, eax ;; Reset ebx to pointer pointed to
mov eax, [ebx] ;; Get the low 4
mov [edi].VARIANT.ulVal, eax ;; Set the low 4
add ebx, 4
add edi, 4
mov eax, [ebx] ;; Get the high 4
mov [edi].VARIANT.ulVal, eax ;; Set the high 4
sub ebx, 4
sub edi, 4
.else
mov eax, [ebx] ;; Get data32 in General
mov [edi].VARIANT.ulVal, eax ;; Set data32 in General
.endif
add edi, 16 ;; EDI = EDI + Sizeof Variant
jmp @B ;; Next Param to process
@@:

.endif ;; End of IF Param COunt

; Make the disparams header
;--------------------------+
mov eax, lpVars ;; Get Variant List addres;
mov dispparams.rgvarg, eax ;; set dispatch params pointer;
mov eax, dw_dFlag
.if(( eax == DISPATCH_PROPERTYPUT ) || ( eax == DISPATCH_PROPERTYPUTREF) )
mov dispparams.cNamedArgs, 1 ;; Indicate 1 named argument
mov dwPropPutId, DISPID_PROPERTYPUT ;; Indicate 'get' property call
lea eax, dwPropPutId ;; get the address to this indication
mov dispparams.rgdispidNamedArgs, eax ;; Set the named agrument pointer
.else
mov dispparams.cNamedArgs, 0 ;; Indicate 0 named argument
mov dispparams.rgdispidNamedArgs, NULL ;; Set the named agrument pointer
.endif

; Make the Invoke Call
;---------------------+
.data
xlsIID_NULL sIID_NULL
.code

.if !( lpResult )
invoke vf(pDispatch, IDispatch, Invoke_), w_Did, addr xlsIID_NULL, LOCALE_SYSTEM_DEFAULT,\
dw_dFlag, addr dispparams, NULL, NULL, NULL
.else
invoke VariantInit, addr varReturn
xor eax, eax
mov ax, w_oVT_TYPE
mov varReturn.vt, ax
invoke vf(pDispatch, IDispatch, Invoke_), w_Did, addr xlsIID_NULL, LOCALE_SYSTEM_DEFAULT,\
dw_dFlag, addr dispparams, addr varReturn, NULL, NULL

mov edx, lpResult
lea ecx, varReturn
add ecx, 8
mov eax, [ecx]
mov [edx],eax

.if (( w_oVT_TYPE == VT_R8 ) || (w_oVT_TYPE == VT_CY ) || (w_oVT_TYPE == VT_DATE))
mov eax, [ecx+4]
mov [edx+4], eax
.endif

.endif

; Clean up the Heaps
;-------------------+

invoke GetProcessHeap
invoke HeapFree, eax, NULL, lpVars
invoke GetProcessHeap
invoke HeapFree, eax, NULL, lpParam

ret
InvokeHelp ENDP


It has not been fully tested yet (since im burnt and going to bed). However, It does work with the following code example (places an integer 202 at (1,4) ):


invoke MakeInvokeString, 3, VT_I4, VT_I4, VT_I4
invoke InvokeHelp, RNG, 0AAh, DISPATCH_PROPERTYPUT, NULL, NULL, eax, 1, 4, 202


It should be robust enough. It has limitations tho (ie, no array types), but can be fixed if i make it the 'MakeInvokeString' produce words. As well, im not sure if the proper way to handle 'property get' methods is by setting the NamedArgs to Zero (likewize the DISP_METHOD type).

The Function "MakeInvokeString" returns a heap pointer with the byte array in it. The "InvokeHelp" will use this to build Variants, set them and call the invoke. When done it will destroy it automatically.

Both are 'C' type functions (what i was earlier calling 'pascal' calling convention), so you can go nutz with the number of params tacked on.

Anywho, look it over, use it if you like, and give feedback if you can ;)
Thanks again Japheth,
:alright:
NaN
Posted on 2002-12-30 04:46:24 by NaN
With this it should be rather ez to make an addon for your tool, to generate template functions for properties/methods. It keep EXE sizes under control, i think that each Method/Property should be selected from the Interface list by checkbox. So a person can ask for code for only the interfaces they will use. This is alot of extra bagage in these COM classes ;)

Anywho, just some thoughts. I've tested it further with no hitch, however, im about to test it with "get" type methods (not done yet).

:alright:
NaN
Posted on 2002-12-30 11:48:58 by NaN
Hi NaN,

thanks for the code. I would prefer not to use HeapAlloc for such helper functions, simple stack allocation (like "sub esp, dwbytes", you know? ;) ) would be more appropriate here. Ok , you may say "it works fine this way", but HeapAlloc can be a really costly operation.
Posted on 2002-12-30 14:11:15 by japheth
This code is slightly different with some (minor) fixes:
MakeInvokeString PROTO C cNum:DWORD, args:VARARG


;--------------------------------------------------------------+
; MakeInvokeString Rev 1.1 Dec 30,2002
;
; Use this to build A string representing the vararg parms
; passed into the InvokeHelp fucntion.
; The first argument MUST be total of all following arguments
; Following arguments must be valid VT_TYPES (WORD SIZED)
; but pushed as DOUBLES
;
; The return is a heap memory pointer with the array built
; for used in the InvokeHelp function.
;
; By NaN (c) 2002.
; Help and Support by Japheth and Thomas.
;--------------------------------------------------------------+
MakeInvokeString PROC C USES esi edi ebx cNum:DWORD, args:VARARG

invoke GetProcessHeap ;; Get the process heap pointer in EAX
mov edx, cNum ;; Get EDX = number of byte params
inc edx ;; Add one for NULL
shl edx, 1 ;; count = count * sizeof WORD
add edx, 8 ;; Align to next power of 4, with 4 byte header
and edx, 0FFFFFFFCh ;; Trim off any exess
invoke HeapAlloc, eax, NULL, edx ;; Use it to allocate
mov edx, cNum ;; Get the total count
mov edi, eax ;; Save hMem in edi
mov ebx, edi ;; Save hMem in ebx as well
mov [edi], edx ;; Store the count in the header
add edi, 4 ;; Set edi to start of string of bytes
lea esi, args ;; Get the arguments pointer
xor ecx, ecx ;; Set counter to zero
@@:
cmp ecx, edx ;; Is ecx == Count
je @F ;; Yes, end and tag on null
mov eax, [esi] ;; EAX == VTS type
mov [edi], ax ;; copy WORD into string
inc edi ;; EDI points to next byte location
inc edi ;;
add esi, 4 ;; ESI points to next argument
inc ecx ;; Counter is increased
jmp @B ;;
@@:
xor eax, eax ;; eax = 0
mov [edi], ax ;; tack on null at the end
mov eax, ebx ;; Return the starting pointer to the memory
ret
MakeInvokeString ENDP









InvokeHelp PROTO C pDispatch: LPDISPATCH, w_Did:DWORD, dw_dFlag:DWORD, w_oVT_TYPE:WORD, \
lpResult:DWORD, lpParam:DWORD, :VARARG
;-----------------------------------------------------------------+
; InvokeHelp Rev 1.1 Dec 30,2002
;
; Use this to call DISPATCHABLE interfaces by means of LATE Binding
;
; The params are:
; pDispatch :LPDISPATCH -- Disp Interface pointer
; w_Did :DWORD -- Disp. Unique ID of prop/meth
; dw_dFlag :DWORD -- Either:
; DISPATCH_PROPERTYPUT
; DISPATCH_PROPERTYGET
; DISPATCH_PROPERTYPUTREF
; DISPATCH_METHOD
; woVT_TYPE :WORD -- Return VT_TYPE to expect
; (Set NULL if not used)
; lpResult :DWORD -- Address to store return
; (Set NULL if not used)
; lpParam :DWORD -- Pointer to argument WORD
; array string, created by
; MakeInvokeString. This
; fuction will destory alloc
; heap memory created by
; MakeInvokeString.
; args :VARARGS -- Argument Data as indicated
; By the lpParam WORD string
; For QWORD SIZED arguments
; you must pass a pointer
; to where the data is found.
;
; By NaN (c) 2002.
; Help and Support by Japheth.
;--------------------------------------------------------------+
InvokeHelp PROC C uses edi esi ebx pDispatch: LPDISPATCH, w_Did:DWORD, dw_dFlag:DWORD, \
w_oVT_TYPE:WORD, lpResult:DWORD, lpParam:DWORD, args:VARARG

local dwPropPutId :DWORD
local dispparams :DISPPARAMS
local paramcount :DWORD
local lpVars :DWORD
local varReturn :VARIANT
LOCAL iErr :DWORD

; Get the length of the argument param list
;------------------------------------------+
mov esi, lpParam ;; Get the byte string address
mov ecx, [esi] ;; Get the number of arguments in ECX
mov paramcount, ecx ;; Save it for later
add esi, 4 ;; Move to the argument byte string <<<<<<<<<<<<[esi]

; Allocate Heap Memory for an array of Variants
;----------------------------------------------+
xor eax, eax ;; EAX = 0
mov lpVars, eax ;; Ensure lpVars == Null for later
mov dispparams.cArgs, eax ;; Indicate 0 arguments (for now)
invoke GetProcessHeap ;; Get the process heap pointer in EAX
mov edx, paramcount ;; EDX == Param count
.if(edx != NULL) ;; if EDX != NULL
mov dispparams.cArgs, edx ;; Indicate 'n' arguments
shl edx, 4 ;; ECX = ECX * 16 (Size of Variant)
invoke HeapAlloc, eax, NULL, edx ;; Use it to allocate Variant memory
mov edi, eax ;; EDI == Variants on the heap <<<<<<<<<<<<<<<<<[edi]
mov lpVars, edi ;; Save it for l8r (active value)


; Populate the Variants with the required data
;---------------------------------------------+
@@:
mov ebx, paramcount ;; EBX = paramsleft
xor eax, eax ;; EAX = 0
cmp ebx, eax ;; if EBX = 0
je @F ;; Then done, jump out
invoke VariantInit, edi ;; Initialize the Variant at EDI
xor eax, eax ;; EAX = 0 (after call)
dec ebx ;; EBX--
mov paramcount, ebx ;; Paramsleft--
mov edx, ebx ;; EDX = EBX
shl ebx, 2 ;; EBX = Count * sizeof DWORD
lea ecx, args ;; ECX = args address
add ebx, ecx ;; EBX == param offset for Variant data <<<<<<<[ebx]

shl edx, 1 ;; EDX == Count * sizeof WORD
add edx, esi ;; EDX == lpByteString + WORDCountOffset <<<<<<[edx]
mov ax, [edx] ;; AX == corresponding VT_ TYPE byte

mov [edi].VARIANT.vt, ax ;; Set the Variant Type
.if ( eax == VT_NULL)
nop
.elseif( eax == VT_BOOL)
mov eax, [ebx] ;; Get Param Data
and eax, 1 ;; Trim out any fat
mov [edi].VARIANT.boolVal, ax ;; Set Bool
.elseif(( eax == VT_I1 ) || ( eax == VT_UI1))
mov eax, [ebx] ;; Get the param data
and eax, 0FFh ;; Trim out any fat
mov [edi].VARIANT.bVal, al ;; Set Byte
.elseif(( eax == VT_I2 ) || ( eax == VT_UI2))
mov eax, [ebx] ;; Get the param data
and eax, 0FFFFh ;; Trim out any fat
mov [edi].VARIANT.uiVal, ax ;; Set the word
.elseif(( eax == VT_R8 ) || ( eax == VT_CY ) || (eax == VT_DATE))
mov eax, [ebx] ;; the pointer
mov ebx, eax ;; Reset ebx to pointer pointed to
mov eax, [ebx] ;; Get the low 4
mov [edi].VARIANT.ulVal, eax ;; Set the low 4
add ebx, 4
add edi, 4
mov eax, [ebx] ;; Get the high 4
mov [edi].VARIANT.ulVal, eax ;; Set the high 4
sub ebx, 4
sub edi, 4
.else
mov eax, [ebx] ;; Get data32 in General
mov [edi].VARIANT.ulVal, eax ;; Set data32 in General
.endif
add edi, 16 ;; EDI = EDI + Sizeof Variant
jmp @B ;; Next Param to process
@@:

.endif ;; End of IF Param COunt

; Make the disparams header
;--------------------------+
mov eax, lpVars ;; Get Variant List addres;
mov dispparams.rgvarg, eax ;; set dispatch params pointer;
mov eax, dw_dFlag
.if(( eax == DISPATCH_PROPERTYPUT ) || ( eax == DISPATCH_PROPERTYPUTREF) )
mov dispparams.cNamedArgs, 1 ;; Indicate 1 named argument
mov dwPropPutId, DISPID_PROPERTYPUT ;; Indicate 'get' property call
lea eax, dwPropPutId ;; get the address to this indication
mov dispparams.rgdispidNamedArgs, eax ;; Set the named agrument pointer
.else
mov dispparams.cNamedArgs, 0 ;; Indicate 0 named argument
mov dispparams.rgdispidNamedArgs, NULL ;; Set the named agrument pointer
.endif

; Make the Invoke Call
;---------------------+
.data
xlsIID_NULL sIID_NULL
.code

xor eax, eax
mov iErr, eax

.if !( lpResult )
invoke vf(pDispatch, IDispatch, Invoke_), w_Did, addr xlsIID_NULL, LOCALE_SYSTEM_DEFAULT,\
dw_dFlag, addr dispparams, NULL, NULL, addr iErr

xor eax, eax
mov edx, iErr

.else
invoke VariantInit, addr varReturn
xor eax, eax
mov ax, w_oVT_TYPE
mov varReturn.vt, ax

invoke vf(pDispatch, IDispatch, Invoke_), w_Did, addr xlsIID_NULL, LOCALE_SYSTEM_DEFAULT,\
dw_dFlag, addr dispparams, addr varReturn, NULL, addr iErr

xor eax, eax
mov ax, w_oVT_TYPE
invoke VariantChangeType, addr varReturn, addr varReturn, NULL, eax
mov edx, lpResult
lea ecx, varReturn
add ecx, 8
mov eax, [ecx]
mov [edx],eax

.if (( w_oVT_TYPE == VT_R8 ) || (w_oVT_TYPE == VT_CY ) || (w_oVT_TYPE == VT_DATE))
mov eax, [ecx+4]
mov [edx+4], eax
.endif

xor eax, eax
lea ecx, varReturn
mov ax, [ecx]

mov edx, iErr

.endif

push eax
push edx

; Clean up the Heaps
;-------------------+

.if (lpVars)
invoke GetProcessHeap
invoke HeapFree, eax, NULL, lpVars
.endif

invoke GetProcessHeap
invoke HeapFree, eax, NULL, lpParam

pop edx
pop eax

ret
InvokeHelp ENDP


It now works with all variant types, and returns what is asked for by converting the output variant on all 'get' calls.

I origionally though about doing the same (sub esp, xx) , but to be honest, its not something i practice and didnt feel conformatble taking on too much at once. Not sure how to get around call / ret's and still preserve the stack data??

If you wish to modify this code to do such please do! I agree the stack would be best, cause im allocating only small amounts of heap for the Variant WORD string, and the Variants themselves...

Anywho, with the above routines, i managed to do both put, get, and re-put data with excel.
       invoke MakeInvokeString, 3, VT_I4, VT_I4, VT_R8

invoke InvokeHelp, RNG, 0AAh, DISPATCH_PROPERTYPUT, NULL, NULL, eax, 1, 4, addr ar5


invoke MakeInvokeString, 2, VT_I4, VT_I4
invoke InvokeHelp, RNG, 0AAh, DISPATCH_PROPERTYGET, VT_DISPATCH, addr aout, eax, 2, 3


invoke MakeInvokeString, 0
invoke InvokeHelp, aout, 006h, DISPATCH_PROPERTYGET, VT_I4, addr lpStr, eax

invoke MakeInvokeString, 3, VT_I4, VT_I4, VT_I4
invoke InvokeHelp, RNG, 0AAh, DISPATCH_PROPERTYPUT, NULL, NULL, eax, 1, 1, lpStr


Thanx again!
:alright:
NaN
Posted on 2002-12-30 14:27:13 by NaN

Hi NaN,
thanks for the code. I would prefer not to use HeapAlloc for such helper functions, simple stack allocation (like "sub esp, dwbytes", you know? ;) ) would be more appropriate here. Ok , you may say "it works fine this way", but HeapAlloc can be a really costly operation.


I was thinking about this, i can see how to do such within the 'InvokeHelp' function, since its already scoped (ie. Variant Allocations). But how would i do it for the 'MakeInvokeString' function?? Upon the return the stack will be adjusted..

Unless i jump say 1000h bytes down the stack and assume i wont have enough calls to get here. Modify this area, and keep the stack pointer the same (unmodified), and pass the -1000 pointer into the 'InvokeHelp' function.

As well i guess it would not need to be destoryed.. simply forget about it..

I dunno, need to play around i guess..
:alright:
NaN
Posted on 2002-12-30 22:37:07 by NaN
I played around and discovered it was easier than i first expected. Im simply moving back 1000h in the stack to pass out of scope data between functions. So there is now no heap calls ;)

The 'MakeInvokeString' function jumps back 1000h on the stack and builds a string while preserving esp. Then it returns a pointer to this -1000h stack address.
MakeInvokeString PROTO C cNum:DWORD, args:VARARG


;--------------------------------------------------------------+
; MakeInvokeString Rev 1.2 Dec 31,2002
;
; Use this to build A string representing the vararg parms
; passed into the InvokeHelp fucntion.
; The first argument MUST be total of all following arguments
; Following arguments must be valid VT_TYPES (WORD SIZED)
; but pushed as DOUBLES
;
; The return is a stack memory pointer with the array built
; for used in the InvokeHelp function.
;
; By NaN (c) 2002.
; Help and Support by Japheth and Thomas.
;--------------------------------------------------------------+
MakeInvokeString PROC C USES esi edi ebx cNum:DWORD, args:VARARG

mov eax, esp ;; EAX == Stack data pointer
sub eax, 1000h ;; Back up 1000h hex in the stack for hMem data

mov edx, cNum ;; Get EDX = number of byte params
inc edx ;; Add one for NULL
shl edx, 1 ;; count = count * sizeof WORD
add edx, 12 ;; Align to next power of 4, with 4 byte header, 4 byte size
and edx, 0FFFFFFFCh ;; Trim off any exess

mov edi, eax ;; Save hMem in edi
mov ebx, edi ;; Save hMem in ebx as well

mov [edi], edx ;; Pre-Header How much memory is allocated
add edi, 4 ;; Increment to Param count Header

mov edx, cNum ;; Get the total count
mov [edi], edx ;; Store the count in the header
add edi, 4 ;; Set edi to start of string of bytes
lea esi, args ;; Get the arguments pointer
xor ecx, ecx ;; Set counter to zero
@@:
cmp ecx, edx ;; Is ecx == Count
je @F ;; Yes, end and tag on null
mov eax, [esi] ;; EAX == VTS type
mov [edi], ax ;; copy WORD into string
inc edi ;; EDI points to next byte location
inc edi ;;
add esi, 4 ;; ESI points to next argument
inc ecx ;; Counter is increased
jmp @B ;;
@@:
xor eax, eax ;; eax = 0
mov [edi], ax ;; tack on null at the end
mov eax, ebx ;; Return the starting pointer to the memory
ret
MakeInvokeString ENDP

The 'InvokeHelp' function take a lpParam pointer to this string data. It keep things all good for in-scope push's and pop's, I allocate more stack memory, copy it up, and readjust the lpParam pointer to point to the in-scope memory. This is so i can forget about it being outside the scope, and have the potential of functions / methods writing over this stack memory (unlikely, i realize, but its safe and can now be copied an pasted if need be). From here i also replaced inscope HeapAllocs with sub esp, xx as suggested. ;)
InvokeHelp PROTO C pDispatch: LPDISPATCH, w_Did:DWORD, dw_dFlag:DWORD, w_oVT_TYPE:WORD, \

lpResult:DWORD, lpParam:DWORD, :VARARG
;--------------------------------------------------------------------------------------------+
; InvokeHelp Rev 1.2 Dec 31,2002
;
; Use this to call DISPATCHABLE interfaces by means of LATE Binding
;
; The params are:
; pDispatch :LPDISPATCH -- Disp Interface pointer
; w_Did :DWORD -- Disp. Unique ID of prop/meth
; dw_dFlag :DWORD -- Either:
; DISPATCH_PROPERTYPUT
; DISPATCH_PROPERTYGET
; DISPATCH_PROPERTYPUTREF
; DISPATCH_METHOD
; woVT_TYPE :WORD -- Return VT_TYPE to expect (Set NULL if not used)
; lpResult :DWORD -- Address to store return (Set NULL if not used)
; lpParam :DWORD -- Pointer to argument WORD array string, created by
; MakeInvokeString.
; args :VARARGS -- Argument Data as indicated by the lpParam WORD string
; For QWORD SIZED arguments you must pass a pointer
; to where the data is found.
;
; By NaN (c) 2002.
; Help and Support by Japheth.
;--------------------------------------------------------------------------------------------+
InvokeHelp PROC C uses edi esi ebx pDispatch: LPDISPATCH, w_Did:DWORD, dw_dFlag:DWORD, \
w_oVT_TYPE:WORD, lpResult:DWORD, lpParam:DWORD, args:VARARG

local dwPropPutId :DWORD
local dispparams :DISPPARAMS
local paramcount :DWORD
local lpVars :DWORD
local varReturn :VARIANT
LOCAL iErr :DWORD
LOCAL stkVarSize :DWORD
LOCAL stkParamSz :DWORD

;----------------------------------------------------------+
; Copy Stack Memory into scope
;----------------------------------------------------------+
mov esi, lpParam ; Get pointer
mov ecx, [esi] ; Get copy size
mov eax, 4
sub ecx, eax ; Ignore the copy size DWORD
add esi, eax ; ESI = Start of data (header)
sub esp, ecx ; Allocate memory
mov stkParamSz, ecx ; Save this for cleanup
mov lpParam, esp ; Modify the lpParam pointer
mov edi, esp ; EDI = lpParam data
@@:
cmp ecx, NULL ; Will land on NULL cause ecx=aligned 4
je @F
mov eax, [esi] ; Copy Source outa scope
mov [edi], eax ; into dest, in scope
mov eax, 4 ; EAX = 4
add esi, eax ; ESI + 4
add edi, eax ; EDI + 4
sub ecx, eax ; ECX - 4
jmp @B ; Go again.
@@:
;----------------------------------------------------------+
; Now lpParam points to in scope data
;----------------------------------------------------------+



; Get the length of the argument param list
;------------------------------------------+
mov esi, lpParam ;; Get the byte string address
mov ecx, [esi] ;; Get the number of arguments in ECX
mov paramcount, ecx ;; Save it for later
add esi, 4 ;; Move to the argument byte string <<<<<<<<<<<<[esi]

; Allocate Heap Memory for an array of Variants
;----------------------------------------------+
xor eax, eax ;; EAX = 0
mov stkVarSize, eax ;; Pre-init the stack size to NULL
mov lpVars, eax ;; Ensure lpVars == Null for later
mov dispparams.cArgs, eax ;; Indicate 0 arguments (for now)
mov edx, paramcount ;; EDX == Param count
.if(edx != NULL) ;; if EDX != NULL
mov dispparams.cArgs, edx ;; Indicate 'n' arguments
shl edx, 4 ;; ECX = ECX * 16 (Size of Variant)
mov stkVarSize, edx ;; Save Stack Variant size
sub esp, edx ;; Allocate memory from stack
mov edi, esp ;; EDI == Variants on the heap <<<<<<<<<<<<<<<<<[edi]
mov lpVars, edi ;; Save it for l8r (active value)


; Populate the Variants with the required data
;---------------------------------------------+
@@:
mov ebx, paramcount ;; EBX = paramsleft
xor eax, eax ;; EAX = 0
cmp ebx, eax ;; if EBX = 0
je @F ;; Then done, jump out
invoke VariantInit, edi ;; Initialize the Variant at EDI
xor eax, eax ;; EAX = 0 (after call)
dec ebx ;; EBX--
mov paramcount, ebx ;; Paramsleft--
mov edx, ebx ;; EDX = EBX
shl ebx, 2 ;; EBX = Count * sizeof DWORD
lea ecx, args ;; ECX = args address
add ebx, ecx ;; EBX == param offset for Variant data <<<<<<<[ebx]

shl edx, 1 ;; EDX == Count * sizeof WORD
add edx, esi ;; EDX == lpByteString + WORDCountOffset <<<<<<[edx]
mov ax, [edx] ;; AX == corresponding VT_ TYPE byte

mov [edi].VARIANT.vt, ax ;; Set the Variant Type
.if ( eax == VT_NULL)
nop
.elseif( eax == VT_BOOL)
mov eax, [ebx] ;; Get Param Data
and eax, 1 ;; Trim out any fat
mov [edi].VARIANT.boolVal, ax ;; Set Bool
.elseif(( eax == VT_I1 ) || ( eax == VT_UI1))
mov eax, [ebx] ;; Get the param data
and eax, 0FFh ;; Trim out any fat
mov [edi].VARIANT.bVal, al ;; Set Byte
.elseif(( eax == VT_I2 ) || ( eax == VT_UI2))
mov eax, [ebx] ;; Get the param data
and eax, 0FFFFh ;; Trim out any fat
mov [edi].VARIANT.uiVal, ax ;; Set the word
.elseif(( eax == VT_R8 ) || ( eax == VT_CY ) || (eax == VT_DATE))
mov eax, [ebx] ;; the pointer
mov ebx, eax ;; Reset ebx to pointer pointed to
mov eax, [ebx] ;; Get the low 4
mov [edi].VARIANT.ulVal, eax ;; Set the low 4
add ebx, 4
add edi, 4
mov eax, [ebx] ;; Get the high 4
mov [edi].VARIANT.ulVal, eax ;; Set the high 4
sub ebx, 4
sub edi, 4
.else
mov eax, [ebx] ;; Get data32 in General
mov [edi].VARIANT.ulVal, eax ;; Set data32 in General
.endif
add edi, 16 ;; EDI = EDI + Sizeof Variant
jmp @B ;; Next Param to process
@@:

.endif ;; End of IF Param COunt

; Make the disparams header
;--------------------------+
mov eax, lpVars ;; Get Variant List addres;
mov dispparams.rgvarg, eax ;; set dispatch params pointer;
mov eax, dw_dFlag
.if(( eax == DISPATCH_PROPERTYPUT ) || ( eax == DISPATCH_PROPERTYPUTREF) )
mov dispparams.cNamedArgs, 1 ;; Indicate 1 named argument
mov dwPropPutId, DISPID_PROPERTYPUT ;; Indicate 'get' property call
lea eax, dwPropPutId ;; get the address to this indication
mov dispparams.rgdispidNamedArgs, eax ;; Set the named agrument pointer
.else
mov dispparams.cNamedArgs, 0 ;; Indicate 0 named argument
mov dispparams.rgdispidNamedArgs, NULL ;; Set the named agrument pointer
.endif

; Make the Invoke Call
;---------------------+
.data
xlsIID_NULL sIID_NULL
.code
xor eax, eax
mov iErr, eax

.if !( lpResult ) ;-------------------------------------------------------------------+ [IF START]

; Do the DISP:Invoke for SET types
;---------------------------------+
invoke vf(pDispatch, IDispatch, Invoke_), w_Did, addr xlsIID_NULL, LOCALE_SYSTEM_DEFAULT,\
dw_dFlag, addr dispparams, NULL, NULL, addr iErr
xor eax, eax ;; Eax == 0 (no return info)
mov edx, iErr ;; Edx == Any found param error

.else ;----------------------------------------------------------------------------------+ [ELSE]

invoke VariantInit, addr varReturn ;; Init the return Variant
xor eax, eax ;; Eax == 0
mov ax, w_oVT_TYPE ;; Get out type
mov varReturn.vt, ax ;; Set it

; Do the DISP:Invoke for GET types
;---------------------------------+
invoke vf(pDispatch, IDispatch, Invoke_), w_Did, addr xlsIID_NULL, LOCALE_SYSTEM_DEFAULT,\
dw_dFlag, addr dispparams, addr varReturn, NULL, addr iErr
xor eax, eax ;; EAX == 0
mov ax, w_oVT_TYPE ;; Get the desired return typ

; Modify the Return to what we asked for
;---------------------------------------+
invoke VariantChangeType, addr varReturn, addr varReturn, NULL, eax
mov edx, lpResult ;; Get destination address
lea ecx, varReturn ;; Get return variant address
add ecx, 8 ;; got to variant data area
mov eax, [ecx] ;; Copy out the low DWORD
mov [edx],eax ;; Save low DWORD in dest

; If QWORD types, then copy High DWORDS as well
;----------------------------------------------+
.if (( w_oVT_TYPE == VT_R8 ) || (w_oVT_TYPE == VT_CY ) || (w_oVT_TYPE == VT_DATE))
mov eax, [ecx+4] ;; Get High DWORD
mov [edx+4], eax ;; Set High DWORD
.endif

xor eax, eax ;; EAX = 0
lea ecx, varReturn ;; Get return variant address
mov ax, [ecx] ;; Copy EAX == Return VT_TYPE
mov edx, iErr ;; Set EDX to any error returned

.endif ;-------------------------------------------------------------------------------+ [END IF]


; Clean up the Stacks
;-------------------------------------------------+
.if (lpVars)
add esp, stkVarSize ;; Fix stack Variants (if any) [Allocated Last]
.endif
add esp, stkParamSz ;; Fix lpData stack (lpData is now invalid)
;-------------------------------------------------+
ret
InvokeHelp ENDP


Anywho, let me know what you think ;)
It works great so far, I havent yet tested it on 'METHOD' types, but PUT/GET PROPERTY types work great!

:alright:
NaN
Posted on 2002-12-31 00:59:05 by NaN