首先,COM对我来说就像是黑魔法。但是我需要在我正在开发的一个项目中使用COM dll。
所以,我有一个我正在开发的DLL,我需要一些单独的COM DLL中可用的功能。当我使用Depends.exe查看COM DLL时,我看到像DllGetClassObject()这样的方法和其他函数,但没有我感兴趣的函数。
我可以访问COM DLL(遗留)源代码,但它很乱,我更喜欢使用二进制中的COM DLL,就像一个不知道内部发生了什么的大黑盒子。
那么,如何使用LoadLibrary从我的代码中调用COM DLL函数?可能吗?如果,是的,你能给我一个如何做的例子吗?
我正在将Visual Studio 6用于此项目。
非常感谢!
答案 0 :(得分:22)
一般情况下,您应该更喜欢CoCreateInstance
或CoGetClassObject
,而不是直接访问DllGetClassObject
。但是,如果您正在处理可以使用的DLL,那么t,或者不想,注册,然后下面描述(部分)这些功能在幕后做什么。
给定一个CLSID,DllGetClassObject
允许你获取类对象,你可以从中创建实例(如果我没记错的话,通过IClassFactory
接口)。
步骤摘要(自从我上次触摸COM以来已经有一段时间了,所以请原谅任何明显的错误):
DllGetClassObject(clsid, IID_IClassFactory, &cf)
,其中clsid
是您要获取类对象的CLSID,cf
当然是类工厂。cf->CreateInstance(0, iid, &obj)
,其中iid
是您要使用的界面的IID,obj
当然是对象。(CoCreateInstance
执行步骤1和2. CoGetClassObject
执行步骤1.如果您需要创建同一类的多个实例,则可以使用CoGetClassObject
,以便步骤1不会每次都需要重复。)
答案 1 :(得分:11)
通常,您将使用CoCreateInstance()
从COM DLL实例化对象。当你这样做时,不需要首先加载DLL并获得proc地址,就像你需要使用普通的DLL一样。这是因为Windows“知道”COM DLL实现的类型,它们实现了什么DLL,以及如何实例化它们。 (当然假设COM DLL已经注册,通常是这样)。
假设您有一个带有要使用的IDog接口的COM DLL。在那种情况下,
interface IDog : IUnknown
{
HRESULT Bark();
};
coclass Dog
{
[default] Interface IDog;
};
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,但为什么会这样?