我正在用C ++创建一个DLL,它将在Delphi 7项目中使用。
这个问题与this one有关,我只提出两个函数Validate
和GetToken
,现在它们将在C ++中完成,字符串数组GetToken
生成将被送回德尔福。
问题是我不知道如何在dll中创建将在c ++中返回字符串数组的函数,我不知道它将如何存储以便在Delphi中进一步使用。
该功能的声明如下:
function GetToken(Chain:string):Arrayofstring;
答案 0 :(得分:2)
根据您的代码审查,Delphi代码希望该函数具有以下签名:
function GetToken(Chain: AnsiString): array of AnsiString;
你不能用C ++编写这样的函数。 C ++不知道Delphi字符串是什么,也不知道Delphi动态数组是什么。这两种类型都需要从Delphi的内存管理器中分配,而C ++ DLL无法访问它。此外,C ++不知道如何使用Delphi的register
调用约定。
DLL接口的设计很差。 DLL不应该使用特定于语言的类型,除非设计者的意图排除所有其他语言。 (在这种情况下,甚至更高版本的相同语言被排除在外,因为AnsiString
的定义在Delphi 2009中发生了变化,包含了Delphi 7无法正确处理的更多元数据。)最安全的调用约定通常是stdcall
。这就是Windows API中的所有内容。
更好的界面将使用所有语言共有的类型,并且它将规定使用可普遍访问的内存管理。有几种常见的方法可以做到这一点。例如:
字符串作为简单的以空字符结尾的字符数组返回 - 在Delphi中为PAnsiChar
; C ++中的char*
。 DLL为字符串分配缓冲区,并为这些字符串的数组分配缓冲区。当主机应用程序使用数组和字符串完成时,它调用DLL导出的另一个函数,其中DLL释放它分配的内存。这是例如FormatMessage
使用的模型;当主机程序使用消息字符串完成时,它会调用LocalFree
。
type
PStringArray = ^TStringArray;
TStringArray = array[0..Pred(MaxInt) div SizeOf(PAnsiChar)] of PAnsiChar;
function GetToken(Char: PAnsiChar): PStringArray; stdcall;
procedure FreeStringArray(StringArray: PStringArray); stdcall;
char** __stdcall GetToken(char const* Chain);
void __stdcall FreeStringArray(char** StringArray);
使用COM返回BStr对象的safearray。它类似于以前的技术,但内存管理是由COM而不是你的DLL定义的,所以接口的任何一方都需要定义的东西较少。
将回调函数传递给DLL,因此DLL不会返回一个字符串数组,而只是为它识别的每个字符串调用一次函数。然后你不必定义任何数组的外观,并且每个字符串的生命周期可以只是回调调用的生命周期 - 如果主机应用程序想要一个副本,它就可以这样做。新的函数签名看起来像这样:
type
TTokenCallback = procedure(Token: PAnsiChar); stdcall;
procedure GetToken(Chain: PAnsiChar; ProcessToken: TTokenCallback); stdcall;
typedef void (__stdcall* TokenCallback)(char const* Token);
void __stdcall GetToken(char const* Chain, TokenCallback ProcessToken);
如果您不是设计DLL接口的人,那么您需要依靠那些做过的人并将其更改为非Delphi代码更易于访问。如果你不能这样做,那么最后的替代方法是在Delphi中编写一个DLL,它包装你的DLL,将参数按照每个方面都能理解的东西。