如何在C ++中使用带有LoadLibrary的COM DLL

时间:2010-02-02 20:27:07

标签: c++ visual-c++ com dll loadlibrary

首先,COM对我来说就像是黑魔法。但是我需要在我正在开发的一个项目中使用COM dll。

所以,我有一个我正在开发的DLL,我需要一些单独的COM DLL中可用的功能。当我使用Depends.exe查看COM DLL时,我看到像DllGetClassObject()这样的方法和其他函数,但没有我感兴趣的函数。

我可以访问COM DLL(遗留)源代码,但它很乱,我更喜欢使用二进制中的COM DLL,就像一个不知道内部发生了什么的大黑盒子。

那么,如何使用LoadLibrary从我的代码中调用COM DLL函数?可能吗?如果,是的,你能给我一个如何做的例子吗?

我正在将Visual Studio 6用于此项目。

非常感谢!

6 个答案:

答案 0 :(得分:22)

一般情况下,您应该更喜欢CoCreateInstanceCoGetClassObject,而不是直接访问DllGetClassObject但是,如果您正在处理可以使用的DLL,那么t,或者不想,注册,然后下面描述(部分)这些功能在幕后做什么。


给定一个CLSID,DllGetClassObject允许你获取类对象,你可以从中创建实例(如果我没记错的话,通过IClassFactory接口)。

步骤摘要(自从我上次触摸COM以来已经有一段时间了,所以请原谅任何明显的错误):

  1. 调用DllGetClassObject(clsid, IID_IClassFactory, &cf),其中clsid是您要获取类对象的CLSID,cf当然是类工厂。
  2. 致电cf->CreateInstance(0, iid, &obj),其中iid是您要使用的界面的IID,obj当然是对象。
  3. ???
  4. 利润!
  5. CoCreateInstance执行步骤1和2. CoGetClassObject执行步骤1.如果您需要创建同一类的多个实例,则可以使用CoGetClassObject,以便步骤1不会每次都需要重复。)

答案 1 :(得分:11)

通常,您将使用CoCreateInstance()从COM DLL实例化对象。当你这样做时,不需要首先加载DLL并获得proc地址,就像你需要使用普通的DLL一样。这是因为Windows“知道”COM DLL实现的类型,它们实现了什么DLL,以及如何实例化它们。 (当然假设COM DLL已经注册,通常是这样)。

假设您有一个带有要使用的IDog接口的COM DLL。在那种情况下,

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

然而,所有这些内存管理的东西都会变得相当蹩脚,并且ATL提供了智能指针,可以实现实例化的任务。管理这些对象更容易:

CComPtr<IDog> dog;
dog.CoCreateInstance(CLSID_DOG);
dog->Bark();

编辑:

当我在上面说过:

  

Windows“知道”COM DLL实现的类型[...和]   它们在

中实现了什么DLL

......我真的很清楚Windows如何知道这一点。这不是魔术,虽然起初看起来有些神秘。

COM库附带了类型库,它列出了库提供的接口和CoClasses。此类型库采用硬盘驱动器上的文件形式 - 通常它直接嵌入到与库本身相同的DLL或EXE中。 Windows通过查看Windows注册表知道在哪里可以找到类型库和COM库本身。注册表中的条目告诉Windows DLL在硬盘驱动器上的位置。

当您调用CoCreateInstance时,Windows会在Windows注册表中查找clsid,找到相应的DLL,加载它,并在实现COM对象的DLL中执行正确的代码。

此信息如何进入Windows注册表?安装COM DLL时,它已注册。这通常是通过运行regsvr32.exe来完成的,{{3}}又将您的DLL加载到内存中并调用名为DllRegisterServer的函数。在COM服务器中实现的该功能将必要信息添加到注册表中。如果您正在使用ATL或其他COM框架,这可能是在引擎盖下完成的,这样您就不必直接与注册表连接。 DllRegisterServer只需要在安装时调用一次。

如果您尝试为尚未通过CoCreateInstance / regsvr32进程注册的COM对象调用DllRegisterServer,则CoCreateInstance将失败并显示错误表示:

  

未注册的课程

幸运的是,解决此问题的方法是简单地在COM服务器上调用regsvr32,然后再试一次。

答案 2 :(得分:6)

您不直接将LoadLibrary()与COM库一起使用。 CoCreateInstance()将调用此函数(如果尚未调用),然后将您在库中实现的类的实例添加到堆上,最后返回指向该对象的原始指针。当然,它可能在此过程中失败,因此您可以通过某种机制检查状态,如HRESULT。

为了简单使用它,您可以将COM库视为一个常见的DLL,1)一些预定义的入口(主)函数,2)您必须调用一些预定义的函数,如CoCreateInstance()来输入它,并接受这就是因为它必须。

答案 3 :(得分:3)

如果类型库嵌入在DLL中,您可以将其导入项目中:

#import "whatever.dll"

这将自动生成包含在项目中的头文件,并允许您使用导出的对象。

答案 4 :(得分:1)

这里有一些代码显示如何获取类工厂并使用它来创建COM对象。它使用struct来跟踪模块句柄和DllGetClassObject函数指针。您应该坚持使用模块句柄,直到完成COM对象。

要使用此功能,您需要分配ComModuleInfo结构的实例,并将szDLL设置为DLL文件名或完整路径名。然后使用要从该DLL获取的COM对象的类id和接口Id调用该函数。

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;
}

答案 5 :(得分:0)

如果它是一个COM DLL,您只需将其添加为项目的引用,然后您可以调用DLL中的函数。

是的,您可以使用低级COM函数,如DLLGetClassObject,但为什么会这样?