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

时间:2013-04-04 20:46:44

标签: com vb6 windows-xp ada windows-xp-sp3

在我的VB6应用程序中,我多次调用我的团队从Ada项目创建的COM服务器(使用GNATCOM)。 COM服务器上基本上有两种方法可用。他们在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的计算机上,我们在尝试使用'Long'类型的参数时得到错误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可能会很好。

4 个答案:

答案 0 :(得分:6)

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

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

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

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

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

您的代码段还有另一个方面可能会导致此问题。结构是软件中非常麻烦的野兽,它们的成员对齐到它们的自然存储边界,并且对齐规则需要由相应的编译器解释。对于您引用的结构,情况肯定如此。它需要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的实用程序中获得帮助,尽管这些日子很难找到它们。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

我认为尝试修补结构是一个好主意,以便生成的类型库结构是四(或八)的倍数。您的状态成员占用2个字节,因此您可能应该在状态之前或之后插入相同类型的虚拟值 - 这应该使其最多为12个字节(如果打包为8个字节,则必须是三个虚拟变量)