如何在 C++ 中将 COM DLL 与 LoadLibrary 一起使用

发布于 2024-08-20 09:02:14 字数 377 浏览 5 评论 0原文

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

因此,我正在开发一个 DLL,并且需要单独的 COM DLL 中提供的一些功能。当我使用 Depends.exe 查看 COM DLL 时,我看到了 DllGetClassObject() 等方法和其他函数,但没有我感兴趣的函数。

我可以访问 COM DLL(旧版)源代码,但它很混乱,我宁愿使用二进制形式的 COM DLL,就像一个大黑匣子,不知道里面发生了什么。

那么,如何使用 LoadLibrary 从我的代码中调用 COM DLL 函数呢?是否可以?如果是的话,您能给我一个如何做到这一点的例子吗?

我在这个项目中使用 Visual Studio 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

屌丝范 2024-08-27 09:02:14

一般来说,您应该更喜欢CoCreateInstanceCoGetClassObject 而不是直接访问 DllGetClassObject但是,如果您正在处理无法或不想注册的 DLL,则下面的描述如下(这些函数在幕后执行的操作的一部分。


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

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

  1. 调用 DllGetClassObject(clsid, IID_IClassFactory, &cf),其中 clsid 是您想要获取其类对象的 CLSID,而 cf 当然是类工厂。
  2. 调用 cf->CreateInstance(0, iid, &obj),其中 iid 是您要使用的接口的 IID,obj 当然是对象。
  3. ???
  4. 利润!

CoCreateInstance 执行步骤 1 和 2。CoGetClassObject 执行步骤 1。如果需要创建同一类的多个实例,则可以使用 CoGetClassObject,这样就不需要每次都重复步骤 1。)

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.)

李不 2024-08-27 09:02:14

通常,您可以使用 CoCreateInstance() 来实例化 COM DLL 中的对象。当您执行此操作时,无需像使用普通 DLL 那样首先加载 DLL 并获取 proc 地址。这是因为 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 库附带类型库,其中列出了该库提供的接口和 CoClass。该类型库以硬盘上的文件形式存在——通常它直接嵌入到与库本身相同的 DLL 或 EXE 中。 Windows 通过查看 Windows 注册表知道在哪里可以找到类型库和 COM 库本身。注册表中的条目告诉 Windows DLL 在硬盘驱动器上的位置。

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

这些信息如何进入 Windows 注册表?当安装 COM DLL 时,它就会被注册。这通常是通过运行 regsvr32.exe 来完成的,它将您的 DLL 加载到内存中并调用名为 DllRegisterServer 的函数。该函数在 COM 服务器中实现,将必要的信息添加到注册表中。如果您使用 ATL 或其他 COM 框架,这可能是在幕后完成的,这样您就不必直接与注册表交互。 DllRegisterServer 只需在安装时调用一次。

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

班级未注册

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

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.

╭⌒浅淡时光〆 2024-08-27 09:02:14

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

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

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.

初懵 2024-08-27 09:02:14

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

#import "whatever.dll"

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

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.

染墨丶若流云 2024-08-27 09:02:14

下面的一些代码展示了如何获取类工厂并使用它来创建 COM 对象。它使用一个结构来跟踪模块句柄和 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;
}

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;
}
没︽人懂的悲伤 2024-08-27 09:02:14

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

是的,您可以使用 DLLGetClassObject 等低级 COM 函数,但为什么要这样做呢?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文