IT-Consultant: Frederick J. Harris > Fred's COM (Component Object Model) Tutorials

COM Tutorial #1: COM Object Memory

<< < (2/9) > >>

James C. Fuller:
  Just a heads up.
  I used Borland's free c++ compiler 5.5 with RADasm and the client1 cpp code worked flawlessly.


Frederick J. Harris:
Hi James

     Thanks for the feedback.  I have a good bit of code written to create procedure declarations for C/C++.  I was doing it just to learn the whole TypeLib territory working with the interfaces, etc.  I figured there probably were a multitude of tools out there to do it from the C/C++ angle.

     When I get time I'm going to explore further the difficulties I was having with the GNU compilers.  I really see no reason why they won't work.  Actually, they sometimes do work creating the COM object.  But I have to work on 'real' work right now!

     Anyway, that's something I'd like to work on sometime if I have a little time, i.e., a TypeBib Browser with a radio button that produces either PB or C++ includes.

James C. Fuller:
  I know you mentioned covering this in Part 2 but would it be possible to get the c++ code to load
 an unregistered com object (using your CA.DLL example) from a dll? My c++ knowledge is very poor at best.

This is the José PB code I used before the functionality was added to the compiler.

Thank You,


--- Code: ---' ========================================================================================
' Loads the specified library and creates an instance of an object.
' If it succeeds, returns a reference to the requested interface; otherwise, it returns null.
' Note: Do not use FreeLibrary to unload the library once you have got a valid reference
' to an interface or your application will GPF. Before calling FreeLibrary, all the
' interface references must be released. If you don't need to unload the library until
' the application ends, then you don't need to call FreeLibrary because PB calls
' CoUninitialize under the hood, that closes the COM library on the current thread,
' unloads all DLLs loaded by the thread, frees any other resources that the thread
' maintains, and forces all RPC connections on the thread to close.
' ========================================================================================
FUNCTION CreateInstanceFromDll ( _
   BYVAL strLibName AS STRING _               ' // [in] Library name and path
 , BYREF rclsid AS GUID _                     ' // [in] CLSID that will associate the correct data and code
 , BYREF riid AS GUID _                       ' // [in] IID of the interface to be used to communicate with the newly created objec
 , OPTIONAL BYVAL strLicKey AS STRING _       ' // [in] License key (ansi string)
 ) AS IUnknown                                ' // [out] The interface pointer requested in riid

   LOCAL hr AS LONG                           ' // HRESULT
   LOCAL hLib AS DWORD                        ' // Library handle
   LOCAL pProc AS DWORD                       ' // Procedure address
   LOCAL pIClassFactory AS IClassFactory      ' // IClassFactory interface
   LOCAL pIClassFactory2 AS IClassFactory2    ' // IClassFactory2 interface
   LOCAL ppv AS IUnknown                      ' // The requested interface pointer

   ' // See if the library is already loaded in the address space
   hLib = GetModuleHandle(BYCOPY strLibName)
   ' // If it is not loaded, load it
   IF hLib = %NULL THEN hLib = LoadLibrary(BYCOPY strLibName)
   ' // If it fails, abort

   ' // Retrieve the address of the exported function DllGetClassObject
   pProc = GetProcAddress(hLib, "DllGetClassObject")
      FreeLibrary hLib

   IF LEN(strLicKey) = 0 THEN
      ' // Request a reference to the IClassFactory interface
      CALL DWORD pProc USING DllGetClassObject(rclsid, $IID_IClassFactory, pIClassFactory) TO hr
      IF hr <> %S_OK THEN
         FreeLibrary hLib
      END IF
      ' // Create an instance of the server or control
      hr = pIClassFactory.CreateInstance(%NULL, riid, BYVAL VARPTR(ppv))
      ' // Request a reference to the IClassFactory2 interface
      CALL DWORD pProc USING DllGetClassObject(rclsid, $IID_IClassFactory2, pIClassFactory2) TO hr
      IF hr <> %S_OK THEN
         FreeLibrary hLib
      END IF
      ' // Create a licensed instance of the server or control
      strLicKey = UCODE$(strLicKey)
      hr = pIClassFactory2.CreateInstanceLic(NOTHING, NOTHING, riid, strLicKey, ppv)

   FUNCTION = ppv


--- End code ---

Frederick J. Harris:
My confession: Never even tried it!  Just assummed it would work.  Just banged this out and luckily it does!

(you need to change the path to where you have CA.dll in LoadLibrary() call at top)

--- Code: ---//CAClient3.cpp
#include <windows.h> //CAClient
#include <stdio.h>   //IID_IClassFactory={00000001-0000-0000-C000-000000000046};
const CLSID CLSID_CA ={0x20000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04}}; //Class ID of Class CA, ie., Class A
const IID   IID_I_X  ={0x20000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05}}; //Interface X
const IID   IID_I_Y  ={0x20000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06}}; //Interface Y
const IID   IID_Junk ={0x12345678,0x9876,0x5432,{0x12,0x34,0x56,0x78,0x98,0x76,0x54,0x32}}; //Junk Number So QueryInterface() Fails

HRESULT (__stdcall* ptrDllGetClassObject) (const CLSID&, const IID&, void**);
HRESULT (__stdcall* ptrQueryInterface)    (int, const IID&, void**);
ULONG   (__stdcall* ptrAddRef)            (int);
ULONG   (__stdcall* ptrRelease)           (int);
void    (__stdcall* pIFn)                 (int,int);

interface I_X : IUnknown
 virtual HRESULT __stdcall Fx1(int)=0;
 virtual HRESULT __stdcall Fx2(int)=0;

interface I_Y : IUnknown
 virtual HRESULT __stdcall Fy1(int)=0;
 virtual HRESULT __stdcall Fy2(int)=0;

int main(void)
 IClassFactory*  pCF=NULL;
 HMODULE         hDll=NULL;
 BOOL blnFree    =FALSE;
 IUnknown*       pIUnk=NULL;
 unsigned int*   pVTbl=0;
 unsigned int*   VTbl=0;
 HRESULT         hr;

    ptrDllGetClassObject=(HRESULT (__stdcall*)(REFCLSID, REFIID, void**))GetProcAddress(hDll,"DllGetClassObject");
    printf("ptrDllGetClassObject = %u\n",ptrDllGetClassObject);
       printf("pVTbl   = %u\n",pVTbl);
       printf("&pVTbl[i]\t&VTbl[i]\tVTbl[i]\t\tFunction Call Through Pointer\n");
       for(unsigned int i=0;i<=1;i++)
           VTbl=(unsigned int*)pVTbl[i];                                             //Call...
           ptrQueryInterface=(HRESULT(__stdcall*)(int, const IID&, void**)) VTbl[0];
           ptrQueryInterface((int)&pVTbl[i],IID_Junk,(void**)&pIUnk);                //QueryInterface()
           ptrAddRef=(ULONG(__stdcall*)(int)) VTbl[1];
           ptrAddRef((int)&pVTbl[i]);                                                //AddRef()
           ptrRelease=(ULONG(__stdcall*)(int)) VTbl[2];
           ptrRelease((int)&pVTbl[i]);                                               //Release()
           pIFn=(void(__stdcall*)(int,int)) VTbl[3];
           pIFn((int)&pVTbl[i],i);                                                   //Fx1() / Fy1()   
           pIFn=(void(__stdcall*)(int,int)) VTbl[4];
           pIFn((int)&pVTbl[i],i);                                                   //Fx2() / Fy2()   
       ptrRelease=(ULONG(__stdcall*)(int)) VTbl[2];
 return 0;

--- End code ---

Let me know if that works Jim!  You had mentioned you had ver 5.5 of Borland's C++ product.  I actually have the standard edition version 5.  I almost never used it, as right after that I obtained Microsoft's high octane version through my employer, and I'm still using that. 

I've had a love/hate relationship with C++ for years.  After I struggled teaching myself C way back in the nineties I was looking forward to learning C++.  When I started learning about classes and all that stuff I thought it was really neat.  Then I bumbed up hard against MFC and that stoped me dead in my tracks.  I truely hated it.  I just simply don't like class libraries that wrap the Windows Api.  At the time though I wasn't prescient enough to make the distinction between C++ and class libraries that wrap the Windows Api; all I knew was that I hated MFC and C++ was associated with it rather intimately, and I more or less threw out the baby with the bathwater.  As time went on though it dawned on me that the type of raw Windows Api programming I do with PowerBASIC I could do just as well in C++, the only real problem being the lack of dynamic string handling abilities in native unadorned C++.  Yes, C++ has string class libraries, but as far as I know you need to link with the horrible big and bloated MFC.dll to use them, and under no circumstances was I ever about to do that.  So eventually I got good enough with it to write my own string class, and I use that now in C++ apps.  So really, I've the best of both worlds, i.e., a super powerful OOP implementation, plus I can write small executables just like in PowerBASIC.

At work I pretty much have to use C++ for my data recorder work but I only use PowerBASIC for desktop work - not much C++ except for learning this COM stuff.

....back to the program though...most C++ folks (all C++ folks!?!) use typedefs to cast the return from GetProcAddress() to a usable function pointer.  I don't usually (I'll be honest - I never do it) do that because typedefs confuse me more than they help me.  The way I see the issue is that if I create a typedef it cleans up the point of call, but it introduces 'magic' in my programs.  If I don't use a typedef its ugly as sin at the point of call but at least there's no witchcraft involved! 

Frederick J. Harris:
I added a bunch of printf statements within the dll, recompiled it, then ran this PowerBASIC program that uses GetProcAddress() instead of COM services to load the dll.  It seems to work OK. 

--- Code: ---#Compile               Exe "CAClient3.exe"
#Dim                   All
#Include               ""
#Include               ""
$IID_IUnknown          =Guid$("{00000000-0000-0000-C000-000000000046}")  'Microsoft Defined - IUnknown
$IID_IClassFactory     =Guid$("{00000001-0000-0000-C000-000000000046}")  'Microsoft Defined - IClassFactory
$CLSID_CA              =Guid$("{20000000-0000-0000-0000-000000000004}")  'Class ID of Class CA, ie., Class A
$IID_IX                =Guid$("{20000000-0000-0000-0000-000000000005}")  'Interface X
$IID_IY                =Guid$("{20000000-0000-0000-0000-000000000006}")  'Interface Y
$IID_Junk              =Guid$("{12345678-9876-5432-1012-345678901234}")  'Junk Number So QueryInterface() Fails

Declare Function DllGetClassObject   (Byref Clsid As Guid, Byref iid As Guid, Byref pIUnknown As Any) As Long
Declare Function ptrCreateInstance   (Byval this As Dword, Byref pUnknown As Any, Byref iid As Guid, Byref pVTbl As Any) As Long
Declare Function ptrQueryInterface   (Byval this As Dword, Byref iid As Guid, Byref pUnknown As Any) As Long
Declare Function ptrAddRef           (Byval this As Dword) As Dword
Declare Function ptrRelease          (Byval this As Dword) As Dword
Declare Function ptrFn               (Byval this As Dword, ByVal iNum As Long) As Long

Interface I_X $IID_IX : Inherit IUnknown
  Method Fx1(ByVal iNum As Long) As Long
  Method Fx2(ByVal iNum As Long) As Long
End Interface

Interface I_Y $IID_IY : Inherit IUnknown
  Method Fy1(ByVal iNum As Long) As Long
  Method Fy2(ByVal iNum As Long) As Long
End Interface

Function PBMain() As Long
  Local pVTbl,VTbl,pCF,CFVTbl,pUnk,pDllProc As Dword Ptr
  Local pSerInfo As COSERVERINFO
  Local hResult As Long
  Local hDll As DWord
  Register i As Long

  hDll=LoadLibrary("C:\Code\VStudio\VC++6\Projects\COM\ComObjects\CA\Release\CA.dll")    'Change This Line!!!!
  If hDll Then
     Print "hDll     = " hDll
     If pDllProc Then
        Print "pDllProc = " pDllProc
        Call DWord pDllProc Using DllGetClassObject($CLSID_CA, $IID_IClassFactory, pCF) To hResult
        'hResult=CoGetClassObject($CLSID_CA, %CLSCTX_INPROC_SERVER, pSerInfo, $IID_IClassFactory, pCF)
        If SUCCEEDED(hResult) Then
           Print "Successfully Loaded CA.dll And Succeeded In DllGetClassObject() Call!"
           Print "pCF      = " pCF
           CFVTbl=@pCF[0]  '@CFVTbl[3] is the address of CAClassFactory::CreateInstance(...)
           Call Dword @CFVTbl[3] Using ptrCreateInstance(pCF,pUnk,$IID_IUnknown,pVTbl) To hResult
           If SUCCEEDED(hResult) Then  'If we get inside this If the class factory has alreadt created class CA
              Print "pCF->CreateInstance() Succeeded!"  'so we can do a pCF->Release().  Release() is in slot #2
              Call DWord @CFVTbl[2] Using ptrRelease(pCF) To hResult  'Release() CAClassFactory
              Print "pVTbl    = " pVTbl  'We now have the same VTbl Ptr ( pVTbl ) we originally got from
              Print                         'CoCreateInstance(), so go to town.....
              Print "Varptr(@pVTbl[i])  Varptr(@VTbl[i])  @VTbl[i]     Function Call With Call Dword"
              Print "==============================================================================="
              For i=0 To 1
                VTbl=@pVTbl[i]                                                                             'Call...
                Print LTrim$(Str$(Varptr(@pVTbl[i]))) Tab(19)Varptr(@VTbl[0]) Tab(37)@VTbl[0] "   ";
                Call DWord @VTbl[0] Using ptrQueryInterface(Varptr(@pVTbl[i]), $IID_Junk, pUnk) To hResult 'QueryInterface()
                Print LTrim$(Str$(Varptr(@pVTbl[i]))) Tab(19)Varptr(@VTbl[1]) Tab(37)@VTbl[1] "   ";
                Call DWord @VTbl[1] Using ptrAddRef(Varptr(@pVTbl[i])) To hResult                          'AddRef()
                Print LTrim$(Str$(Varptr(@pVTbl[i]))) Tab(19)Varptr(@VTbl[2]) Tab(37)@VTbl[2] "   ";
                Call DWord @VTbl[2] Using ptrRelease(Varptr(@pVTbl[i])) To hResult                         'Release()
                Print LTrim$(Str$(Varptr(@pVTbl[i]))) Tab(19)Varptr(@VTbl[3]) Tab(37)@VTbl[3] "   ";
                Call DWord @VTbl[3] Using ptrFn(Varptr(@pVTbl[i]),i) To hResult                            'Fx1() / Fy1()
                Print LTrim$(Str$(Varptr(@pVTbl[i]))) Tab(19)Varptr(@VTbl[4]) Tab(37)@VTbl[4] "   ";
                Call DWord @VTbl[4] Using ptrFn(Varptr(@pVTbl[i]),i) To hResult                            'Fy1() / Fy2()
              Next i
              Call DWord @VTbl[2] Using ptrRelease(pVTbl) To hResult
           End If
        End If
     End If
  End If

End Function

'hDll     =  268435456
'pDllProc =  268441664
'Entering DllGetClassObject()
'Called CAClassFactory Constructor!
'Called CAClassFactory::QueryInterface()
'Called CAClassFactory::AddRef()
'Leaving DllGetClassObject()
'Successfully Loaded CA.dll And Succeeded In DllGetClassObject() Call!
'pCF      =  8521248
'Called CAClassFactory::CreateInstance()
'this       = 8521216
'sizeof(CA) = 12
'Called CA::AddRef()
'pCF->CreateInstance() Succeeded!
'Called CAClassFactory::Release()
'Called CAClassFactory Destructor!
'pVTbl    =  8521216
'Varptr(@pVTbl[i])  Varptr(@VTbl[i])  @VTbl[i]     Function Call With Call Dword
'8521216            268464396         268439696    Called CA::QueryInterface()
'8521216            268464400         268439840    Called CA::AddRef()
'8521216            268464404         268439872    Called CA::Release()
'8521216            268464408         268439936    Called Fx1()  :  iNum = 0
'8521216            268464412         268439968    Called Fx2()  :  iNum = 0
'8521220            268464376         268440528    Called CA::QueryInterface()
'8521220            268464380         268440544    Called CA::AddRef()
'8521220            268464384         268440560    Called CA::Release()
'8521220            268464388         268440000    Called Fy1()  :  iNum = 1
'8521220            268464392         268440032    Called Fy2()  :  iNum = 1
'Called CA::Release()

--- End code ---


[0] Message Index

[#] Next page

[*] Previous page

Go to full version