【问题标题】:ICallFactory with 32-bit and 64-bit type libraries side by sideICallFactory 与 32 位和 64 位类型库并排
【发布时间】:2015-08-26 11:40:35
【问题描述】:

我有一个进程内 COM 服务器,我想为其构建 32 位和 64 位版本。我可以毫无问题地做到这一点。但是,我在注册两个版本时遇到了一些问题。

我没有使用 ATL。在我的 DllRegisterServer 函数中,我使用的是RegisterTypeLibForUser。我通过使用我的 DLL 路径调用 LoadTypeLibEx 来获取 RegisterTypeLibForUser 调用的 ITypeLib 实例,并且我正在使用 REGKIND_NONE 标志。我正在使用 .idl 和 MIDL 编译器创建我的类型库。我将类型库作为资源嵌入到我的 .dll 中。鉴于下面的前两个项目符号(一切都按预期工作),我这样做的方式似乎没有任何问题。

  • 如果我只注册 32 位,则在 32 位客户端中一切正常,而在 64 位客户端中会出现预期故障(未注册类)。
  • 如果我只注册 64 位,则在 64 位客户端中一切正常,而在 32 位客户端中会出现预期故障(未注册类)。
  • 如果我先注册 64 位,然后注册 32 位,则在 32 位客户端中一切正常,但在 64 位客户端中出现故障。如果我随后取消注册 32 位服务器,则 64 位客户端继续失败。如果我重新注册 64 位服务器(无论是否取消注册),64 位客户端都可以工作。
  • 如果我先注册 32 位,然后注册 64 位,则在 64 位客户端中一切正常,但在 32 位客户端中出现故障。如果我随后取消注册 64 位服务器,则 32 位客户端继续失败。如果我重新注册 32 位服务器(无论是否取消注册),32 位客户端都可以工作。

看来,当我注册两台服务器时,后一个 RegisterTypeLibForUser 调用会破坏前一个 RegisterTypeLibForUser 调用的注册表设置。

至于我遇到的错误:

  • 只要注册了正确的服务器,CoCreateInstance 就始终有效。在 32 位客户端中,只要注册了 32 位服务器,CoCreateInstance 就可以工作(即使也注册了 64 位服务器)。带有 64 位服务器的 64 位客户端也是如此。
  • CoCreateInstance 工作的任何情况下,我都可以调用对象上的方法。我可以在公寓之间编组我的接口(我正在使用全局接口表),并且我可以在编组的接口上调用方法。
  • 我得到的错误与ICallFactory 在一个已编组接口的公寓中特别相关。我可以在编组接口上查询ICallFactory,没有任何问题。但是,当我使用异步接口的 IID 调用 CreateCall 时,收到错误 E_NOINTERFACE。
  • 如上一个列表中所述,只要上次注册的服务器是与客户端具有相同目标平台的服务器,我就不会收到此错误。

我现在正试图深入研究我的注册表,并确切地弄清楚注册发生时发生了什么变化,但是由于注册表重定向器,这并不是那么简单。当我发现注册表信息时,我会更新这篇文章。

【问题讨论】:

  • 尝试使用SysInternals Process Monitor 来查看RegisterTypeLibForUser() 对注册表的实际作用。听起来注册是在某处修改共享密钥,而不是使用单独的 32/64 位密钥。
  • 找到了。它与 RegisterTypeLib/RegisterTypeLib 的使用方式有关,为接口编写 ProxyStubClsid32 键。

标签: windows winapi com registry typelib


【解决方案1】:

想通了。

RegisterTypeLib 和 RegisterTypeLibForUser 在 64 位操作系统上运行时始终写入 32 位和 64 位条目(即使进程是 32 位)。这在大多数情况下是完全可以接受的,因为它只是被写入的接口和类型库元数据。编写接口键时,RegisterTypeLib/RegisterTypeLibForUser 将 ProxyStubClsid32 设置为适用于接口类型(dual、oleautomation 等)的通用默认 p/s。但是,通用代理/存根似乎不适用于自定义异步接口上的 ICallFactory。我修改了我的注册例程以始终在 32 位和 64 位注册表项中设置我的自定义代理/存根信息。这样可以确保以后的注册不会超过以前注册的信息。

[更新]:最后,由于可用 APIS 的一些弱点,我不得不编写自己的注册例程:

  • RegisterTypeLib 和 RegisterTypeLibForUser 不写入 AsynchronousInterface、SynchronousInterface 和 NumMethods 注册表项。这是因为编译的类型库中没有显式链接同步和异步接口的信息。我自己的注册例程按名称匹配接口——它找到具有相应 AsyncIXxx 接口的 IXxx 接口(仅按名称,没有方法比较),并根据需要设置这些注册表项。
  • RegisterTypeLib 和 RegisterTypeLibForUser 始终写入 32 位和 64 位注册表项,包括但不限于 ProxyStubClsid32。这意味着通过这种方式注册类型库将覆盖之前通过其他方式编写的相反架构的任何 ProxyStubClsid32。为了解决这个问题,可以先注册两个类型库(32 和 64),然后注册代理/存根 dll。但是:
  • MIDL 编译器生成的代理/存根代码似乎没有提供任何方法来在每个用户注册表中注册 p/s。如果需要按用户注册,则生成的注册例程将毫无用处。
  • 即使生成的代理/存根注册例程可以是每个用户的,如果代理/存根合并到 com 服务器 dll 中,则只有一个注册机会 - 单个 dll 中的单个 DllRegisterServer。如果该例程仅在注册表的一侧(32 位或 64 位,而不是两者)写入 ProxyStubClsid32 条目,则随后为其他架构注册 dll 将更改第一个架构的 ProxyStubClsid32。
  • 我还没有测试过一些像 CoRegisterPSClsid 这样的 api。由于文档没有说明按用户注册,我想它不受支持。

【讨论】:

  • 你能再扩展一下答案吗?您能告诉我们AsynchronousInterfaceSynchronousInterface 键(不是)在哪里创建的吗?您具体需要做什么来纠正它?
  • 感谢您的更新。两个笔记。 1) Interfaceregistry 密钥是每个体系结构的,因此(手动)为 32 位编写 ProxyStubClsid32 不会取代 64 位的密钥(反之亦然)。另一方面,您可以并且可能应该将类型库放在一个单独的文件中,因为它应该完全独立于平台,并且您可以将它注册到两个平台。如果您的接口与自动化兼容,则不需要标准 P/S DLL。您可以并且可能需要具有不同的 CLSID 条目。 2) CoRegisterPSClsid 只是运行时,它对注册表没有影响。
  • 所以,除了 RegisterTypeLib[ForUser] 所做的之外,您只需添加 AsynchronousInterfaceSynchronousInterface 键。由于您控制着您的图书馆,因此您可以继续按名称进行操作。
  • 好点,除了我确实需要覆盖我的 oleautomation 接口的默认代理/存根,因为标准的不支持异步调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-25
  • 2013-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多