1

我有一个 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 不会改变行为。

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

4

2 回答 2

2

在 Delphi XE2(或任何高于 2009 的版本)中无法禁用 UNICODE,但是有许多资源可以帮助您迁移应用程序。

于 2013-02-21T06:34:05.397 回答
1

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

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

一个通用的规则是永远不要导出 C 不能使用的 DLL 函数。1特别是,这意味着对于任何函数参数和返回类型只使用 C 兼容的类型,所以string它已经过时了,但WideString由于以下原因是安全的它的BSTR根源。

更改 DLL 以WideString用于其参数,而不是string.


1保持 C 兼容性还意味着使用 C 支持的调用约定。registerMicrosoft C 不支持Delphi 的默认调用约定,因此请使用cdeclorstdcall代替,就像您在曾经使用过的每个 Windows DLL 中看到的那样。

于 2013-02-21T18:46:32.360 回答