views:

507

answers:

6

First, COM is like black magic for me. But I need to use COM dll in one project I'm working on.

So, I have a DLL I am developing and I need some functionalities that are available in a separate COM DLL. When I look to the COM DLL with Depends.exe I see methods like DllGetClassObject() and other functions but none of the functions I'm interested in.

I have access to the COM DLL (legacy) source code but it's a mess and I'd rather like to use the COM DLL in binary like a big black box not knowing what's going on inside.

So, how can I call the COM DLL functions from my code using LoadLibrary? Is it possible? If, yes, could you give me an example of how to do it?

I'm using Visual Studio 6 for this project.

Thanks a lot!

+9  A: 

Actually, DllGetClassObject is a function you're interested in. Given a CLSID, this allows you to get the class object, from which you can create instances (via the IClassFactory interface, if I remember correctly).

Summary of steps (it's been a while since I've last touched COM, so pardon any obvious errors):

  1. Call DllGetClassObject(clsid, IID_IClassFactory, &cf), where clsid is the CLSID you want to get the class object for, and cf is of course the class factory.
  2. Call cf->CreateInstance(0, iid, &obj), where iid is the IID of the interface you'd like to use, and obj is of course the object.
  3. ???
  4. Profit!
Chris Jester-Young
+1 for poker forum vernacular seeping in to other realms of geekdom.
John Dibling
Is there a reason that you recommend calling the exported `DllGetClassObject()` directly instead of using the COM function `CoGetClassObject()`? It seems like an... unconventional approach to me.
Phil Booth
@Phil: No, I don't recommend it per se; I was just answering the OP's question. But, I think there are occasional uses for it, like when you're wanting to test an internal development COM component that you have multiple versions of, that you don't want to reregister all the time.
Chris Jester-Young
A: 

If it's a COM DLL, all you need do is add it as a reference to your project, and then you can call the functions that are within the DLL.

Yes, you can use the low level COM functions like DLLGetClassObject, but why would you?

Gregor S.
One upside to doing manual loading is that your DLL doesn't have to be registered to be usable.
Chris Jester-Young
Why would you have a COM DLL that isn't registered? That seems HIGHLY unlikely...
Gregor S.
On XP and later you can use manifest files to access an unregistered COM DLL
Nemanja Trifunovic
@Gregor S: Registered COM objects can be used by your competitors. A lot of companies think that's a bad thing.
John Knoeller
A: 

Here's a bit of code showing how to get the class factory and use it to create a COM object. It uses a struct to keep track of the module handle and DllGetClassObject function pointer. You should hold on to the module handle until you are done with the COM object.

To use this function, you need to allocate an instance of the ComModuleInfo struct and set the szDLL to the DLL filename or full path name. Then call the function with the class id and interface Id of the COM object you want to get from that DLL.

typedef struct {
   TCHAR   szDLL[MAX_PATH];
   HMODULE hModule;
   HRESULT (WINAPI *pfnGetFactory)(REFCLSID, REFIID, void**);
   } ComModuleInfo;

HRESULT CreateCOMObject(
   ComModuleInfo & mod,  // [in,out] 
   REFCLSID iidClass,    // [in] CLSID of the COM object to create
   REFIID iidInterface,  // [in] GUID of the interface to get
   LPVOID FAR* ppIface)  // [in] on success, interface to the COM object is returned
{
    HRESULT hr = S_OK;

    *ppIface = NULL; // in case we fail, make sure we return a null interface.

    // init the ComModuleInfo if this is the first time we have seen it.
    //
    if ( ! mod.pfnGetFactory)
    {     
       if ( ! mod.hModule)
       {
          mod.hModule = LoadLibrary(mod.szDLL);
          if ( ! mod.hModule)
             return HRESULT_FROM_WIN32(GetLastError());
       }
       mod.pfnGetFactory = (HRESULT (WINAPI *)(REFCLSID, REFIID, void**))GetProcAddress(mod.hModule, TEXT("DllGetClassObject"));
       if ( ! mod.pfnGetFactory)
          return HRESULT_FROM_WIN32(GetLastError());
    }

    IClassFactory* pFactory = NULL;
    hr = mod.pfnGetFactory(iidClass, IID_IClassFactory, (void**)&pFactory);
    if (SUCCEEDED(hr))
    {
       hr = pFactory->CreateInstance(NULL, iidInterface, (void**)ppIface);
       pFactory->Release();
    }

    return hr;
}
John Knoeller
+3  A: 

Typically you would use CoCreateInstance() to instantiate an object from a COM DLL. When you do this, there's no need to load the DLL first and get proc addresses like you would need to do with a normal DLL. This is because Windows "knows" about the types that a COM DLL implements, what DLL they are implemented in, and how to instantiate them. (Assuming of course that the COM DLL is registered, which it typically is).

Suppose you have a COM DLL with the IDog interface you want to use. In that case,

dog.idl

interface IDog : IUnknown
{
  HRESULT Bark();
};

coclass Dog
{
  [default] Interface IDog;
};

myCode.cpp

IDog* piDog = 0;
CoCreateInstance(CLSID_DOG, 0,  CLSCTX_INPROC_SERVER, IID_IDOG,  &piDog); // windows will instantiate the IDog object and place the pointer to it in piDog
piDog->Bark();  // do stuff
piDog->Release();  // were done with it now
piDog = 0;  // no need to delete it -- COM objects generally delete themselves

All this memory management stuff can get pretty grungy, though, and the ATL provides smart pointers that make the task of instantiating & managing these objects a little easier:

CComPtr<IDog> dog;
dog.CoCreateInstance(CLSID_DOG);
dog->Bark();
John Dibling
+3  A: 

You do not directly use LoadLibrary() with a COM library. CoCreateInstance() will call this function if it's not already, and then new an instance of the class you implemented in the library onto the heap and finally return to you a raw pointer to that object. Of course, it could fail during the process, and thus some mechanism for you to check the status like HRESULT.

For simplicity of using it, you can think of a COM library as a common DLL with 1) some predefined entry(main) function, 2) you have to call some predefined function like CoCreateInstance() to enter it, and accept that it's like that because it has to.

t.g.
I like your explanation of COM.
Gravitas
+2  A: 

If the type library is embedded in the DLL you can import it into your project:

#import "whatever.dll"

This will auto-generate header files that get included in your project and allow you to use the exported objects.

Luke