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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4651869/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:57:03  来源:igfitidea点击:

Given a COM DLL, extract all classes CLSID and corresponding interface name

c++windowscom

提问by douglaz

My question is similar to Getting CLSID for a DLL file?, I think.

我的问题类似于获取 DLL 文件的 CLSID?, 我认为。

I have a directory with some DLLs, each one implementing one or more COM interfaces. I would like to get:

我有一个包含一些 DLL 的目录,每个 DLL 都实现了一个或多个 COM 接口。我想得到:

1) Each interface name 2) The CLSID of the class implementing the interface

1) 每个接口名称 2) 实现接口的类的 CLSID

For each DLL. It's important that everything can be done programatically (So I can't use some sort of COM browser and manually look up for that information).

对于每个 DLL。一切都可以以编程方式完成很重要(所以我不能使用某种 COM 浏览器并手动查找该信息)。

Later I will lookup the CLSID given the interface name and call some methods using IDispatch.

稍后我将查找给定接口名称的 CLSID 并使用 IDispatch 调用一些方法。

One alternative seems to be scanning the registry trying to match the type, interface and class GUID and the .dll filename. But that seems to be slow and not robust.

一种替代方法似乎是扫描注册表,尝试匹配类型、接口和类 GUID 以及 .dll 文件名。但这似乎很慢而且不健壮。

Does someone has a clear solution to this problem?

有人对这个问题有明确的解决方案吗?

EDIT:

编辑:

With the response of Ben Voigt, I came with the following code which suit my needs:

根据 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
        }
    }
}

回答by Ben Voigt

You can't get that from the COM DLL, but you can get it from the typelib. I'm pretty sure the MIDL compiler has a switch to decompile a typelib, but parsing IDL wouldn't be as easy as using the TypeLib API.

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

To complicate matters, the typelib is often stored as a resource inside the DLL. So you'd extract the resource, and open it with the TypeLib API.

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

Start with LoadTypeLibExwhich will return you an ITypeLib*interface pointer (you knew you were going to need COM in order to get information about COM libraries, right?). This will actually do the resource extraction step for you.

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

Then, call ITypeLib::GetTypeInfoCountto find out how many types there are. Call ITypeLib::GetTypeInfoTypefor each one to find the interfaces and coclasses. And call ITypeLib::GetTypeInfofollowed by ITypeInfo::GetDocumentationto get the name.

然后,调用ITypeLib::GetTypeInfoCount以找出有多少种类型。调用ITypeLib::GetTypeInfoType每个以查找接口和协同类。和呼叫ITypeLib::GetTypeInfo之后ITypeInfo::GetDocumentation得到的名称。

It looks like you have all of this so far. Next you need the GUID of the type, which is gotten with ITypeInfo::GetTypeAttr(not ITypeLib::GetLibAttr). That gives you a TYPEATTRstructure, which has a guidfield.

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

From the same TYPEATTRstructure, you'll need the cImplTypesfield. That together with ITypeInfo::GetRefTypeOfImplTypewill let you match up each coclass to the interfaces it implements.

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

Note that there's not guaranteed to be a 1:1 relationship between interfaces and implementation coclasses. And the interface can be in a different library from the coclass.

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

回答by Seva Alekseyev

Few caveats to Ben Voigt's answer: not every COM DLL has a typelib. In your case, it seems, it does; but that's not a requirement. The only rock-solid requirement is that the DLL exports the function DllGetClassObject(), where you pass a CLSID and get back an object factory.

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

You could load the library and call DllGetClassObject for every registered CLSID on the system (scan the registry under HKCR\CLSID for the list of those). The ones where you get a valid object back are the ones that the DLL supports. Now, in theory, it's not even a requirement that the CLSIDs the DLL supports are registered; I can envision a DLL that implements private object classes that only the intended client knows about and no one else should. But this is a very, very exotic scenario; for one thing, the regular COM mechanism of looking up the DLL path by the CLSID will break for those. The client would have to load the DLL directly.

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

You could also scan the registry for the CLSID's where the DLL under consideration is registered as InprocServer32; this, again, will break in case of private classes. You can do a registry search in regedit, search in data. Also, you'd have to deal with filename case issues, short vs. long names, hardlinks, environment variable substitution and so on. So I would not recommend it.

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

EDIT: just thought of another way. Download Regmon, run it, and call regsvr32 on the DLL. Then watch what CLSID's are touched.

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

回答by James Johnston

You might want to look at the source code in WiX's heat utility, which does the same thing:

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

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

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