我的问题类似于获取 DLL 文件的 CLSID?,我想。
我有一个包含一些 DLL 的目录,每个 DLL 都实现一个或多个 COM 接口。我想要得到:
1) 各接口名称 2)实现接口的类的CLSID
对于每个 DLL。重要的是一切都可以通过编程完成(所以我不能使用某种 COM 浏览器并手动查找该信息)。
稍后我将查找给定接口名称的 CLSID 并使用 IDispatch 调用一些方法。
一种替代方法似乎是扫描注册表,尝试匹配类型、接口和类 GUID 以及 .dll 文件名。但这似乎很慢而且不稳健。
有人对这个问题有明确的解决方案吗?
编辑:
根据 Ben Voigt 的回复,我提供了以下适合我的需求的代码:
ITypeLib *typelib;
ITypeInfo *typeinfo;
LoadTypeLibEx(_T("c:\\mydir\\mycom1"), REGKIND_NONE, &typelib);
for (UINT i = 0;i < typelib->GetTypeInfoCount();++i) {
TYPEKIND typekind;
typelib->GetTypeInfoType(i, &typekind);
if (typekind == TKIND_COCLASS) {
// class!
CComBSTR className;
TYPEATTR *typeattr;
typelib->GetTypeInfo(i, &typeinfo);
typeinfo->GetDocumentation(MEMBERID_NIL, &className, NULL, NULL, NULL);
typeinfo->GetTypeAttr(&typeattr);
GUID classGUID = typeattr->guid;
for (UINT j = 0;j < typeattr->cImplTypes;++j) {
// interface!
CComBSTR interfaceName;
HREFTYPE hreftype;
ITypeInfo *classtypeinfo;
typeinfo->GetRefTypeOfImplType(j, &hreftype);
typeinfo->GetRefTypeInfo(hreftype, &classtypeinfo);
classtypeinfo->GetDocumentation(MEMBERID_NIL, &interfaceName, NULL, NULL, NULL);
// associate interfaceName with classGUID here
}
}
}
您无法从 COM DLL 中获取它,但可以从类型库中获取它。我很确定 MIDL 编译器有一个反编译 typelib 的开关,但解析 IDL 并不像使用 TypeLib API 那么容易。
让事情变得复杂的是,类型库通常作为资源存储在 DLL 中。因此,您需要提取资源,并使用 TypeLib API 打开它。
LoadTypeLibEx
开始,它将返回一个 ITypeLib*
接口指针(您知道您将需要 COM 来获取有关 COM 库的信息,对吗?)。这实际上会为您完成资源提取步骤。
ITypeLib::GetTypeInfoCount
来了解有多少种类型。为每一个调用 ITypeLib::GetTypeInfoType
来查找接口和组件类。然后拨打 ITypeLib::GetTypeInfo
然后拨打 ITypeInfo::GetDocumentation
即可获取姓名。
到目前为止,您似乎已经拥有了所有这些。接下来,您需要该类型的 GUID,该 GUID 通过
ITypeInfo::GetTypeAttr
(而不是 ITypeLib::GetLibAttr
)获得。这给你一个 TYPEATTR
结构,它有一个 guid
字段。
在相同的
TYPEATTR
结构中,您需要 cImplTypes
字段。与 ITypeInfo::GetRefTypeOfImplType
一起,您可以将每个组件类与其实现的接口相匹配。
请注意,不保证接口和实现组件类之间存在 1:1 的关系。并且接口可以位于与组件类不同的库中。
Ben Voigt 的回答有几点需要注意:并非每个 COM DLL 都有类型库。就你的情况而言,似乎确实如此;但这不是一个要求。唯一可靠的要求是 DLL 导出函数 DllGetClassObject(),您可以在其中传递 CLSID 并取回对象工厂。
您可以加载该库并为系统上每个已注册的 CLSID 调用 DllGetClassObject(扫描 HKCR\CLSID 下的注册表以获取这些列表)。您返回的有效对象是 DLL 支持的对象。现在,从理论上讲,甚至不需要注册 DLL 支持的 CLSID;我可以设想一个 DLL 实现私有对象类,只有目标客户知道,其他人不知道。但这是一个非常非常奇特的场景;一方面,通过 CLSID 查找 DLL 路径的常规 COM 机制将被破坏。客户端必须直接加载 DLL(编辑:或在可执行文件的清单中引用 DLL,如免注册 COM 所允许的那样)。
您还可以扫描注册表中的 CLSID,其中所考虑的 DLL 注册为 InprocServer32;如果是私人课程,这又会被打破。您可以在
regedit
中进行注册表搜索,在数据中搜索。此外,您还必须处理文件名大小写问题、短名称与长名称、硬链接、环境变量替换等。所以我不会推荐它。
编辑:只是想到了另一种方法。下载 Regmon,运行它,然后在 DLL 上调用 regsvr32。然后观察哪些 CLSID 被触及。
您可能想查看 WiX 的 heat 实用程序中的源代码,它做同样的事情: