Delphi 7调用DelphiXE2 dll得到了腐败的宽带

时间:2013-02-21 06:30:35

标签: delphi dll unicode delphi-7 widestring

我有一个Delphi 7应用程序需要调用一个对于可用的SOAP导入程序来说太新的SOAP API。我很满意D7无需花费太多精力就可以调用SOAP API。但我也有Delphi XE2,它可以导入SOAP并非常愉快地调用它。所以我在XE2中编写了一个简单的dll包装器,它暴露了soap接口的必要部分。我可以从XE程序中调用dll。

在Delphi7中,我从XE中获取了SOAP API导入文件,删除了{$ SCOPED_ENUMS ON}定义和调用不可用SOAP包装器的初始化部分,以及整个更改字符串到宽字符串。编译。我正在使用启用ShareMM的FastMM来使字符串传递工作并避免使所有内容都成为stdcall。

我试图这样做的原因是,如果它工作,它将使SOAP垫片非常容易编码和维护,因为90%的代码是由XE2 SOAP导入器生成的,这意味着当我们将D7应用程序移动到现代Delphi时,代码将基本保持不变。

但是当我运行它时,我会得到奇怪的字符串(以及随之而来的访问冲突)。我有一些简单的函数,它们不使用SOAP代码来使问题更加明显。

将Delphi7 exe中的宽字符串传递给DelphiXE2 dll,字符串长度加倍(根据Length()函数),但没有匹配的数据转换。因此,D7中的宽字符串“123”在XE2中变为“1234 ....”,其中....是堆栈中发生的任何垃圾。被视为字节数组的字节数均为半零字节。

将宽字符串从XE2 dll传回D7我得到了镜像效果 - 字符串长度减半,字符串被截断(“1234”变为“12”)。

我正在粘贴代码,因为我知道你会要求代码。

在Delphi XE2中,我正在导出这些函数:

// testing
function GetString(s:string):string; export;
function AddToString(s:string):string; export;

implementation

function GetString(s:string):string;
begin
  Result := '0987654321';
end;

function AddToString(s:string):string;
begin
  Result := s + '| ' + IntToStr(length(s)) + ' there is more';
end;

在Delphi 7中:

function GetString(s:widestring):widestring; external 'SMSShim.dll';
function AddToString(s:widestring):widestring; external 'SMSShim.dll';

procedure TForm1.btnTestGetClick(Sender: TObject);
var
  s: widestring;
begin
  s := widestring('1234');
  Memo1.Lines.Add(' GetString: ' + GetString(s));
end;

procedure TForm1.btnTestAddClick(Sender: TObject);
var
  s: widestring;
begin
  s := widestring('1234567890');
  Memo1.Lines.Add(' AddToString: ' + AddToString('1234567890'));
end;

我可以从任何一方运行,使用D7可执行文件作为主机应用程序来调试dll。检查调试器中的参数和返回值会得到上面的结果。

令人讨厌的是,如果我将delphi7中的导入声明为字符串,我会获得正确的长度但数据无效。如图所示声明我尝试返回时获得有效数据,错误长度和访问冲突。

全部使用stdcall不会改变行为。

显而易见的解决方案是只编写简单的包装函数,它们完全暴露了我现在需要的功能。我可以这样做,但我更喜欢上面那种狡猾的方式。

2 个答案:

答案 0 :(得分:2)

答案 1 :(得分:1)

有问题的DLL导出期望接收UnicodeString参数的函数。 (如您所知,string类型在Delphi 2009中成为UnicodeString的别名。)Delphi 7应用程序无法使用该DLL;运行时库不知道如何操作该类型,因为它在2002年发布Delphi 7时不存在。

虽然UnicodeString的字符大小与WideString兼容,但它们的类型不同。 UnicodeString的结构类似于新的AnsiString,因此它具有长度字段,引用计数,字符大小和代码页。 WideString有一个长度字段,但它携带的任何其他元数据都没有记录。 WideString只是Delphi暴露COM BSTR类型的方式。

生存的一般规则是永远不会导出C不能使用的DLL函数。 1 特别是,这意味着只对任何函数参数和返回类型使用C兼容类型,所以string已经结束,但WideString因其BSTR根而安全。

更改DLL以使用WideString作为参数,而不是string


1 维护C兼容性还意味着使用C支持的调用约定。 Microsoft C不支持Delphi的默认register调用约定,因此请使用cdeclstdcall,就像您在使用过的每个Windows DLL中看到的一样。