Japheth,

Im still hacking thru your COM example (Simple server). And im wondering why you bother to use offsets in the interface map.

I mean, the IUnknown, IDispatch, ISimpleServer is all based at an offest of zero. And should always be this if your truely inheriting other interfaces (albeit, non-aggregated).

I see you like to use typing alot with your source, but when you boil it down, the code ultimately places a zero offest every time (CSimpleServe._IDispatch == offset 0):
iftab	label dword

dd offset IID_IUnknown, 0
dd offset IID_IDispatch, CSimpleServer._IDispatch
dd offset IID_ISimpleServer, CSimpleServer._IDispatch
;--------------------- insert new exposed interfaces here
iftabsize equ ($ - offset iftab) / 8


Thanks..
:NaN:
Posted on 2003-09-15 20:20:55 by NaN
NaN,

youre right, the offset is always zero in this sample.

But if you add another interface (say: IProvideClassInfo) the server object will implement "multiple inheritance" and needs more than one vtable (first at offset 0 for IUnknown, IDispatch, IComExeSvr, second at offset 4 for IProvideClassInfo). BTW: thats the same way as VC does implement this.

I made an EXE server sample supporting events, which adds interface IConnectionPointContainer and so in fact needs more than one vtable. Download it from http://www.japheth.de/download/ComExeSvr2.zip. Besides the event stuff (and being an EXE instead of DLL) the sample is mainly the same.


I just looked around a bit and found a C++ sample (hosting webbrowser control, which was converted to ASM by Xtreme some years ago). The class is defined as:


class CBrowserWindow : public IOleClientSite,
public IOleInPlaceSite,
public IDocHostUIHandler,
public IDocHostShowUI,
public IDispatch
{

public:
CBrowserWindow();
~CBrowserWindow();


and QueryInterface is implemented as


STDMETHODIMP CBrowserWindow::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
*ppvObject= NULL;

if (IsEqualIID(riid, IID_IUnknown))
{
*ppvObject = this;
}
else if (IsEqualIID(riid, IID_IOleClientSite))
{
*ppvObject = static_cast<IOleClientSite*>(this);
}
else if (IsEqualIID(riid, IID_IDocHostUIHandler))
{
*ppvObject = static_cast<IDocHostUIHandler*>(this);
}
else if (IsEqualIID(riid, IID_IDocHostShowUI))
{
*ppvObject = static_cast<IDocHostShowUI*>(this);
}
else if (IsEqualIID(riid, IID_IOleInPlaceSite))


the casting done here will in fact add offset of vtables to "this" as it is done in simpleserver sample.



Japheth
Posted on 2003-09-16 00:01:41 by japheth
Thanks, i will have to look closer at this. Im still not a 100% how this works, but hopefully it will sink in ;)

I was under the impression you would make a separate interface map for another interface. Not offset to it.

Regards,
NaN
Posted on 2003-09-16 17:05:13 by NaN
Ok. I finally found the example in your source:
iftab	label dword

dd offset IID_IUnknown, 0
dd offset IID_IDispatch, CComExeSvr2._IDispatch
dd offset IID_IComExeSvr2, CComExeSvr2._IDispatch
dd offset IID_IConnectionPointContainer, CComExeSvr2._IConnectionPointContainer
;--------------------- insert new exposed interfaces here
iftabsize equ ($ - offset iftab) / 8


And the Class Object structure is defined as
CComExeSvr2	struct

_IDispatch IDispatch <>
_IConnectionPointContainer IConnectionPointContainer <>
dwRefCount DWORD ?
pTypeInfo LPTYPEINFO ?
pConnectionPoint LPCONNECTIONPOINT ?NUMCP dup (?)
dwValue DWORD ?
CComExeSvr2 ends


My problem here is that your "inherited" Interface is not a position 0 in the object. This means you can not take this object and call with early binding:


invoke vf( pObject, IConnectionPointContainer, EnumConnectionPoints), ppIEnumConnectionPoints


This is because (as i understand things) a COM Interface object calls itself by:
mov eax, [eax]

call eax+vtble_offset


What am i missing here??
:stupid:
Posted on 2003-09-19 10:23:03 by NaN
Posted on 2003-09-19 11:59:57 by NaN
Very good, NaN ;) ,

as already mentioned, thats the same approach as VC implements it. I compiled a C++ source file implementing muliple inheritance with virtual base classes (=interfaces) and studied the ASM file generated.

Japheth
Posted on 2003-09-20 00:58:21 by japheth
Japheth,

Theory asside, in actual implimentation im hitting the wall registering the object. I have it registers without a problem, but when viewed from comview the TypeLib is not available.

I suspect the typelib is not being found in the resource file. However, im puzzled where in your framework you reference the typelib in the resource file? I have it indicated as ID 201. And i know your framework doesnt have this ID anywhere.

Its something I assumed, and overlooked. How does it get referenced. I didnt find any direct "LoadResource" in the source and assume its somehow related with GetRegTypeLib (or whatever the api is ~ im at work at the moment).

If you can help me out here it would be appreciated. Im pretty sure this is the last snag im on!
:alright:
NaN
Posted on 2003-09-25 13:45:49 by NaN
Ok.. got kinda past this... Turns out that RadASM wasnt including the resource obj file with the link :rolleyes:

Now i got it registering, and with ComView i can see the type lib, as well as the registered class etc.

When i unregister it it get this error:
Posted on 2003-09-25 18:33:43 by NaN
I was so frustrated trying to chase this one down that I even directly cut and pasted your source in, with no solution.

I dont get it. Im running out of "leads" to follow. I do know that comview returns an error when i try to instanciate this, but thats different and running different functions in the DLL. For now im trying to simply get it registering and unregistering properly.

I do know that the registry is not being corrupted, and the proper keys are both registered and removed (After all, it is your source ;) )

I thought it was the tlb file, but it looks correct to me. The only Clue i get is the top line of the error box shown above "DllUnregisterServer in DllInstall failed.". When i run the register function it says "RegisterServer in JAYMESON.DLL Successful". I though this was odd, so i compared it to your example SIMPLESERVER.DLL. I dont get the error message, but it does say the correct name with its success message. I dont know what this means, if anything.

Anywho, here is your source, reworked for a single file compile:


    [*]COMDll.asm is the main DLL entry stuff
    [*]CClassFactory is what you expect
    [*]COMHelp.asm is your utility functions and ClassObject (your ObjectEntry) structure ~ didnt like the name
    [*]CJaymeson.asm is the unique stuf that makes each COM object different. Here i define the ObjectMap, Registry stuff and specific methods.


    I hope your experience might be able to spot this bug, cause mine is failing.... :rolleyes:

    Thanks,
    :NaN:
Posted on 2003-09-25 18:42:46 by NaN
Hi NaN,

there's a rather sophisticated bug in the source:

heres the original ObjectEntry structure


ObjectEntry struct
pClsId REFGUID ?
pLibId REFGUID ?
dwVerMajor SWORD ?
dwVerMinor SWORD ?
pRegKeys LPREGSTRUCT ?
constructor LPCONSTRUCTOR ?
ObjectEntry ends


and heres your modified structure



ClassObject STRUC
pClsId dd ? ; Class GUID pointer
pLibId dd ? ; Typelib GUID pointer
dwVerMajor dw ? ; Major Version
dwVerMinor dw ? ; Minor Version
pRegKeys dd ? ; Registry Keys pointer
constructor dd ? ; Class Constructor pointer
ClassObject ENDS


what basically has changed is type of members dwVerMajor and dwVerMinor

now look at this code:



;------------------------------ Unregister Type Library and Interfaces
invoke UnRegisterTypeLib, [edi].ClassObject.pLibId,\
[edi].ClassObject.dwVerMajor, [edi].ClassObject.dwVerMinor, 0, SYS_WIN32


Its not modified, but it doesnt work any more. Thats because of a well known (;) ) bug in MASM, which pushes WORD values in wrong size, but has no problems pushing signed WORD values. Thats the only reason why I had defined it as SWORD. Should have documented that, thou.

Japheth
Posted on 2003-09-26 01:52:51 by japheth
Thanks, i was aware that when you push WORDS, the stack ends up with DWORDS in 32 bit mode, but i wasnt aware that this API was truely expecting a 16 bit value. Thanks for spotting this! Very much apprechiated!

As a return, i noticed that your source has a potential bug in it:
SimpleServer.asm, function DllUnregisterServer, line 358:
invoke StringFromGUID2, [edi].ObjectEntry.pClsId, addr wszGUID,40
Should be:
invoke StringFromGUID2, [edi].ObjectEntry.pClsId, addr wszGUID,[b]sizeof wszGUID[/b]


Since the buffer is 40 WORDS not BYTES. You wouldnt have noticed this i figure until you unregister something deep in a directory structure....

The same applies to:
Line 268, 272 of the same file in the RegisterServer routine.


Thanks again!
:NaN:
Posted on 2003-09-26 17:44:39 by NaN
All seems to be going well... Except two things (both are non critical):

1) For some reason my object appears in you comview with a " 0" after it. I checked the IDL and there is no spelling errors that I can find. I aslo check the registry entries with your ComView and again no spelling mistakes... Im not sure how its doing this. (see picture below)

2) Im testing with VB5 now and I get it working with the following code:
Private Sub Command1_Click()

Dim A As Object
Set A = CreateObject("_JaymesonServerASM.1")
A.MyMethod 1, 2
Set A = Nothing
End Sub

However, I thought VB would automatically parse the typelib for the method/property names and arguments in its tool tip. Im findings this is not the case. Im not a VB programmer, so i may simply be forgetting to turn on this feature (somehow). If not, is this the difference between Dispatchable objects and OCX's??

Thankx for your help.
Posted on 2003-09-26 22:13:59 by NaN
Hi NaN,


1) For some reason my object appears in you comview with a " 0" after it. I checked the IDL and there is no spelling errors that I can find. I aslo check the registry entries with your ComView and again no spelling mistakes... Im not sure how its doing this. (see picture below)


Comview displays the "default" value of an IDispatch object - if any - in window caption. This is the property with dispid 0.


However, I thought VB would automatically parse the typelib for the method/property names and arguments in its tool tip. Im findings this is not the case. Im not a VB programmer, so i may simply be forgetting to turn on this feature (somehow). If not, is this the difference between Dispatchable objects and OCX's??


Try this:


Dim obj1 As New JAYMESON.JAYMESON
Private Sub Form_Load()
obj1.MyProp = 1
End Sub



As a return, i noticed that your source has a potential bug in it:
SimpleServer.asm, function DllUnregisterServer, line 358:
code:--------------------------------------------------------------------------------invoke StringFromGUID2, .ObjectEntry.pClsId, addr wszGUID,40--------------------------------------------------------------------------------
Should be:
code:--------------------------------------------------------------------------------invoke StringFromGUID2, .ObjectEntry.pClsId, addr wszGUID,sizeof wszGUID--------------------------------------------------------------------------------


Dont think this is a bug. function transfers a GUID (which is always 16 bytes) to a string (which in no case can be > 40. Besides that, are you sure the third parameter should be size in bytes? It should be size in characters, so best is possibly "LENGTHOF wszGUID"

Japheth
Posted on 2003-09-27 01:20:41 by japheth
Thanks again Japheth!

You've been extremely helpful! I will chase down this last bug and i think i will be finished (the dispatch name thingy).

All along i have been fixing up Extremes early attempt at a Com object generator. Im nearly finished it too. I revised the created files it outputs to utilize your COMVeiw /B option. As well it builds a default RADAsm project for the generated files. Working pretty good actually... I just need to get the output data right ;)

:NaN:
Posted on 2003-09-27 07:22:38 by NaN
A code generator for COM objects in ASM is very interesting, NaN. Xtreme's version hasnt interested me because of its use of colib, which I personally dont appreciate too much.

Japheth
Posted on 2003-09-27 09:56:39 by japheth
With no offense to Ernie, I seriously appreciate the level of work he put into co-lib (studied it up before your model), however, I've personally COM to agree with your version as i find it simpler to understand in general and work with. A black box lib with specific dependancies is not what i like. Your solution alows people to change nutz & bolts if they need to at any point.

As well your version is more direct for new people to get aquainted with. This is another hope from the generator... to get more people interested in the COM topic.

I have 100% removed any co-lib dependancies and made it 100% alike to your. Actually the "JAYMESON.DLL" is the output generated by the tool. (Im very unimaginative with testing names ;) )

Will have it out soon...
:alright:
NaN
Posted on 2003-09-27 21:24:37 by NaN