Although it's a "final" version bug reports are still welcome :) .

have fun!

Posted on 2003-02-01 14:45:51 by japheth
Great work Japheth.. I like your example source as well.

However, i was randomly playing with it and found a bug. I opened it up, sorted the classid's by typelib, selected the first entry (MS animation control) and tried to create an instance on. It asked for some cpu name to create on, i click ok with it blank and it crashed with:
COMVIEW caused an invalid page fault in

module COMVIEW.EXE at 017f:00415448.
EAX=00000002 CS=017f EIP=00415448 EFLGS=00010202
EBX=0065f724 SS=0187 ESP=0065f6d0 EBP=0065f6d0
ECX=00000b14 DS=0187 ESI=00008748 FS=2c47
EDX=00415442 ES=0187 EDI=0065f6d8 GS=0000
Bytes at CS:EIP:
89 08 eb 0b 6a 08 ff 75 08 ff 15 78 62 41 00 0b
Stack dump:
0065f6f0 bff7363b 00000b14 00000110 00000acc 00000002
8722325f 00000187 0065f704 bff94407 68bf8748 000068bf
00000000 bff719b8 00008742 0065fbc4

As well doing just Create Instance (f7) crashes immediately as well:
COMVIEW caused an invalid page fault in

module COMCT232.OCX at 017f:20b72702.
EAX=20b71a90 CS=017f EIP=20b72702 EFLGS=00010246
EBX=0065f760 SS=0187 ESP=0065f6f4 EBP=0044637c
ECX=00000000 DS=0187 ESI=00447654 FS=5e4f
EDX=00447644 ES=0187 EDI=20b72408 GS=0000
Bytes at CS:EIP:
8b 41 04 50 8b 00 ff 50 04 c3 8b 4c 24 04 8b 41
Stack dump:
20b763a7 0044637c 0065f720 004464f4 00447644 0000000c
20b72304 00447644 0065f760 004464f4 00447644 0065f774
00409440 00446388 00447644 0065f760

Still quite useful tho ;)
Posted on 2003-02-02 10:46:11 by NaN
Hi NaN,

thanks for feedback.

You've found a bug :) . For the first error there exists a workaround: set option "query all interfaces with CoCreateInstanceEx" in CLSID options to on. With that option off creating a remote object would cause multiple QueryInterfaces calls - if it would work.

Posted on 2003-02-02 12:43:09 by japheth

Off topic, but i got a currious question for you...

A while ago we discovered that early-bound calls were crashing with MS Excel (case in point the _Worksheet interface). So the work around led us to the development of the new dm() macro and late binding methods (largely due to your help).

This provided means to these interfaces, however, im still calling a virtural funtion (early binding) on them and not realizing it ~ the IUnknown::Release() method.

While all my work so far has not crashed ever from a Release call, i wondering if there is potential for memory leaks here, as im not sure i can even trust any form of early binding on interfaces like _Worksheets, Range, etc. (How would i know if it *did* release??)

Anywho, I hope you understand what im getting at, and look forward to you thoughts...
Posted on 2003-02-04 21:35:32 by NaN
Hi NaN,

Im not sure if I understand your concerns.But as far as I know an interface inheriting from IDispatch must have at least the 7 virtual functions of IDispatch in its vtable (and these functions cannot be called with dm() macro. The rest may depend (dual or dispatchonly). So everything should be quite right.

I' ve tested your COMCT232.OCX bug (took me a while to find a copy of this module from VC++ CD). It crashes, too, but it crashes during startup at a QueryInterface call, so there isnt much what can be done. Workaround: take another CLSID :) .

Posted on 2003-02-05 04:54:54 by japheth
Thankx Japheth,

Your prooved the point.. i forgot that there *is* a clear separation between the calling styles for the inherited IDispatch, and the resulting object (Range, or whatever). I was assuming it to be constant through all vtable entries (which included the release method). Anyways, good, i happy i was worried for no reason ;)

As for the ComView bug.. sure these things are to be expected i guess, Ms seems to have a funny way of not following their own rules (MS Excel's "_Worksheet" inteface, and now the Visual C++ OCX is not behaving proberly... :rolleyes: )

PS: I like the clean up you did on the asm generation as well. :alright: . Also i like the typelib highlighting as you use interfaces (another good feature). One thing i found a little troubling is that the ComView still hides in its display the LCID params when you double click on a specific typelib item. I have to consult the generated ASM's to verify that there *is* a hidden param (kinda of a wasted extra step when ComView is generating these asm's in the first place). This is just a thing i've noticed cause im using ComView alot as i develop my latest project. Looking up an extra ASM file is not big overhead, its just you have to remember to do so ;)

Thanx again.
Posted on 2003-02-05 16:26:14 by NaN
I have another question for you Japheth,

How does your ComView deal with Collections?

When i double click on an enumerated list, a collection is obtained, and presented in a popup window. From here if i double click on any of the entries it uses the Item property and selects that item (this impressed me for its ability). However, im more intereseted in knowing if you wrote your own code to 'collect' the enumeration, or if there is a global COM object on everyone's computer desigend for such things? (I dont want to re-invent the wheel if i dont have to ;) )

Thanks again!
Posted on 2003-02-05 16:47:50 by NaN
Do you use SafeArrays as a collection object?? (any suggestions, pros', cons' ?)

Thanx ;)
Posted on 2003-02-05 21:53:01 by NaN
Hi NaN,

first point to mention: if you (or anybody else) is using comview 1.6.0 under a win9x system, I strongly suggest to download the newest version (it has reached 1.6.2 already :grin: ). Thats because in 1.6.0 there is a bug in COMViews termination routine causing the system to behave somewhat strange after comview has been closed (it errouneously closes a hidden OLE window during termination which, I suppose, does some DDE stuff. In consequence, copying data to clipboard or starting a win16 app seems to "hang" the system. win9x systems! only). So before you format your HD because you suspect a virus just download the newer version.

About your question:
for displaying/editing collections and (safe)arrays of variants I made a class CCollectionDlg handling this stuff. All is done "by hand", which isnt so much. the object is queried to expose IEnumVARIANT or IEnumUnknown, then read all items in an array of variants with IEnumVariant:Next. After that the code for collections and safearrays is pretty much the same.

About hiding LCID: comview doesnt hide the LCID parameter. It just isnt there. For dispatch interfaces there in fact exist 2 interfaces (which you may view with comview). Just open a interface with "TKIND_DISPATCH" and select tab "interfaces". There should be an entry (with identical or similar name), which has the same IID, but is of type "TKIND_INTERFACE". If you doubleclick it, you may examine it and see that THIS interface does have all the LCIDs and RETVAL parameters (and this instance is used by comviews code generator).

Posted on 2003-02-06 02:25:43 by japheth
Thanks for the advice Japheth :alright: (Yes i did notice stange system things.. IExplorer would hang indefinitely, and then crash the systray. If i was luck i could rebot safely from here). I suspected it was from me using COM's like Excel (and not closing out an interface properly, not your program.. thanx for the heads up).

As well i will write some code tonight with the Enum interfaces and see how it works, thanks for the tip :)

Posted on 2003-02-06 17:05:59 by NaN
Any chance you want to share your CCollectionDlg with me ;)

I just tried a hack at it (following MSDN doc's) and ended up with one wicked crash (no chances to save that, even the BSOD was too late :grin: )

Still hacking at it (good learning anyways).. found this link anyways
Posted on 2003-02-06 18:31:59 by NaN
Im getting there, but im stumped on one area.

I figured out how to get the collection, (calling the get__NewEnum interface property). This returns a 32 bit value (not sure what type of pointer it is). I call QueryInterface on it for "IID_EnumVariant" and it returns a pIEnumVariant. From here i can call the IEnumVariant::Next method with a param line "1, addr varTemp, NULL" (varTemp is a Variant). Looking at the returned variant, i see its of type "VT_DISPATCH", i copy the dispatch pointer out and cant get any further.....

Using Comview, and retracing the above steps, i know the interface pointer copied out from the variant is of type IAcadDocument (enumerated from the IAcadDocuments collection). However, when i apply this interface to it returns an error... (An interface that i know works correctly).

So the best recourse i see is to debug it and find out just what type of dispatchable interface i have... but im not sure how to query it for its Interface Name.... perhaps if i figure this out, i can realize why its not reacting to the interface im applying to this pointer...

Any thoughts, help??
Posted on 2003-02-06 20:43:13 by NaN
Well, i later discovered that i was doinging things correctly (or i assume so anyways). Turns out that the property i chose wont work when the interface is gotten this way.. if i get the same interface through the Item method, it works flawlessly?? I puzzled, and dont like inconsistancies... :(

Here is the code I've put together
.if( pIAcad )

Zero TempA
vZero vTempA

invoke vf(pIAcad, IAcadApplication, get_Documents), addr TempA
.if( TempA )

Zero TempB

invoke vf(TempA, IAcadDocuments, get__NewEnum), addr TempB
invoke vf(TempA, IAcadDocuments, get_Count), addr TempC
invoke vf(TempA, IUnknown, Release)
.if( TempB )

Zero TempA

invoke vf(TempB,IUnknown,QueryInterface), addr IID_IEnumVARIANT,addr TempA
invoke vf(TempB, IUnknown, Release)

.if( TempA )

Zero TempB

invoke vf( TempA, IEnumVARIANT, Next), 1, addr vTempA, addr TempC

mov eax, vTempA.lVal
mov TempD, eax

invoke vf( TempD, IAcadDocument, get_ModelSpace), addr TempE
PrintHex eax
invoke vf( TempD, IUnknown, Release)

invoke vf( TempA, IUnknown, Release)




Im not checking for VT_DISPATCH type from vTempA, as there is always at least one Document. The get_ModelSpace property works if i get the IAcadDocument from the IAcadDocuments::Item method, however, getting it from the IEnumVARIANT interface works partially...??

Should i query for every interface used?? This is alot of extra work...??

Posted on 2003-02-06 23:14:26 by NaN
Code looks good to me (I would suggest to do a VariantInit to initialize vTempA).
You may query the pointer returned from IEnumVariant::Next for IACadDocument interface, but if comview shows it to be of this type already, this wont help much.

I used this to get the type of a pDispatch in string form (pDispatch comes directly from IEnumVariant::Next):

GetDispatchType proc pDispatch:LPDISPATCH, pszTextOut:LPSTR, dwMax:DWORD

local dwRC:DWORD
local pTypeInfo:LPTYPEINFO
local bstr:BSTR

mov dwRC, 0
invoke vf(pDispatch, IDispatch, GetTypeInfo), 0, LANG_SYSTEM_DEFAULT, addr pTypeInfo
.if (eax == S_OK)
invoke vf(pTypeInfo, ITypeInfo, GetDocumentation), MEMBERID_NIL, addr bstr, NULL, NULL, NULL
.if (eax == S_OK)
invoke WideCharToMultiByte, CP_ACP, 0, bstr, -1, pszTextOut, dwMax, 0, 0
mov eax, pszTextOut
mov dwRC, eax
invoke SysFreeString, bstr
invoke vf(pTypeInfo, ITypeInfo, Release)
return dwRC

GetDispatchType endp

Posted on 2003-02-07 02:38:45 by japheth
Thanx for the routine.. I was trying to write one like it last night, but was getting all confused and lost in the documentation (never played with these interfaces before and was too tire last night to want to ;) )

I will give the routine a try when i get home and see just what i *am* getting back from the variant. BTW, i am using VariantInti(), its wrapped up in a macro called "vZero" ;)

Anywho, thanx for your help.
Posted on 2003-02-07 13:47:15 by NaN
Well i managed to use the interface finally. Your routine confirmed I was definitely getting a IAcadDocument interface returned by the enumeration.

The surprise is this:

; invoke vf( vTempA.lVal, IAcadDatabase, get_ModelSpace), addr TempB

invoke MakeInvokeString, 0
invoke dm(vTempA.lVal, MyIAcadDocument, get_ModelSpace), VT_DISPATCH, addr TempB
Early binding will cause a crash. Late binding will return the interface. This is just like the "_Worksheet" interface in Excel. However your tool doesnt recognize it for late binding and only generates early binding virtual definitions. (So i manually looked up the Memid code and made a definition that suited my need). Do you have any idea why its misssing this?

A while ago you discovered that the dual interfaces are dispatchable only or something like that.. is there something simular here that is not getting noticed??

Anywho, thanks for your help so far... it is appreciated!
Posted on 2003-02-07 17:01:49 by NaN

have no idea what to do about that. If an interface is marked as "dual" (as the _Worksheet interface in excel) it should be safe to call methods thru vtable - obviously its not always the case, seems a bug in excel to me. Therefore I've added option "generate dispatch helpers for dual interfaces as well" to the tool. May be I should add a simple dialog to show the vtables of created objects - the _Worksheet vtable has entries with value NULL, which clearly indicates at least some methods aren't assumed to be called by vtable.
Posted on 2003-02-08 07:48:38 by japheth
Do you think it has anything to do with the fact that the enumeration and its contents are ment for dynamic allocations? (Hence late binding).

I ask this because i can ask for the same interface from another method (IAcadDocuments::Item(x)) and the returned IAcadDocument interface will work properly with vf() early binded calls. So somehow two separate methods from the same interface is producing two different versions of IAcadDocuments.

More outlandish, and not very likely, but is it possible that the Enumerated IAcadDocument interface is generated differently for some reason, such as droping its inherited IAcadDatabase, but still including the IDispatch?? This would cause such erronious crashes, since the vtable for the vf() calls would no longer be accurate... Hmm.. wish i had softice installed to see what function is looked up between the two calling styles for the same property...

Posted on 2003-02-08 13:53:27 by NaN