where did you get your VARIANTARG struct from?

When I look in my definition I see

1. VARIANTARG is defined as:


2. VARIANT is defined as

VARIANT struct
wReserved1 WORD ?
wReserved2 WORD ?
wReserved3 WORD ?
lVal SDWORD ? ; VT_I4
bVal BYTE ? ; VT_UI1

Your definition differs from that. Are you sure yours is correct?

Posted on 2002-02-28 10:48:26 by japheth
where did you get your VARIANTARG struct from?

I felt like the includes could be wrong, that's why I've posted them together with the code.
However I've taken the struct def from a borland cpp include file...
except I've copied the WRONG LINES
I feel quite an idiot, sorry :( and thanks for your patience!!!


P.S.:I've fixed the variantarg definition but I still can't set properties. I always get 80020009 = Exception occurred.
Is there somethin else to fix?!

P.P.S.: Ok, ok, I'm even more stupid...
Please forget the pevious line I just used the ID of a ReadOnly property.
Now EVERYTHING works perfectly thanx to all you people who patiently replyed to my silly posts...

Just last (I promise) little question: how do I get an interface vtable from a com server (exe or dll)?
Thanks again,
Posted on 2002-02-28 11:03:56 by acab
Ahh, VB? I've got that (versions 3 thru 6), so if the .exe is small enough, you can send that along.

But let me show you how I would find a vtable for a new class. I'm going to make a exe server project in VB. The only code I wrote was in the supplied class using the Add Procedure menu:

Option Explicit

Public Sub Sub1()

End Sub

Public Property Get SomeProperty() As Long

End Property

Public Property Let SomeProperty(ByVal vNewValue As Long)

End Property

Public Function Function1()

End Function

Pretty simple for a sample. Now, after I compile it (which also registers it), I open OLE View and scan for Project1. Complete disaster. OLE View has always been buggy, and you are lucky when it works.

So I move on to Tlb2Inc.exe, a utility written by Maurice MONTGENIE. It reads type libs, and translated them into MASM .inc files. It's WONDERFUL, though I'm not so sure it got the method names correct this time (but it usually does, so it's worth a try).

Here's what it told me about Project1:

; Generated .INC file (by Tlb2Inc v1.2.3 - Copyright (c) 2001 Maurice MONTGENIE)
; TypeLib filename: Project1.exe

IFNDEF _Project1_INC_
_Project1_INC_ EQU 1

;Library : Project1
sLIBID_Project1 TEXTEQU <{0CB61A1D9H, 02C99H, 011D6H, \
{0A3H, 024H, 000H, 050H, 0BAH, 05AH, 09FH, 026H}}>

;CoClass : Class1
sCLSID_Class1 TEXTEQU <{0CB61A1DBH, 02C99H, 011D6H, \
{0A3H, 024H, 000H, 050H, 0BAH, 05AH, 09FH, 026H}}>

;DispInterface : _Class1
sIID__Class1 TEXTEQU <{0CB61A1DAH, 02C99H, 011D6H, \
{0A3H, 024H, 000H, 050H, 0BAH, 05AH, 09FH, 026H}}>

_Class1_Sub1Proto typedef proto _Class1_Sub1Proto :DWORD
_Class1_get_SomePropertyProto typedef proto _Class1_get_SomePropertyProto :DWORD,:DWORD
_Class1_put_SomePropertyProto typedef proto _Class1_put_SomePropertyProto :DWORD,:SDWORD
_Class1_Function1Proto typedef proto _Class1_Function1Proto :DWORD,:DWORD

_Class1_Sub1Val typedef ptr _Class1_Sub1Proto
_Class1_get_SomePropertyVal typedef ptr _Class1_get_SomePropertyProto
_Class1_put_SomePropertyVal typedef ptr _Class1_put_SomePropertyProto
_Class1_Function1Val typedef ptr _Class1_Function1Proto

_vt_Class1 MACRO CastName:REQ
; IDispatch methods
_vtIDispatch CastName
; _Class1 methods
&CastName&_Sub1 _Class1_Sub1Val ?
&CastName&_get_SomeProperty _Class1_get_SomePropertyVal ?
&CastName&_put_SomeProperty _Class1_put_SomePropertyVal ?
&CastName&_Function1 _Class1_Function1Val ?


_Class1 STRUCT
_vt_Class1 _Class1
_Class1 ENDS


Cool, huh? Almost makes you wanna use MASM over TASM, huh? <wink>

You can get this handy tool at
Posted on 2002-02-28 22:09:31 by Ernie
Hi Ernie,
I managed at last to write a fully functional ole client in (t)asm.
Yesterday, after all the strictly-ole-related issues were fixed, I felt in troubles again with string arguments. I needed to deeply reverse a pack of code to fix the bug. At last I discovered (my win32.hlp says nothing about this) that a Unicode string need to be preceded by a dword that specifies its length in bytes.
Except for this I found out that com programming is quite easy once you learn how it works. It's just little documented :(.
Just one more question. I downloaded Tlb2inc: it's great! It works perfectly also determining ID's (oleview scrambles them around).
What I still can't figure out is why some entries inside the vtable are zeroed.
For example, in my case, I found out that the entry that should correspond to IApplication.Quit method is just an empty dword. So I used IDispatch::Invoke and I traced the code for a while just to reach the true function entrypoint. Then I've searched for a corresponding entry inside the vtable but I got no result.
Is it possible that some methods are implemented only through IDispatch and not via the vtablr?

Thanx a lot for all your help,
Posted on 2002-03-01 14:44:45 by acab
What I still can't figure out is why some entries inside the vtable are zeroed.

Where are these values? Are you tracing into the object you requested?

While this is OK for debugging, it's strictly a violation of the COM standard, as clients are permitted to do anything in there they want, and you're not supposed to peek.

But it does sound quite odd to me, I could not explain it.

Is it possible that some methods are implemented only through IDispatch and not via the vtablr?

An interface with a vtable entry for every method is called a dual interface, it gives you the best of both worlds, IDispatch for late binding clients (mostly script driven code) and a vtable for early binding for compiled type library code (such as VB or VC).

However, that is just one way to do it. A dispinterface has no vtable for the methods, it just has the 7 IDispatch methods.

Now for some methods having a vtable, and some not, I don't think such an interface is legal. Perhaps if a dual interface is inherited by a dispinterface it might have some but not all methods, but I've never seen that, nor do I have a guess how to define it in IDL (interface definition language, the interface language of COM).

If you get an answer on your own, please post it back. I'm curious to see what this is.

And also, congratulations on your first TASM client. I believe you are the first one to achieve that.
Posted on 2002-03-01 17:27:00 by Ernie

Platform SDK: COM

Dual Interfaces
OLE Automation enables an object to expose a set of methods in two ways: via the IDispatch interface, and through direct OLE VTable binding. IDispatch is used by most tools available today, and offers support for late binding to properties and methods. VTable binding offers much higher performance because this method is called directly instead of through IDispatch::Invoke. IDispatch offers late bound support, where direct VTable binding offers a significant performance gain; both techniques are valuable and important in different scenarios. By labeling an interface as dual in the type library, an OLE Automation interface can be used either via IDispatch, or it can be bound to directly. Containers can thus choose the most appropriate technique. Support for dual interfaces is strongly recommended for both controls and containers.

From this I myself conclude that it would be considered 'illegal' for an OLE Automation Object to NOT support all functions via vtable that it supports via IDispatch, since a COM object has no way of telling a client that it can not use direct VTable binding.
Posted on 2002-03-01 17:55:12 by Hiroshimator
This is the vtable struct for IApplication reported by Tlb2Inc for IApplication interface:

_vtIApplication MACRO CastName:REQ
; IDispatch methods
_vtIDispatch CastName
; IApplication methods
CastName&_Quit IApplication_QuitVal ? ; <--- This is the method I need to call
&CastName&_PrinterSystem IApplication_PrinterSystemVal ?
&CastName&_Resize IApplication_ResizeVal ?
&CastName&_Move IApplication_MoveVal ?
&CastName&_ShowHelp IApplication_ShowHelpVal ?
&CastName&_GetLastError IApplication_GetLastErrorVal ?
&CastName&_ErrorMessage IApplication_ErrorMessageVal ?

And this is the actual vtable returned by cocreateinstance and dumped from the debugger:

1b ee 34 65 4d 4c 34 65 53 4c 34 65 f0 22 b9 7e
0a 23 b9 7f 20 2d b9 7f cb 52 34 65 00 00 00 00 <---
c8 6b 34 65 40 67 34 65 ........... ...........

As you can see the first seven entries (IUnknown+IDispatch) are ok.
The 8th entry, that should point to IApplication.Quit is zeroed.
Can you explain this?

Posted on 2002-03-03 07:27:59 by acab
It is odd but possibly its not "illegal". Every function is described by a FUNCDESC structure, and in that structure there is member funckind with possible values:

So theoretically you can decide for every function if it can be called via vtable, dispatch or both

I think it must be possible to set these flags by IDL/ODL, but currently I have no doc available.
Posted on 2002-03-04 07:24:41 by japheth
I'd still need to peek at the program, or at least the typelib for the program.

Do you have a resource editor that can tear the typelib out of the resource and just post/email me that?
Posted on 2002-03-04 19:23:43 by Ernie
Hi Ernie,
you have mail.

Posted on 2002-03-05 04:51:57 by acab
I got it, thanks. Gimmy a day or two to digest it (OLE view doesn't like it).
Posted on 2002-03-05 23:10:42 by Ernie
thanx Ernie
Posted on 2002-03-06 05:03:01 by acab
Hope ya don't mind - had some minor glitches implementing this...
My DISPPARAMS struct shows that dwType should have been a WORD size.
Aside from that it's now using COINVOKE, and I fixed a problem with one of the error checks ... Hopefully someone else will find this useful.

ComCall PROC wType:WORD, lpvResult:DWORD, hObject:DWORD, lpszName:DWORD, dwArgs:DWORD, lpxArgs:PTR
LOCAL lpWide:DWORD, idProp:DWORD, lpxParams:PTR, dwPut:DWORD

sub esp, MAX_PATH
mov lpWide, esp
sub esp, sizeof DISPPARAMS
mov lpxParams, esp

mov eax, hObject
test eax, eax
je auwParam

;----------------- convert name to unicode
invoke MultiByteToWideChar,CP_ACP,MB_PRECOMPOSED,lpszName,-1,lpWide,MAX_PATH
.if eax==0
jmp auwError
;----------------- get id for the name
coinvoke hObject, IDispatch,GetIDsOfNames, addr IID_NULL, addr lpWide, 1, LOCALE_USER_DEFAULT, addr idProp
jmp auwExit

;----------------- fill param struc
xor edx, edx
mov ebx, lpxArgs
mov eax, lpxParams
mov ecx, dwArgs
mov [eax.DISPPARAMS.cArgs], cx
mov [eax.DISPPARAMS.rgvarg], ebx
mov [eax.DISPPARAMS.cNamedArgs], dx
mov [eax.DISPPARAMS.rgdispidNamedArgs], edx
je auwInvoke

lea ecx, dwPut
mov [eax.DISPPARAMS.cNamedArgs], 1
mov [eax.DISPPARAMS.rgdispidNamedArgs], ecx

;----------------- invoke object
xor eax, eax
coinvoke hObject, IDispatch,Invoke,idProp,addr IID_NULL,LOCALE_USER_DEFAULT,wType,lpxParams,lpvResult,0,0
jmp auwExit

;----------------- exit cases

auwParam: mov eax, E_INVALIDARG
jmp auwExit

auwError: call GetLastError
auwExit: add esp, sizeof DISPPARAMS
add esp, MAX_PATH

ComCall ENDP
Posted on 2003-07-04 12:19:34 by Homer