Vista 是否对 DCOM 调用中的接口 ID 进行更严格的检查?(存根收到错误数据)?

StackOverflow https://stackoverflow.com/questions/63720

  •  09-06-2019
  •  | 
  •  

我希望大家原谅这个问题的篇幅和叙述方式。我决定在我的博客中详细描述这种情况。后来我看到了乔尔对这个网站的邀请,我想我应该把它贴在这里,看看是否有人对情况有所了解。

我编写并现在支持一个应用程序,该应用程序由使用 DCOM 的 Visual Basic 胖客户端和使用 ATL 用 C++ 编写的中间层 COM+ 组件组成。它在我们所有八个办公室运行。每个办公室都托管一个后端服务器,其中包含 COM+ 应用程序(由 18 个独立组件组成)和 SQLServer。SQLServer 通常位于同一后端服务器上,但并非必须如此。

我们最近将我们最大的办公室(纽约)的后端服务器从 MSC 集群迁移到托管在 VMWare ESX 技术上的新虚拟机。由于 COM+ 应用程序的位置已从旧服务器移动到具有不同名称的新服务器,因此我必须重定向所有客户端,以便它们在新服务器上激活 COM+ 应用程序。这个程序是老套的,因为我对几个较小的办公室做了基本上相同的事情,这些办公室也经历了类似的基础设施升级。

一切似乎都很正常,周一早上,整个办公室(大约 1,000 个 Windows XP 工作站)在新服务器上正常运行,没有发生任何事故。但后来我的移动团队打来了电话——有一位律师在家工作,使用 VPN 连接,在被重定向到新服务器后出现了一个奇怪的错误:

Error on FillTreeView2 - The stub received bad data.

啊?我以前从未见过此错误消息。是新服务器吗?但办公室里的所有工作站都工作正常。我告诉移动组将律师切换回旧服务器(仍在运行),错误就消失了。那么有什么区别呢?原来这位律师正在家里运行 Vista。

我们不在任何办公室运行 Vista,但我们确实有一些律师在家里运行 Vista(当然有一些在我的纽约办公室)。我也是这样,从来没见过这个问题。为了确认是否存在问题,我启动了我的 Vista 笔记本电脑,将其指向新服务器,并得到了相同的错误。我将其指向旧服务器,并且运行良好。显然,Vista 和新服务器上的组件存在一些问题——这个问题似乎不会影响 XP 客户端。会是什么呢?

下一站——我的笔记本电脑上的应用程序错误日志。这产生了有关该错误的更多信息:

Source:        Microsoft-Windows-RPC-Events
Date:          9/2/2008 11:56:07 AM
Event ID:      10
Level:         Error
Computer:      DevLaptop
Description:   Application has failed to complete a COM call because an incorrect
interface ID was passed as a parameter.

The expected Interface ID was 00000555-0000-0010-8000-00aa006d2ea4, 
The Interface ID returned was 00000556-0000-0010-8000-00aa006d2ea4.

User Action - Contact the application vendor for updated version of the application.

界面 ID 提供了我解开谜团所需的线索。“预期”接口 id 标识 MDAC 的 Recordset 接口——特别是该接口的 2.1 版本。“返回”接口对应于 Recordset 的更高版本(版本 2.5 与版本 2.1 的不同之处在于在 vtable 末尾包含一个附加条目——方法 Save)。

事实上,我的组件的接口公开了许多将 Recordset 作为输出参数传递的方法。那么他们是否突然返回了 Recordset 的更高版本——具有不同的接口 id?情况确实如此。然后我想,为什么这很重要。对于旧接口的客户端来说,vtable 看起来是一样的。事实上,我怀疑如果我们谈论的是进程内 COM,而不是 DCOM,这种明显无害的阻抗不匹配会被默默地忽略,并且不会引起任何问题。

当然,当进程和机器边界发挥作用时,客户端和服务器之间存在代理和存根。在本例中,我使用类型库封送处理和免费线程封送处理程序。所以有两个谜团需要解开:

为什么我在新服务器上的方法的输出参数中返回不同的接口?

为什么这仅影响 Vista 客户端?

由于我的服务器软件托管在我的八个办公室的服务器上,因此我决定尝试将我的 Vista 客户端按顺序指向所有这些办公室,看看哪些 Vista 存在问题,哪些没有。发光测试。一些较旧的服务器仍然可以在 Vista 上运行,但较新的服务器则不能。尽管一些较旧的服务器仍在运行 Windows 2000,而较新的服务器则运行在 2003 年,但这似乎不是问题。

比较组件 DLL 的日期后发现,只要客户端指向具有 2003 年之前的组件 DLL 的服务器,Vista 就可以正常工作。但那些 DLL 的日期在 2003 年之后的就存在问题。不管您相信与否,多年来服务器组件上的代码没有(或至少没有重大)更改。显然,不同的日期只是由于我的开发机器上的组件的重新编译所致。其中一次重新编译似乎发生在 2003 年。

灯泡亮了。当将记录集从服务器传递回客户端时,我的 ATL C++ 组件将该接口称为 _Recordset。该符号来自 msado15.dll 中嵌入的类型库。这是我在 C++ 代码中的行:

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename ( "EOF", "adoEOF" )

不要被msdad15.dll中的15所欺骗。显然这个 DLL 在 MDAC 版本的长系列中没有改变名称。

当我当时编译该应用程序时,MDAC 的版本是 2.1。因此 _Recordset 使用 2.1 接口 id 进行编译,这是运行这些组件的服务器返回的接口。

所有客户端都使用(我相信)1999 年生成的 COM+ 应用程序代理。定义我的接口的类型库包括以下行:

importlib("msado21.tlb");

这解释了为什么他们期望在我的方法的输出参数中使用 Recordset 2.1 版本。显然,问题出在我 2003 年的重新编译上,而且当时 _Recordset 符号不再对应于 2.1 版本。事实上_Recordset对应的是2.5版本,具有独特的接口id。我的解决方案是在我的 C++ 代码中将所有引用从 _Recordset 更改为 Recordset21。我重建了组件并将它们部署到新服务器上。瞧——客户们似乎又高兴了。

总之,有两个问题仍然困扰着我。

为什么代理/存根基础设施对于 Vista 客户端的行为似乎有所不同?看来 Vista 对从方法参数返回的接口 ID 进行了比 XP 更严格的检查。

早在 1999 年,我应该如何以不同的方式对此进行编码,以免发生这种情况?接口应该是不可变的,当我在较新版本的 MDAC 下重新编译时,我无意中更改了接口,因为这些方法现在返回了不同的 Recordset 接口作为输出参数。据我所知,当时的类型库没有特定于版本的符号——也就是说,更高版本的 MDAC 类型库定义了 Recordset21,但该符号在 2.1 类型库中不可用。

有帮助吗?

解决方案

当微软获得安全信仰时,DCOM(和底层的 RPC)受到了很多关注,并且肯定会做出一些改变来弥补安全漏洞,从而导致更严格的编组。我很惊讶你在 Vista 中看到了这一点,但在 XP 中却没有,但可能是为 Vista 添加了额外的检查。或者,XP 中的可选严格性可能在 Vista 中被强制执行。

虽然我对 MDAC 的了解不够,不知道您是否可以阻止这种情况,但我确实知道安全性是 Microsoft 非常愿意牺牲向后兼容性的少数几个领域之一,因此您可能无法做任何事情”更好”早在1999年。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top