assemblyIdentity 元素始终是必需的,它是清单管道的一部分。您必须始终提供 comClass 元素,它替代HKLM\Software\Classes\CLSID
注册表项并用于使客户端的 CoCreateInstance() 调用工作。file 元素命名 COM 服务器可执行文件。
其余的键是可选的,它们是进行编组工作所必需的。当客户端调用需要在不同的线程上进行时,就会发生编组。当服务器和客户端在不同的进程中时,总是会发生这种情况,对于进程外服务器或服务器在另一台机器上运行时。当 comClass 元素中指定的 ThreadingModel 需要它时,就会发生这种情况。换句话说,当 COM 对象在一个线程上创建但在另一个线程上调用时,服务器不是线程安全的。
RPC 实现了编组,但它需要帮助完成一项工作。它需要知道函数的参数是什么,以及返回类型。这样它就可以正确地将它们的值序列化为一个数据包,该数据包可以通过网络传输或传递给另一个进行调用的线程中的代码。这是代理的工作。存根在接收端运行并反序列化参数以构建堆栈帧并进行调用。函数返回值以及通过引用传递的任何参数值然后返回给调用者。否则,进行调用的代码根本不知道它没有直接调用该函数。
有四种基本情况:
COM 服务器根本不支持以这种方式调用,并且必须始终从创建它的同一线程中使用。停在那里,无需在清单中添加任何内容。
COM 服务器实现IMarshal 接口。当 COM 无法找到另一种编组调用的方式时,由 COM 自动查询。这非常罕见,除了 COM 服务器聚合自由线程编组器的情况。换句话说,它本身是完全线程安全的,不需要任何帮助,并且总是在进程中运行。PDM 很可能以这种方式工作。停在那里,无需在清单中添加任何内容。
COM 服务器作者通过用 IDL 语言编写服务器的接口描述来开始他的项目。然后由 MIDL 编译。它提供的一个选项是从 IDL 声明中自动生成代码,该代码可用于构建实现代理和存根的单独 DLL。IDL 足够丰富来描述函数参数类型和用法的详细信息,以允许通过此自动生成的代码完成编组。有时 IDL 属性是不够的,COM 作者然后编写了一个自定义编组器。COM 在运行时加载该 DLL 以自动创建代理和存根对象。
特定于 COM 自动化子集(IDispatch 接口),Windows 有一个内置的编组器,它知道如何编组满足子集要求的调用。很普通的。它使用类型库来发现函数声明。
后两个项目符号需要 using HKLM\Software\Classes\Interface
,它具有每个接口的 IID 条目。这就是 COM 找出如何为接口创建代理和存根的方法。如果它找不到密钥,那么它会退回到 IMarshal。您必须使用 comInterfaceExternalProxyStub 元素来替换注册表项。使用 comInterfaceProxyStub 是一种特殊情况,即代理和存根代码包含在 COM 服务器可执行文件中,而不是单独的文件。例如,ATL 项目中的一个选项通过“允许合并代理/存根”向导选择打开。
最后一个项目符号还需要使用 typelib 元素,这样内置编组器才能找到它需要的类型库。
当 COM 客户端通过 IDispatch 使用后期绑定时需要 progId,客户端运行时支持库中的 CreateObject() 辅助函数是样板。例如,在任何脚本主机中使用。
了解如何创建 COM 服务器的一些内部知识肯定会有所帮助,请始终联系供应商或作者寻求建议。它可以进行逆向工程,但是通过观察注册服务器时写入的注册表项,SysInternals 的 ProcMon 工具是最好的查看方式。要寻找的基本内容:
如果您看到它写入HKLM\Software\Classes\Interface
密钥,那么您可以假设您必须提供 comInterface|External|ProxyStub 元素
如果您看到它为 ProxyStubClsid32 键写入 {00020420-0000-0000-C000-000000000046},那么您可以假设它使用标准编组器,并且您必须使用 comInterfaceExternalProxyStub 元素以及 typelib 元素。然后,您还应该看到它写入了 IID 的 TypeLib 注册表项以及 HKLM\Software\Classes\Typelib 注册表项中的条目。后者给出了类型库的路径。几乎总是和 COM 服务器一样,将类型库作为资源嵌入是很常见的。如果它是单独的(一个 .tlb 文件),那么您必须部署它。
如果 ProxyStubClsid32 键值是另一个 guid,那么您可以假设它使用自己的代理/存根 DLL。然后您还应该看到它为代理编写了 CLSID 键,它的 InProcServer32 键为您提供了 DLL 的路径。如果该文件名与服务器的文件名匹配,那么您可以假设代理/存根代码已合并,您必须改用 comInterfaceProxyStub 元素。如果不是,则需要 comInterfaceExternalProxyStub 并且您必须部署 DLL
如果您看到它写入 ProgID,HKLM\Software\Classes
则使用 progid 元素,正如跟踪中所示。