gpt4 book ai didi

Delphi 7 调用 DelphiXE2 dll 导致宽字符串损坏

转载 作者:行者123 更新时间:2023-12-02 10:47:31 25 4
gpt4 key购买 nike

我有一个 Delphi 7 应用程序,需要调用 SOAP API,该 API 对于可用的 SOAP 导入器来说太新了。我对自己感到满意的是,如果不付出太多努力,D7 就无法调用 SOAP API。但我还有 Delphi XE2,它可以导入 SOAP 并非常愉快地调用它。所以我在 XE2 中编写了一个简单的 dll 包装器,它公开了肥皂接口(interface)的必要部分。我可以从 XE 程序调用 dll。

在 Delphi7 中,我从 XE 获取了 SOAP API 导入文件,删除了 {$SCOPED_ENUMS ON} 定义和调用不可用 SOAP 包装器的初始化部分,并将整个字符串更改为宽字符串。这样就可以编译了。我使用 FastMM 并启用 ShareMM 来使字符串传递工作并避免使所有内容都成为 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 不会改变行为。

显而易见的解决方案是只需编写简单的包装函数即可准确地公开我现在需要的功能。我可以做到这一点,但我更喜欢上面的狡猾方式。

最佳答案

相关 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 中看到的那样你曾经用过。

关于Delphi 7 调用 DelphiXE2 dll 导致宽字符串损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14995945/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com