10

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!

Symbiosoft
  • 4,681
  • 6
  • 32
  • 46

6 Answers6

23

In general, you should prefer CoCreateInstance or CoGetClassObject rather than accessing DllGetClassObject directly. But if you're dealing with a DLL that you can't, or don't want to, register, then the below describes (part of) what these function do behind the scenes.


Given a CLSID, DllGetClassObject 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!

(CoCreateInstance performs steps 1 and 2. CoGetClassObject performs step 1. You would use CoGetClassObject if you need to create many instances of the same class, so that step 1 doesn't need to be repeated each time.)

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 2
    +1 for poker forum vernacular seeping in to other realms of geekdom. – John Dibling Feb 02 '10 at 21:02
  • 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 Feb 03 '10 at 15:29
  • @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. – C. K. Young Feb 03 '10 at 17:21
  • This is a dangerous thing to do with random 3rd party code: if the target COM object is using a different threading model than your own threads, bad things could happen (multiple threads executing code written for just one). If you're lucky, it'll crash; if unlucky, you'll get corrupt data. CoCreate does extra work behind the scenes to match threading models. Only do this if it's your code or you are absolutely sure of the threading model. (COM also takes care of unloading the library at the appropriate time; though at least in that case you can just never unload to avoid the issue there.) – BrendanMcK Nov 29 '11 at 09:38
  • @BrendanMcK: Indeed, I 100% agree with your points, and I would not, in general, think of instantiating COM objects via any means other than `CoCreateInstance` or the like. My post was written to directly address the OP's question, which is how to use low-level means to access a COM DLL, such as if dealing with a DLL that isn't registered (and doesn't self-register). – C. K. Young Nov 29 '11 at 17:35
  • 1
    @ChrisJester-Young; the trick with this qu is it's not clear if OP actually wants to do that - or is just new to COM, wants to use a COM-based DLL, and mentions LoadLibrary *only* because that's all they are familiar with having used plain Win32 DLLs before. Adding a suitable disclaimer at the top of the answer ("Use CoCreate, but if you can't, eg because you need to X, then...") is often the safest approach to use, especially if the answer is something you wouldn't usually do yourself; otherwise the OP or a casual reader who found this via search might mistake it for a recommended approach. – BrendanMcK Nov 29 '11 at 21:14
  • 1
    @BrendanMcK: Good point. I've added such a disclaimer, along with some explanations of what the recommended functions actually do (minus the thread model checking stuff). – C. K. Young Nov 30 '11 at 21:01
  • 1
    Thanks, this is really helpful as I wanted a way to debug a shell extension (IThumbnailProvider) without having to reregister the dll after every compile change. – Andrew Wyatt Jul 26 '15 at 07:45
15

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();

EDIT:

When I said above that:

Windows "knows" about the types that a COM DLL implements [...and] what DLL they are implemented in

...I really glossed over exactly how Windows knows this. It's not magic, although it might seem a little occult-ish at first.

COM libraries come with Type Libraries, which list the Interfaces and CoClasses that the library provides. This Type Library is in the form of a file on your hard drive -- very often it is embedded directly in the same DLL or EXE as the library itself. Windows knows where to find the Type Library and the COM Library itself by looking in the Windows Registry. Entries in the Registry tell Windows where on the hard drive the DLL is located.

When you call CoCreateInstance, Windows looks the clsid up in the Windows Registry, finds the corresponding DLL, loads it, and executes the proper code in the DLL that implements the COM object.

How does this information get in to the Windows Registry? When a COM DLL is installed, it is registered. This is typically done by running regsvr32.exe, which in turn loads your DLL in to memory and calls a function named DllRegisterServer. That function, implemented in your COM server, adds the necesarry information to the Registry. If you are using ATL or another COM framework, this is probably being done under the hood so that you don't have to interface with the Registry directly. DllRegisterServer only needs to be called once, at install-time.

If you try to call CoCreateInstance for a COM object that has not yet been registered via the regsvr32/DllRegisterServer process, then CoCreateInstance will fail with an error that says:

Class Not Registered

Fortunately, the fix for this is to simply call regsvr32 on your COM server, and then try again.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • +1, this is the right answer. COM may also do extra work behind the scenes to ensure that threading models match: so an app written to use multiple threads can safely CoCreate a component that assumes a single thread, and vice versa. There's a bunch of other things COM may also do, depending on how the component is registered, but this is perhaps the most important one to be aware of. Long story short, just use CoCreateInstance; if you attempt to roll your own and you're not a COM expert, there's a good chance you'll end up with a subtle bug somewhere. – BrendanMcK Nov 29 '11 at 09:42
  • 2
    COM DLL registration is not automatic, and if this is a home-grown DLL that step might have been missed. In that case CoCreateInstance would fail. You should touch on that in the answer somewhere. – Mark Ransom Nov 30 '11 at 21:14
  • @Mark: Good suggestion, edited. You really dug up an oldie, here. :) – John Dibling Nov 30 '11 at 21:39
  • I didn't dig it up, some activity by a clueless newbie brought it back to the Active list. Nice summary on registering a DLL. – Mark Ransom Nov 30 '11 at 21:59
7

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.
  • 1,719
  • 2
  • 14
  • 25
3

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
  • 11,211
  • 2
  • 27
  • 38
2

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, "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;
}
BrendanMcK
  • 14,252
  • 45
  • 54
John Knoeller
  • 33,512
  • 4
  • 61
  • 92
  • 2
    You might want to clarify when you'd actually use this: seems that using CoCreateInstance should be the usual way to go, and you'd only use this technique as a last resort - if the class was not registered, for example - and you're *sure* that the object uses a threading model that's compatible with the caller. This is potentially a dangerous answer for a newcomer to COM to read without context, as they might incorrectly assume that this approach is generally preferable to using CoCreateInstance(). – BrendanMcK Nov 29 '11 at 09:50
0

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?

Greg
  • 751
  • 1
  • 10
  • 11