我有一个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不会改变行为。
显而易见的解决方案是只编写简单的包装函数,它们完全暴露了我现在需要的功能。我可以这样做,但我更喜欢上面那种狡猾的方式。
答案 0 :(得分:2)
无法在Delphi XE2(或任何大于2009的版本)中禁用UNICODE,但有许多资源可以帮助您迁移应用程序。
答案 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
调用约定,因此请使用cdecl
或stdcall
,就像您在使用过的每个Windows DLL中看到的一样。