C# COM客户端从Delphi COM服务器读取值异常问题排查
问题原因及解决方案
核心问题根源
1. Double返回NaN的原因
Delphi的safecall调用约定会自动将带返回值的函数转换为COM标准签名:返回HRESULT错误码,原返回值作为最后一个[out, retval]参数传递。但如果直接声明safecall函数返回Double,会出现调用约定冲突:safecall要求用EAX寄存器返回HRESULT(整数类型),而浮点返回值本应放在FPU栈,这种冲突导致.NET客户端读取到错误的浮点值(NaN)。
2. String调用崩溃的原因
同样是safecall调用约定的冲突:直接返回String违反了COM方法需返回HRESULT的标准。此外,Delphi的String虽与COM的BSTR兼容,但如果未通过[out, retval]参数传递,会导致.NET客户端读取无效内存,直接触发崩溃(无异常提示,返回非0退出码)。
修复步骤
1. 调整Delphi COM服务器的方法签名
将所有带返回值的方法改为遵循COM标准:返回HRESULT,原返回值作为[out, retval]参数传递。示例代码如下:
// 接口声明 type ITest = interface(IDispatch) ['{你的接口GUID}'] function EchoInt(Value: Integer; out Result: Integer): HResult; safecall; function EchoDouble(Value: Double; out Result: Double): HResult; safecall; function EchoString(const Value: WideString; out Result: WideString): HResult; safecall; end; // 类实现 TTest = class(TAutoObject, ITest) public function EchoInt(Value: Integer; out Result: Integer): HResult; safecall; function EchoDouble(Value: Double; out Result: Double): HResult; safecall; function EchoString(const Value: WideString; out Result: WideString): HResult; safecall; end; // 方法实现 function TTest.EchoInt(Value: Integer; out Result: Integer): HResult; begin Writeln('EchoInt received: ', Value); Result := Value; Result := S_OK; end; function TTest.EchoDouble(Value: Double; out Result: Double): HResult; begin Writeln('EchoDouble received: ', Value); Result := Value; Result := S_OK; end; function TTest.EchoString(const Value: WideString; out Result: WideString): HResult; begin Writeln('EchoString received: ', Value); Result := Value; Result := S_OK; end;
2. 更新.NET客户端引用
- 重新编译Delphi ActiveX库,生成新的类型库(TLB文件)。
- 在.NET 6客户端中删除旧的COM引用,重新添加新的TLB引用;或使用
tlbimp.exe工具重新生成互操作程序集:
tlbimp.exe YourComServer.tlb /out:Interop.YourComServer.dll
3. 验证字符串内存管理
确保Delphi中使用WideString作为字符串参数和返回值类型,Delphi会自动处理BSTR的内存分配与释放,无需手动调用SysAllocString等API。
补充说明
无论是进程内/进程外COM,还是32位/64位环境,问题本质都是COM方法签名不遵循标准导致的跨语言互操作冲突。调整为标准COM签名后,所有类型的返回值都能被.NET客户端正确解析。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

