Windows XP SP3 上的错误 0x800706F7“存根收到错误数据”

Posted

技术标签:

【中文标题】Windows XP SP3 上的错误 0x800706F7“存根收到错误数据”【英文标题】:Error 0x800706F7 "The stub received bad data" on Windows XP SP3 【发布时间】:2013-03-27 03:04:42 【问题描述】:

在我的 VB6 应用程序中,我多次调用我的团队从 Ada 项目(使用 GNATCOM)创建的 COM 服务器。 COM 服务器上基本上有 2 种可用的方法。他们在 VB 中的原型是:

Sub PutParam(Param As Parameter_Type, Value)
Function GetParam(Param As Parameter_Type)

其中 Parameter_Type 是一个枚举类型,用于区分我可以从 COM 服务器放入/获取的许多参数,而“Value”是一个 Variant 类型变量。 PutParam() 接收一个变体,GetParam() 返回一个变体。 (我真的不知道为什么在 VB6 对象浏览器中没有对 COM 服务器接口上的 Variant 类型的引用......)。

该项目的产品多年来一直以这种方式连续使用,在装有 Windows XP SP2 的计算机上,此界面没有任何问题。在装有 WinXP SP3 的计算机上,当尝试将参数设置为“长”类型时,我们会收到错误 0x800706F7“存根收到错误数据”。

有人知道是什么原因造成的吗? COM 服务器仍在使用 SP2 的系统中构建。在带有 SP3 的系统上构建它应该有什么不同吗? (就像我们在 X64 系统中为 X64 构建时一样)。

导致问题的调用之一如下(更改了一些 var 名称):

Dim StructData As StructData_Type

StructData.FirstLong = 1234567
StructData.SecondLong = 8901234
StructData.Status = True

ComServer.PutParam(StructDataParamType, StructData)

其中StructData_Type的定义是:

Type StructData_Type
    FirstLong As Long
    SecondLong As Long
    Status As Boolean
End Type

(以下已在问题首次发布后添加)

IDL中COM服务器接口的原语调用定义如下:

// Service to receive data
HRESULT PutParam([in] Parameter_Type Param, [in] VARIANT *Value);

//Service to send requested data
HRESULT GetParam([in] Parameter_Type Param, [out, retval] VARIANT *Value);

我要传递的结构的定义是:

struct StructData_Type

   int FirstLong;
   int SecondLong;
   VARIANT_BOOL Status;
 StructData_Type;

奇怪的是,这里的定义使用“int”作为 FirstLong 和 SeconLong 的类型,当我检查 VB6 对象资源管理器时,它们的类型是“Long”。顺便说一句,当我从 COM 服务器中提取 IDL(使用特定实用程序)时,这些参数被定义为 Long。

更新:

我已经使用为 Windows 7 编译的 COM 服务器版本(不同版本的 GNAT,相同的 GNATCOM 版本)测试了相同的代码,它可以工作!我真的不知道这里发生了什么。我将继续尝试找出 WinXP SP3 上的问题,但很高兴知道它在 Win7 上有效。如果您有类似的问题,最好尝试迁移到 Win7。

【问题讨论】:

你检查过这个吗:support.microsoft.com/kb/895321/en-us 我实际上并没有在我的界面上使用 BSTR...在这种特定情况下,它只是一个具有两个 Long 变量和一个 Integer 的复杂类型(结构)...这个问题只发生长变量。 这是 VB6 特有的:您在从 C++ 客户端或脚本客户端进行调用时遇到问题吗?此外,我认为有关您的 COM 组件的一些详细信息可能会有所帮助。至少定义接口。 上面增加了一些接口信息。我还没有从其他客户那里尝试过。我将尝试从 C++ 客户端进行此调用...如上所述,我的服务器是用 Ada 编写的,并使用带有 GNATCOM(COM 库)的 GNAT 构建。 "...为什么在 VB6 对象浏览器中没有对 Variant 类型的引用..." 这是因为 Variant 是 VB 中的默认类型。 “...这里的定义使用'int' 作为 FirstLong 的类型... [并且在] 对象资源管理器中,它们的类型为'Long'...” 如果您使用 MIDL 编译您的 IDL,那没关系。在 32 位平台上,int 与 MIDL 的 long 同义。如果您不使用 MIDL 编译,那么我们需要更多详细信息。 msdn.microsoft.com/en-us/library/windows/desktop/… 【参考方案1】:

我将重点解释错误的含义,问题中的提示太少,无法提供简单的答案。

当您跨执行边界进行调用时,COM 中会使用“存根”。问题中没有明确说明,但您的 Ada 程序可能是 EXE 并实现了进程外 COM 服务器。在 Windows 中跨越进程之间的边界是很困难的,因为它们具有很强的隔离性。这是在 Windows 中通过 RPC(远程过程调用)完成的,这是一种用于跨边界进行调用的协议,典型的例子是网络。

要进行 RPC 调用,必须将函数的参数序列化为网络数据包。 COM 不知道如何做到这一点,因为它对函数的实际参数知之甚少,它需要代理 的帮助。 确实知道参数类型是什么的一段代码。在接收端是一段非常相似的代码,它的作用与代理的作用完全相反。它反序列化参数并进行内部调用。这是存根。

当存根接收到一个网络数据包并且它包含的数据多于或少于函数参数值所需的数据时,这可能会失败。显然,它不知道如何处理该数据包,没有明智的方法将其转换为 StructData_Type 值,并且它将因“存根收到错误数据”错误而失败。

所以要考虑的这个错误的第一个解释是 DLL Hell 问题。代理和存根之间不匹配。如果这个应用已经稳定了很长时间,那么这不是一个令人愉快的解释。

您的代码 sn-p 的另一个方面可能会导致此问题。结构是软件中非常麻烦的野兽,它们的成员对齐到它们的自然存储边界,对齐规则受制于各自编译器的解释。您引用的结构肯定就是这种情况。它需要 10 个字节来存储字段,4 + 4 + 2 并且它们自然对齐。但该结构实际​​上是 12 字节长。最后填充两个字节以确保当结构存储在数组中时整数仍然对齐。这也使 COM 的工作变得非常困难,因为 COM 隐藏了实现细节并且结构对齐是一个巨大的细节。它需要帮助来复制一个结构,即 IRecordInfo 接口的工作。当找不到该接口的实现时,存根也会失败。

我将谈谈代理、存根和 IRecordInfo。生成代理/存根对有两种基本方式。一种方法是用一种称为 IDL(接口描述语言)的语言来描述接口,然后用 MIDL 编译它。该编译器能够自动生成代理/存根代码,因为它知道函数参数类型。您将获得一个需要在客户端和服务器上注册的 DLL。你的服务器可能正在使用它,我不知道。

第二种方式是 VB6 使用的,它利用 Windows 内置的通用代理。称为 FactoryBuffer,其 CLSID 为 00000320-0000-0000-C000-000000000046。它通过使用类型库来工作。类型库是 COM 服务器中函数的机器可读描述,足以让 FactoryBuffer 弄清楚如何序列化函数参数。这个类型库也是提供 IRecordInfo 需要了解结构成员如何对齐的信息的类型库。我不知道它在服务器端是如何完成的,之前从未听说过GNATCOM。

所以对这个问题的一个强有力的解释是你的类型库有问题。在 VB6 中尤其棘手,因为您无法直接控制它使用的 guid。当您进行微不足道的更改时,它喜欢生成新的,避免它的唯一方法是选择二进制兼容性选项。它使用类型库的旧副本,并尝试使新副本尽可能兼容。如果您没有打开该选项,那么确实会遇到麻烦,尤其是对于结构的指导。 Kaboom 如果它发生了变化并且另一端仍在使用旧的 guid。

只是一些关于从哪里开始寻找的提示。不要以为这是 SP3 引起的问题,这个 COM 基础结构已经很长时间没有改变了。但是由于安装了新的操作系统版本并且必须重新注册所有内容,因此肯定会出现这种问题。 SysInternals 的 ProcMon 是一个很好的实用程序,可以查看程序使用注册表来查找代理、存根和类型库。你肯定会从 COM Spy 类型的实用程序中获得帮助,尽管现在很难找到它们。

【讨论】:

"...在 VB6 中特别棘手,因为您不能直接控制它使用的 guid..." 我误认为这一段(当然,通常是正确的)不应该与 OP 的特定情况?我的印象是他只在他的客户端中使用了VB6,而不是COM服务器。 客户端使用代理,因为它与服务器通信。 是的。我曾经认为“选择二进制兼容性”适用于 COM 服务器,而不适用于客户端。 没错。然而,在 OP 的情况下,原始类型库的来源还不清楚。很有可能它来自 VB6 声明,因为他的问题中有一个 VB6 类型,并被 ADA 服务器使用。 原始类型库来自一个 IDL(手写),使用 make_idl 处理以生成 ADA 规范和类型库 (.tlb)。 ADA 规范已添加到我们的主项目中。在 C++ 中,我们只需 #import 可执行文件以加载 COM 服务器的命名空间(包含 COM 服务器类定义 - 嗯,不完全是,我认为它只包含存根。)。【参考方案2】:

如果它突然停止在 XP 上正常工作,我要寻找的第一个罪魁祸首是类型不匹配。此类系统上的“long”现在可能是 64 位,而您的 Ada COM 代码(和/或您的 C int)可能需要 32 位。对于传统编译的系统,编译器会为您检查这一点,但是您使用 COM 的额外间接性使得这变得困难。

你在那里写的关于“当我们为 64 位系统编译时”的内容让我特别怀疑。你知道,64 位编译可能会改变许多 C 类型的大小。

【讨论】:

COM 是二进制标准,并且只有 32 位,所以如果操作系统的“bittishness”会有所作为,我会感到惊讶。 @MarkBertenshaw - 它确实对非 COM C 代码产生了影响。许多 C 类型在编译为 64 位时大小不同。 由于该进程使用 COM,它必须是 32 位的,包括 Ada 服务器,除非有一些巧妙的代码,COM 服务器通过一个 thunking 层与 64 位进程通信.【参考方案3】:

Related Post 建议您在结构中需要填充,因为编组代码可能需要比您实际发送的数据更多的数据(当然,这是一个错误)。您的结构包含 9 个字节(假设每个 ints/long 有 4 个字节,布尔值有 1 个字节)。尝试添加填充,以便您的结构包含 4 个字节的倍数(或者,如果没有,则为 8 的倍数,因为帖子的预期大小不清楚)

【讨论】:

使用 C++ 客户端执行此操作。没用。哦,当我使用 sizeof()(12 字节)查询它的大小时,该结构已经被填充了。尝试用额外的“long”填充它以完成 16 个字节,但它仍然返回相同的错误。【参考方案4】:

我还建议问题是由于您的结构中的填充问题。我不知道您是否可以使用 #pragma 来控制它,但可能值得查看您的文档。

我认为尝试修补您的结构以使生成的类型库结构是四(或八)的倍数是个好主意。您的 Status 成员占用 2 个字节,所以也许您应该在 Status 之前或之后插入一个相同类型的虚拟值 - 这应该使它达到 12 个字节(如果打包到 8 个字节,这必须是三个虚拟变量) .

【讨论】:

"[...] 这样生成的类型库 struct [...]" - 我不明白这个,你说我应该更改类型库的定义StructData' 的长度为 12 个字节(四的倍数)? 不,你不应该改变类型库,如果它是由 Ada 编译器生成的。您需要以某种方式强制编译器使用适合与 VB 一起使用的布局。我找不到这个编译器的文档,所以恐怕球在你的球场上。或许你可以提供一个链接?

以上是关于Windows XP SP3 上的错误 0x800706F7“存根收到错误数据”的主要内容,如果未能解决你的问题,请参考以下文章

求WINDOWS XP SP3 英文版下载

Windows XP SP3环境下如何关闭WFP(windows file protection)功能

番茄花园 Windows XP Pro SP3 V 1.1 的KEY

找Windows XP SP3 ISO文件的下载地址

windows XP Sp3 简体中文(官方正版认证)【微软正版XP操作系统SP3专业版】 怎么装呀?

谁能给个Windows XP Professional 版本2002 SP3 的iso镜像下载的地址?拜托了各位 谢谢