4

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

我的应用程序是在 D2009 中编译的。当它调用 AnsiString 函数时,它会返回垃圾。

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

我已经尝试在两个项目中包含最新版本的 FastMM,但即便如此,2009 应用程序也无法从 2007 dll 中读取 AnsiStrings。

关于这里出了什么问题的任何想法?有没有办法解决这个问题?

4

6 回答 6

11

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

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

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

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

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

此外,编写一个只能由单一语言使用的 DLL 是自私的。以与 Windows API 相同的风格编写您的 DLL,您不会出错。

于 2009-06-25T00:27:20.993 回答
2

好的,所以还没有尝试过,所以一个很大的免责声明拍在这个上面。

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

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

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

第二个问题是元素大小,这将是完全疯狂的事情。它应该是 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 变量,以确保不会出现内存被覆盖的问题。

于 2009-06-25T11:10:11.333 回答
0

您的 DLL 一开始就不应该返回 AnsiString 值。首先正确工作的唯一方法是,如果 DLL 和 EXE 都使用 ShareMem 单元编译,即使这样,也只有使用相同的 Delphi 版本编译。D2007 的内存管理器与 D2009 的内存管理器(或任何其他跨版本使用的内存管理器)AFAIK 不兼容。

于 2009-07-09T02:01:46.847 回答
0

就像这里的快速解决方案一样:如果您从字符串中的 dll 传回的实际数据不超过 255 个字符,您可以更改 in-dll 和接口声明以使用 ShortString,无论 2007/2009 版本如何. 由于您在 2007 年已经在使用 AnsiString 而没有代码页标识符,因此 unicode 不会给您带来任何麻烦。

如果你这样做,你需要做的就是改变声明,如:

function MyStringReturningFunction : ShortString ; external 'MyLibrary.dll';

(并在 dll 中:function MyStringReturningFunction : ShortString;分别)

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

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

应该比更改大量代码更容易。但请注意,正如我所说,您的数据不得超过 255 个字符,因为这是 ShortString 可以容纳的最大大小。

于 2012-06-05T14:27:09.927 回答
0

我同意 Rob 和 Remy 的观点:common Dlls 应该返回 PAnsiChar 而不是 AnsiStrings。

如果使用 D2009 编译的 DLL 工作正常,为什么不停止使用 D2007 编译它并一劳永逸地开始使用 D2009 编译它?

于 2009-07-09T04:22:22.040 回答
0

您可能只需要将 DLL 转换为 2009。根据 Embarcadero 的说法,转换为 2009 是“容易的”,应该不会花您任何时间。

于 2009-06-25T23:17:17.990 回答