Ok, I have been happily coding for hours in COM.. (it was surprisingly ez to get going at with some examples, and C++ code.. Anywho, Im hacking out MSAGENT com controls.. I have only gotten as far as CoInitailize the IAgentEx control.. My problem is i need to load a character from here. The method requires a VARIANT type to hold the file address of the character when calling, so i made a special proto for the Interface to handle this varient type. I cant seem to create a BSTR to fill the variant type with. If anyone can help me with these wide strings it would be appreciated.. (I tried L.inc.. but it doesnt like D:\...) Here is my code thus far:
;
;  MS AGENT IN ASM
.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\ole32.inc
include \masm32\include\bstrlib.inc
include \masm32\include\oleaut32.inc
include \masm32\include\L.inc

include \masm32\com\include\oaidl.inc
include \masm32\com\include\shlobj.inc

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

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

comethodspProto      typedef proto :DWORD,:VARIANT,:DWORD,:DWORD
comethodsp           typedef ptr comethodspProto

  IAgentEx            STRUCT DWORD
       IAgentEx_Load           comethodsp  ?
       IAgentEx_Unload        comethod2 ?
       IAgentEx_Register      comethod3 ?
       IAgentEx_Unregister    comethod2 ?
       IAgentEx_GetCharacter comethod3 ?
 IAgentEx            ENDS

comment % 
       HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( 
            IAgent __RPC_FAR * This,
            /*  */ VARIANT vLoadKey,
            /*  */ long __RPC_FAR *pdwCharID,
            /*  */ long __RPC_FAR *pdwReqID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unload )( 
            IAgent __RPC_FAR * This,
            /*  */ long dwCharID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Register )( 
            IAgent __RPC_FAR * This,
            /*  */ IUnknown __RPC_FAR *punkNotifySink,
            /*  */ long __RPC_FAR *pdwSinkID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unregister )( 
            IAgent __RPC_FAR * This,
            /*  */ long dwSinkID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCharacter )( 
            IAgent __RPC_FAR * This,
            /*  */ long dwCharID,
            /*  */ IDispatch __RPC_FAR *__RPC_FAR *ppunkCharacter);
%

.data
          hInstance      dd 0
          hResult         dd 0
          pAgentEx      dd 0       
          ICharID         dd 0
          IRequestID   dd 0
          ; useage:       sztext   wchar   L()
          szTheChar    wchar  L()
          szAppName  db "MS AGENT IN ASM",0
          
         sCLSID_AgentServer   TEXTEQU      <{0D45FD2FCH, 5C6EH, 11D1H,  \
     {09EH, 0C1H, 000H, 0C0H, 04FH, 0D7H, 008H, 01FH}}>
      
          sIID_IAgentEx TEXTEQU <{048D12BA0H,5B77H,11d1H, \
{09EH,0C1H,000H,0C0H,04FH,0D7H,008H,01FH}}>

         CLSID_AgentServer     GUID       sCLSID_AgentServer
         IID_IAgentEx                GUID       sIID_IAgentEx
         
.code
start:
      invoke GetModuleHandle, NULL
      mov hInstance, eax

      invoke WinMain,hInstance,NULL,NULL,NULL
      invoke ExitProcess,eax

WinMain proc hInst     :DWORD,
             hPrevInst :DWORD,
             CmdLine   :DWORD,
             CmdShow   :DWORD


          LOCAL vPath   :VARIANT
          LOCAL p :DWORD
    
    invoke CoInitialize, NULL
    .IF SUCCEEDED 
        MakeMessage "CoInitialized!"
    .endif

    invoke CoCreateInstance, ADDR CLSID_AgentServer, NULL, 
                             CLSCTX_SERVER, 
                             ADDR IID_IAgentEx, ADDR pAgentEx
    mov hResult, eax
    tes
Posted on 2001-03-12 02:22:00 by NaN
Gee, havn't you been the busy one? ;-) Some points in order: 1) L can handle backslashes such as "D:\pathstuff", you just have to double them as in C: wszMyPath wchar L() 2) Your interface proto looks fine, except I'm not so sure the variant is passed directly, and not by reference. I've never seen anything that resolved to larger then a dword pushed like that. (Question: Does this mean VARIANTs are always passed by reference? I ran thru some literature, it did not seem to say so.) A very good way to check param size and count is put the coinvoke INSIDE a procedure. If the param count is wrong, this will result in a GPF due to an incorrect stack pointer (or if you use Dmacros, spy on esp with 'DPrintValD esp "the stack"' before and after the coinvoke) 3)BSTR's are a whole topic to themselves. A BSTR is a POINTER to a string structure. Never change this struct by yourself, always let the API's do it for you (although changing the words inside is OK, that's ALL you should do). BSTRs are always unicode. Basically, if you made it, you destroy it, unless you give it away byref:

wszTest wchar  L()
MyBstr  BSTR   0   ; defines a null bstr

invoke SysAllocString, wszTest
mov MyBstr, eax
coinvoke pSomething, ISomething, MyBstr  ; you still own the string
coinvoke pSomethingElse, ISomethingElse, ADDR MyBstr ; you just gave it away. 
                                                     ; You no longer own it
In a like fashion, and bstr's passed back to you as an byref you also own, and thus must destroy. A bstr passed in to you as an param must be byref, and any pre-existing string must be deleted, then you are free to change the orgional. The SysReAllocString API will do this for you. Think of it this way: If you just have the address of the string the bstr itself), leave it along. If you have the pointer to the address (ADDR bstr), you can change this pointer to another string and delete the orgional. (There is a nice tut in my MSDN on BSTRs. I found it by searching for "Rules of BSTR") Other then this, I'm out of ideas. This is not an interface I am familiar with, and the only times I've passed (or received) VARIANTS has been byref.
Posted on 2001-03-12 10:10:00 by Ernie
Thanx Ernie, I will keep hacking at it.. Im starting to doubt that my structure IS correct.. Q1) Can i leave out function memebers from the structure if i dont intend to use them? (i have only defined 5, but there are more.) Q2) Of this structure, is there an ordering preference that must be met? Q3) There is a hash-key im not familair with in the C++ header preceeding this structure.. could this be a source of error?? This is the Entire IAgentEx as defined in C++: (Look it over and see if it helps you understand the VARIANT situation.. :) )

EXTERN_C const IID IID_IAgentEx;

#if defined(__cplusplus) && !defined(CINTERFACE)

    interface DECLSPEC_UUID("48D12BA0-5B77-11d1-9EC1-00C04FD7081F")
    IAgentEx : public IAgent
    {

    public:
        virtual HRESULT STDMETHODCALLTYPE GetCharacterEx( 
            /*  */ long dwCharID,
            /*  */ IAgentCharacterEx __RPC_FAR *__RPC_FAR *ppCharacterEx) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetVersion( 
            /*  */ short __RPC_FAR *psMajor,
            /*  */ short __RPC_FAR *psMinor) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE ShowDefaultCharacterProperties( 
            /*  */ short x,
            /*  */ short y,
            /*  */ long bUseDefaultPosition) = 0;
        
    };
    
#else 	/* C style interface */

    typedef struct IAgentExVtbl
    {
        BEGIN_INTERFACE
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( 
            IAgentEx __RPC_FAR * This,
            /*  */ REFIID riid,
            /*  */ void __RPC_FAR *__RPC_FAR *ppvObject);
        
        ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( 
            IAgentEx __RPC_FAR * This);
        
        ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( 
            IAgentEx __RPC_FAR * This);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( 
            IAgentEx __RPC_FAR * This,
            /*  */ UINT __RPC_FAR *pctinfo);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( 
            IAgentEx __RPC_FAR * This,
            /*  */ UINT iTInfo,
            /*  */ LCID lcid,
            /*  */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( 
            IAgentEx __RPC_FAR * This,
            /*  */ REFIID riid,
            /*  */ DISPID __RPC_FAR *rgDispId);
        
        /*  */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( 
            IAgentEx __RPC_FAR * This,
            /*  */ DISPID dispIdMember,
            /*  */ REFIID riid,
            /*  */ LCID lcid,
            /*  */ WORD wFlags,
            /*  */ DISPPARAMS __RPC_FAR *pDispParams,
            /*  */ VARIANT __RPC_FAR *pVarResult,
            /*  */ EXCEPINFO __RPC_FAR *pExcepInfo,
            /*  */ UINT __RPC_FAR *puArgErr);


        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( 
            IAgentEx __RPC_FAR * This,

            /*  */ VARIANT vLoadKey,
            /*  */ long __RPC_FAR *pdwCharID,
            /*  */ long __RPC_FAR *pdwReqID);


        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unload )( 
            IAgentEx __RPC_FAR * This,
            /*  */ long dwCharID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Register )( 
            IAgentEx __RPC_FAR * This,
            /*  */ IUnknown __RPC_FAR *punkNotifySink,
            /*  */ long __RPC_FAR *pdwSinkID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unregister )( 
            IAgentEx __RPC_FAR * This,
            /*  */ long dwSinkID);
        
        HRESULT ( STDMETHODCALLTYPE __RPC_FAR
Posted on 2001-03-12 10:38:00 by NaN
/* */ VARIANT vLoadKey, (This)->lpVtbl -> Load(This,vLoadKey,pdwCharID,pdwReqID) Either way, looks like BYVAL and not BYREF. IE, you push the whole variant, not ADDR variant. You only need to define the interface members up to the highest one you use... if say it has 101 methods, but you only use up to 12, just defining those first 12 (in order) is A-OK. Ahhh, a light slowly dawned on me. IAgentEx is a Dispinterface! That means it has the 3 IUnknown and 4 IDispatch functions first at the start! So it would look like so:

IAgentEx_LoadProto      typedef proto :DWORD,:VARIANT,:DWORD,:DWORD
IAgentEx_LoadPtr           typedef ptr IAgentEx_LoadProto

_vtIAgentEx   MACRO CastName:REQ
    ; IDispatch methods first
    _vtIDispatch CastName   ; this also defines IUnknown, 7 methods total
    ; now the IAgent methods you defined
    &CastName&_Load          IAgentEx_LoadPtr  ?
    &CastName&_Unload        comethod2 ?
    &CastName&_Register      comethod3 ?
    &CastName&_Unregister    comethod2 ?
    &CastName&_GetCharacter comethod3 ?
    ; can add other methods here later
ENDM

IAgentEx   STRUCT
    _vtIAgentEx IAgentEx
IAgentEx   ENDS
I hope you can read my interface macro ;-) About the hash key, I'm not sure what you meant. Was that this: interface DECLSPEC_UUID("48D12BA0-5B77-11d1-9EC1-00C04FD7081F") That's just the interface IID GUID. (I think you converted those GUIDs by hand... check the MASM32 SP2, there is a QuickEditor plug-in to do that for you in there)
Posted on 2001-03-12 12:39:00 by Ernie
Ernie, you did it again!.. Thanx.. (I have gotten it working now..) It was the BSTR issues.. I discovered that LOADSTRING from a resouce file turned the data back into ansii.. A bit of a surprise because the MSDN gives indication it can suport this. My resouce was : 5051, L"needed stuff" and the data came back as normal chars.. I have to use MultiByteToWideChar to make it back to Unicode, and then pass it though SysAllocString to make it a BSTR.. ( Im definitely thinking of a macro set here.. :) Anywho, thanx for your advice and help.. My first little project with COMs was actually easier and more interesting than i thought it would be. (why i was putting it off in the first place.) Im going to clean up my code and make a little tutorial outa it for others in the next week or so. Oh BTW, when I defined my IAgentCharacter interface structure, i ran across a few functions with 'short' in their parameters... Seeing this is 32bit, i assumed everything is a DWORD regardless.. is this correct? I havent tested these functions yet as they are a bit more obscure... but here is an example:

HRESULT STDMETHODCALLTYPE IAgentEx_GetVersion_Proxy( 
    IAgentEx __RPC_FAR * This,
    /*  */ short __RPC_FAR *psMajor,
    /*  */ short __RPC_FAR *psMinor);
NaN
Posted on 2001-03-12 16:01:00 by NaN
Yep, Windows 9x has abysmal unicode support. MultiByteToWideChar and the inverse really get overused. I dislike these functions so much, I put em in macro wrappers so I don't have to look at them (macros at the end of bstrLib) In my masm book, a C short is defined as a SWORD, so a custom (not a comethod) proto will be needed. Sorry I didn't notice where you got your string, I knew LoadString only returns ASCII. (rc.exe BUILDS in unicode, that's what that mysterious "Using codepage xxxx as default" means (it's your system's default language). It's good to hear things are working. Keep up the good work! I wanna see yout tut.
Posted on 2001-03-12 17:45:00 by Ernie
I have to say, after going through all the translations, when i look back at the ASM version verses the C++ version, the ASM is conscise and to the point! There is soooo much convolution in that C++ crap.. (why i pulled a homer on that 'hash-key' comment, my head was spinning while wading thru all that crap and didnt clue in at all.. ) w32asm sometimes is a bit more keystrokes, but it makes up in time spent trying to read source code... NaN
Posted on 2001-03-12 20:23:00 by NaN