Vista是否对DCOM调用中的接口ID进行更严格的检查? (存根收到错误的数据)?
我希望每个人都可以原谅这个问题的长度和叙述方式。我决定在我的博客中详细描述这种情况。后来我看到乔尔(Joel)的邀请访问此站点,我想将其粘贴到此处以查看是否有人对此情况有任何了解。
我编写并现在支持一个应用程序,该应用程序由一个Visual Basic胖客户端组成,该客户端将DCOM与使用ATL用C ++编写的中间层COM +组件进行通信。它在我们所有的八个办公室中运行。每个办公室都托管一个后端服务器,该服务器包含COM +应用程序(由18个独立的组件组成)和SQLServer。 SQLServer通常位于同一台后端服务器上,但不一定要位于同一台后端服务器上。
最近,我们将纽约最大的办公室中的后端服务器从MSC群集迁移到了基于VMWare的ESX技术托管的新虚拟机。由于COM +应用程序的位置已从旧服务器移到了另一个具有不同名称的服务器,因此我必须重定向所有客户端,以便它们在新服务器上激活COM +应用程序。该过程有些陈旧,因为我对经历了类似基础架构升级的几个小型办公室进行的操作基本上是相同的。
一切似乎都很常规,在星期一早上,整个办公室(约1000台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 -method Save的末尾包含一个添加条目)。
实际上,我组件的接口公开了许多将Recordset作为输出参数传递的方法。那么,他们是否突然返回具有不同接口ID的Recordset的更高版本?看来确实是这样。然后我想,为什么要这么重要。对于较旧界面的客户端,vtable看起来相同。确实,我怀疑如果我们谈论的是进程内COM,而不是DCOM,那么显然这种无害的阻抗失配将被默默地忽略,并且不会造成任何问题。
当然,当进程和机器的边界发挥作用时,客户端和服务器之间就会有一个代理和一个存根。在这种情况下,我使用了带有自由线程编组器的类型库编组。因此,有两个谜题需要解决:
为什么在新服务器上的方法的输出参数中返回不同的接口?
为什么这只影响Vista客户端?
当我的服务器软件托管在我的八个办公室中每个服务器上时,我决定尝试依次将我的Vista客户端指向所有这些客户端,以查看哪些问题出在Vista上,哪些没有。照明测试。一些较旧的服务器仍可与Vista一起使用,但较新的服务器则无法。尽管一些较旧的服务器仍在运行Windows 2000,而较新的服务器在2003年运行,但这似乎不是问题。
在比较了组件DLL的日期之后,似乎每当客户端指向带有组件DLL的服务器的日期早于2003 Vista时,就可以了。但是那些带有DLLs的日期在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客户端的行为似乎有所不同?与XP相比,Vista似乎对从方法参数返回的接口ID进行了更严格的检查。
我应该如何在1999年以不同的方式对此进行编码,以免发生这种情况?接口应该是不可变的,当我在较新版本的MDAC下重新编译时,我无意中更改了接口,因为这些方法现在返回了另一个Recordset接口作为输出参数。据我所知,当时的类型库没有特定于版本的符号-也就是说,MDAC类型库的更高版本定义了Recordset21,但是该符号在2.1类型库中不可用。
解决方案
回答
当Microsoft掌握安全原则时,DCOM(和底层的RPC)引起了很多关注,并且确实进行了一些更改以关闭安全漏洞,从而导致更加严格的封送处理。我很惊讶我们在Vista中看到了这一点,但在XP中却没有看到,但是有可能为Vista添加了额外的检查。另外,在Vista中可能会强制要求XP中的可选严格性。
虽然我对MDAC的了解不足,无法知道我们是否可以避免这种情况,但我确实知道,安全性是Microsoft愿意牺牲向后兼容性的少数几个领域之一,因此我们可能无能为力。早在1999年"。