在Delphi 2009应用程序中解析Delphi 2007 DLL的返回值

在Delphi 2009应用程序中解析Delphi 2007 DLL的返回值,delphi,string,dll,delphi-2009,ansistring,Delphi,String,Dll,Delphi 2009,Ansistring,我有一个用D2007编译的DLL,它有返回ansistring的函数 我的申请是用D2009编译的。当它调用ansisting函数时,它会返回垃圾 我创建了一个小测试app/dll来进行实验,发现如果app和dll都是用相同版本的Delphi(2007或2009)编译的,那么就没有问题了。但当一个在2009年编译,另一个在2007年编译时,我得到的是垃圾 我已经尝试在这两个项目中包括最新版本的FastMM,但即使这样,2009年的应用程序也无法从2007年的dll中读取解析 你知道这里出了什么问

我有一个用D2007编译的DLL,它有返回ansistring的函数

我的申请是用D2009编译的。当它调用ansisting函数时,它会返回垃圾

我创建了一个小测试app/dll来进行实验,发现如果app和dll都是用相同版本的Delphi(2007或2009)编译的,那么就没有问题了。但当一个在2009年编译,另一个在2007年编译时,我得到的是垃圾

我已经尝试在这两个项目中包括最新版本的FastMM,但即使这样,2009年的应用程序也无法从2007年的dll中读取解析


你知道这里出了什么问题吗?有办法解决这个问题吗?

在Delphi 2007和Delphi 2009之间,Ansistring的内部结构发生了变化。(不要生气,这种可能性从第一天起就存在了。)Delphi2009字符串维护一个数字,指示其数据所在的代码页

我建议您像地球上其他DLL一样,传递函数可以填充的字符缓冲区。调用者应该传递一个缓冲区指针和一个指示缓冲区大小的数字。(确保您清楚地知道是以字节还是以字符为单位测量大小。)DLL函数填充缓冲区,写入的内容不超过给定的大小,并计算终止的空字符

如果调用者不知道缓冲区应该有多少字节,那么您有两个选项:

  • 当输入缓冲区指针为空时,使DLL特别工作。在这种情况下,让它返回所需的大小,以便调用者可以分配那么多的空间并再次调用该函数

  • 让DLL为自己分配空间,调用方可以使用预定的方法在以后释放缓冲区。DLL可以导出一个函数来释放它已分配的缓冲区,也可以为调用方指定一些相互可用的API函数,例如
    GlobalFree
    。DLL必须使用相应的分配API,例如
    GlobalAlloc
    。(不要使用Delphi内置的内存分配函数,如
    GetMem
    New
    ;不能保证调用方的内存管理器会知道如何调用
    Free
    Dispose
    ,即使它是用相同的语言编写的,即使它是用相同的Delphi版本编写的。)


此外,编写只能由单一语言使用的DLL是自私的。用与Windows API相同的样式编写DLL,这样就不会出错。

好的,所以还没有尝试过,所以在这一条上加了一个大大的免责声明

在帮助查看器中,查看主题(RAD Stufio中的Unicode)ms-help://embarcadero.rs2009/devcommon/unicodeinide_xml.html

将Delphi 2007字符串返回到Delphi 2009,您将遇到两个问题

首先,Rob提到的代码页。您可以通过声明另一个Anistring并在新Anistring上调用StringCodePage来设置。然后通过调用SetCodePage将其分配给旧的ansisting。这应该行得通,但如果不行,还有希望

第二个问题是元素大小,这将是完全疯狂的事情。它应该是1,所以设为1。这里的问题是没有可依赖的SetElementSize函数

试试这个:

var
  ElemSizeAddr: PWord; // Need a two-byte type
  BrokenAnsiString: AnsiString; // The patient we are trying to cure
...
  ElemSizeAddr := Pointer(PAnsiChar(BrokenAnsiString) - 10);
  ElemSizeAddr^ := 1; // The size of the element
那就够了

现在,如果StringCodePage/SetCodePage不起作用,您可以执行与上面相同的操作,将获取地址的行更改为扣除12,而不是10

它到处乱涂乱画,这就是为什么我喜欢它

您最终将需要移植这些DLL,但这使端口更易于管理


最后一个字-根据您返回AnsiString(函数结果、输出参数等)的方式,您可能需要首先将字符串分配给不同的AnsiString变量,以确保内存被覆盖不会出现问题。

您可能只需要将DLL转换为2009。根据Embarcadero的说法,到2009年的转换是“简单的”,应该不会占用您任何时间。

您的DLL一开始就不应该返回AnsiString值。唯一能正确工作的方法是,如果DLL和EXE都是用ShareMem单元编译的,甚至只有当它们是用相同的Delphi版本编译的时候。D2007的内存管理器与D2009的内存管理器(或内存管理器的任何其他跨版本使用)不兼容,恕我直言。

我同意Rob和Remy的观点:普通DLL应该返回PAnsiChar 答案

如果DLL可以用D2009编译,为什么不停止编译呢
使用D2007并开始一次性使用D2009编译它?

这里只是一个快速解决方案:如果您从dll传回的实际数据在字符串中不超过255个字符,您可以将dll中的和接口的偏差更改为使用ShortString,无论2007/2009版本如何都可以使用。由于您已经在2007年使用了AnsiString,并且没有代码页标识符,所以unicode不会给您带来任何麻烦

如果这样做,只需更改声明,如:

function MyStringReturningFunction : ShortString ; external 'MyLibrary.dll';
(在dll中分别为:
函数MyStringReturningFunction:ShortString;

当然,输入/输出参数也是如此:

procedure MyStringTakingAndReturningFunction(s1:ShortString; var s2:ShortString); external 'MyLibrary.dll';

应该比更改大量代码更容易。但是要小心,正如我所说,您的数据不能超过255个字符,因为这是短字符串可以容纳的最大大小。

对不起,我应该发布一些代码。需要明确的是:接口将返回值声明为“ansisting”,而不是“string”。据我所知,这与Unicode字符串转换无关。至少不是直接的。如果你应该发布一些代码,那么继续发布一些代码。您可以编辑自己的问题。很高兴知道2009年更改了ansistring定义,但重写DLL会使端口变为20