注册免费COM所需的清单中需要哪些标签?

17

TL;DR regsvr32生成的所有注册表条目是否都需要在SxS无注册表COM清单中存在,反之亦然?


我正在尝试为第三方组件实现无需注册的COM。

阅读 相关 主题, 我发现有几个元素被提到,可以放入清单中:

从文档中得知, 我们可以向清单中添加以下标签来描述COM组件:

  • assemblyIdentity - 这实际上只是描述了“抽象的程序集”(assemblly)。
  • comClass - 描述COM类(IID接口)。似乎总是需要。
  • typelib - 什么时候使用?
  • comInterfaceExternalProxyStub - 什么时候使用?
  • comInterfaceProxyStub - 什么时候使用?

HKEY_LOCAL_MACHINE\SOFTWARE\Classes的其他文档中我们可以观察到COM注册表条目有几个类别:

使用regsvr42提取我尝试进行注册释放的dll中的内容时,会产生一个仅包含comClass条目的清单,没有typelib或ProxyStub条目。 (我交叉检查了写入的键,有关的DLL pdm.dll,MS的Process Debug Manager只写入那些键,也就是说,在注册表中似乎没有类型库或代理存根信息。)
如果注册表仅包含与comClass相关的信息,那么这是否意味着此信息将足以在SxS清单中使用,还是需要在清单中添加其他信息?
作为旁注,我注意到注册表中包含一个VersionIndependentProgId和一个在末尾附加了版本号的ProgId。清单只有一个ProgId条目,并且文档说明如下: progid:与COM组件相关联的依赖于版本的程序标识符。 ProgID的格式为..。
但是文档还指出:
comClass元素可以有…元素作为子元素,列出依赖于版本的progids。
并且他们说 progid属性应该是与版本无关的。
那么,在这里放什么?当客户端不请求特定版本时,这是否重要?

PDM没有类型库,它没有IDispatch接口,必须使用CoCreateInstance()。而且它像一个自由线程内部组件一样运行,因此不需要代理/存根。这里实际上出了什么问题? - Hans Passant
@Hans,谢谢。并没有什么特别问题,但我不确定是否存在任何潜在的陷阱。(COM总是让我感觉我错过了什么。)如果你能帮助我将上面的胡言乱语解开成两到三个实际有用的问题,我会非常感激。否则,我很高兴你认为一切都很顺利 :-) - Martin Ba
1个回答

16

assemblyIdentity元素始终是必需的,是清单的一部分。 必须始终提供comClass元素,它代替HKLM\Software\Classes\CLSID注册表键,并用于使客户端的CoCreateInstance()调用工作。 file元素命名COM服务器可执行文件。

其余的键是可选的,它们需要使编排工作。 当客户端调用需要在不同线程上进行时,就会发生编排。 当服务器和客户端处于不同进程中(即进程外服务器)或当服务器在另一台计算机上运行时,这将始终发生。 当comClass元素中指定的ThreadingModel要求时,也可以发生这种情况。 换句话说,当在一个线程上创建了COM对象但在另一个线程上调用该对象并且服务器不支持多线程时,就会发生这种情况。

RPC实现编排,但它有一个需要帮助的任务。 它需要知道函数的参数以及返回类型。 这样,它就可以将它们的值正确地序列化为数据包,以便跨网络传输或传递到在另一个线程中进行调用的代码。 这是代理的工作。 存根在接收端运行,并反序列化参数以构建堆栈帧并进行调用。 然后,函数返回值以及通过引用传递的任何参数值将返回给调用者。 进行调用的代码实际上没有意识到它没有直接调用该函数。

有四种基本情况:

  • COM服务器根本不支持以这种方式进行调用,必须始终在创建它的相同线程中使用。 在此停止,无需向清单添加任何内容。

  • COM服务器实现了IMarshal接口。当COM无法找到其他方式来封送调用时,自动查询该接口。这相当罕见,除非COM服务器聚合了自由线程化的封送程序。换句话说,它完全是线程安全的,不需要任何帮助,并始终在进程内运行。 PDM很可能是这样工作的。在清单中停止,不需要添加任何东西。

  • COM服务器作者通过用IDL语言编写服务器接口描述来启动项目。然后由MIDL编译。它可用的一个选项是从IDL声明中自动生成代码,可以用于构建实现代理和存根的单独DLL。 IDL足够丰富,可以描述函数参数类型和用法的细节,以允许使用此自动生成的代码进行封送。有时候IDL属性不足,COM作者就会编写自定义封送程序。COM在运行时加载该DLL以自动创建代理和存根对象。

  • 针对COM Automation子集(IDispatch接口),Windows具有内置的封送程序,知道如何封送符合子集要求的调用。非常普遍。它使用类型库来发现函数声明。

后两个项目需要使用HKLM\Software\Classes\Interface,它具有每个接口的IID条目。这就是COM找到为接口创建代理和存根的方法。如果它无法找到该键,则退回到IMarshal。您必须使用comInterfaceExternalProxyStub元素替换注册表键。使用comInterfaceProxyStub是一种特殊情况,即代理和存根代码包含在COM服务器可执行文件中而不是作为单独的文件。例如,ATL项目中的选项通过“允许合并代理/存根”向导选择打开。

最后一个项目还需要使用typelib元素,以便内置封送程序可以找到所需的类型库。

当COM客户端通过IDispatch进行晚期绑定时,需要使用progId,客户端的运行时支持库中的CreateObject()辅助函数是样板文件。例如,在任何脚本宿主中使用。

了解如何创建COM服务器的内部知识肯定会有所帮助,总是要联系供应商或作者寻求建议。但是可以通过观察服务器注册时写入哪些注册表键来进行反向工程,SysInternals' ProcMon工具是查看的最佳方法。要查找的基本事项:

  • 如果您看到它写入HKLM\Software\Classes\Interface键,则可以假定您必须提供comInterfaceExternalProxyStub元素

  • 如果看到它为ProxyStubClsid32键写入{00020420-0000-0000-C000-000000000046},则可以假定它正在使用标准编组程序,并且您必须使用comInterfaceExternalProxyStub元素以及typelib元素。然后您还应该看到它在IID的TypeLib注册表键中写入Typelib注册表键中的条目。后者给出类型库的路径。几乎总是与COM服务器相同,将类型库嵌入资源非常常见。如果是单独的(.tlb文件),则必须部署它。

  • 如果ProxyStubClsid32键值是另一个GUID,则可以假定它使用自己的代理/存根DLL。然后,您还应该看到它为代理写入CLSID键,其InProcServer32键为DLL提供了路径。如果该文件名与服务器的文件名匹配,则可以假定已合并代理/存根代码,并且必须改用comInterfaceProxyStub元素。否则,需要使用comInterfaceExternalProxyStub并部署DLL

  • 如果在HKLM\Software\Classes中看到它写入ProgID,则使用progid元素,如跟踪所示。


太棒了。谢谢。 :-) pdm.dll 是一个有趣的案例,因为存在第二个 DLL(msdbg2.dll),它似乎包括用于调试接口的 ProxyStubs。(例如,Visual Studio 要附加到进程,就需要这个 msdbg2.dll)然而,我们使用 pdm.dll(来调试 vbscript)的方式似乎并不需要 msdbg2.dll 中的 ProxStubs,因为我们的代码可以愉快地在没有 msdbg2.dll 的情况下工作。(虽然,如果注册了,msdbg2.dll 将通过 pdm.dll 加载 - 不知道它确切地做什么。)哎,这一切都是一团糟。 - Martin Ba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接