我有一个 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 shim 非常易于编码和维护,因为 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;
在德尔福 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 不会改变行为。
显而易见的解决方案是只编写简单的包装函数,以准确地公开我现在需要的功能。我可以这样做,但我更喜欢上述狡猾的方式。