给定一个 COM DLL,提取所有类 CLSID 和相应的接口名称

Posted

技术标签:

【中文标题】给定一个 COM DLL,提取所有类 CLSID 和相应的接口名称【英文标题】:Given a COM DLL, extract all classes CLSID and corresponding interface name 【发布时间】:2011-01-10 21:46:59 【问题描述】:

我想我的问题类似于Getting CLSID for a DLL file?。

我有一个包含一些 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
        
    

【问题讨论】:

【参考方案1】:

你不能从 COM DLL 中得到它,但你可以从 typelib 中得到它。我很确定 MIDL 编译器有一个开关来反编译 typelib,但解析 IDL 并不像使用 TypeLib API 那样容易。

更复杂的是,类型库通常作为资源存储在 DLL 中。因此,您将提取资源,并使用 TypeLib API 打开它。

LoadTypeLibEx 开始,它将返回一个ITypeLib* 接口指针(你知道你需要COM 来获取有关COM 库的信息,对吧?)。这实际上会为您执行资源提取步骤。

然后,致电ITypeLib::GetTypeInfoCount 了解有多少种类型。为每一个调用ITypeLib::GetTypeInfoType 以查找接口和coclass。并调用ITypeLib::GetTypeInfo 后跟ITypeInfo::GetDocumentation 以获取名称。

到目前为止,您似乎已经掌握了所有这些。接下来,您需要使用ITypeInfo::GetTypeAttr(不是ITypeLib::GetLibAttr)获得的类型的GUID。这会给你一个TYPEATTR 结构,它有一个guid 字段。

从相同的TYPEATTR 结构中,您将需要cImplTypes 字段。与ITypeInfo::GetRefTypeOfImplType 一起,您可以将每个 coclass 与其实现的接口相匹配。

请注意,不能保证接口和实现 coclass 之间存在 1:1 的关系。并且接口可以在与 coclass 不同的库中。

【讨论】:

【参考方案2】:

Ben Voigt 的回答有几点需要注意:并非每个 COM DLL 都有类型库。在您的情况下,似乎确实如此;但这不是要求。唯一坚如磐石的要求是 DLL 导出函数 DllGetClassObject(),您可以在其中传递 CLSID 并返回对象工厂。

您可以加载库并为系统上每个注册的 CLSID 调用 DllGetClassObject(扫描 HKCR\CLSID 下的注册表以获取这些列表)。获得有效对象的对象是 DLL 支持的对象。现在,理论上,甚至不需要注册 DLL 支持的 CLSID;我可以设想一个实现私有对象类的 DLL,只有预期的客户知道,其他人都不知道。但这是一个非常非常奇特的场景。一方面,通过 CLSID 查找 DLL 路径的常规 COM 机制会因此而中断。客户端必须直接加载 DLL。

您还可以扫描注册表中的 CLSID,其中考虑的 DLL 注册为 InprocServer32;在私人课程的情况下,这将再次中断。您可以在regedit 中进行注册表搜索,在数据中进行搜索。此外,您还必须处理文件名大小写问题、短名称与长名称、硬链接、环境变量替换等。所以不推荐。

编辑:只是想到了另一种方式。下载 Regmon,运行它,然后在 DLL 上调用 regsvr32。然后观察 CLSID 被触摸到了什么。

【讨论】:

DllGetClassObject 可能会有不良副作用,所以我不建议这样做。此外,完全有可能将公共 CLSID 和私有 CLSID 组合在一起。由于 DLL 是通过创建公共类型来加载的,因此无需在 COM 机制之外加载 DLL。 嗯,终极取证将涉及反汇编 DLL 并检查 DllGetClassObject() 的代码。在典型的实现中(阅读:ATL 之一)会有一个全局数据结构,在一个地方整齐地列出所有 CLSID。我很难想象一个 COM DLL,其中支持的 coclass 集是有条件的。 这取决于许可计划的狡猾程度。我绝对可以设想在实例化任何其他类之前实例化许可证管理器对象并使用它的要求,问题是尝试在没有许可证的情况下创建对象的失败是“我不知道那个对象”还是“尝试过创建该对象但失败” 我相信有一个专门的 HRESULT 用于“未获得许可的课程”。 可能。并不意味着每个开发人员都选择使用它。 “TCP 端口关闭”也有专门的回复,但防火墙通常不使用它。【参考方案3】:

您可能想查看 WiX 的 heat 实用程序中的源代码,它执行相同的操作:

http://wix.sourceforge.net/manual-wix3/heat.htm

【讨论】:

以上是关于给定一个 COM DLL,提取所有类 CLSID 和相应的接口名称的主要内容,如果未能解决你的问题,请参考以下文章

检索COM类工厂中CLSID为{000245000-0000-0000-C000-000000000046的组件时失败,出现错误以下80040154

检索 COM 类工厂中 CLSID 为 的组件时失败,原因是出现以下错误: 80040154。

导出excel的程序,COM 类工厂中 CLSID 的组件时失败.错误: 80040154

通过命令行编译一个 VB6 COM DLL 每次都会得到一个不同的 CLSID

需要在注册表中找到已注册 com dll 的 clsid

Window 7 上的 ATL COM DLL 注册无法更新 CLSID 部分,但 TypeLib 可以工作