简体   繁体   English

从Delphi 7中的c ++ DLL接收字符串数组

[英]Receive an array of string from a c++ DLL in Delphi 7

I am creating a DLL in C++, it would be used in a Delphi 7 project. 我正在用C ++创建一个DLL,它将用于Delphi 7项目。

This question is related to this one , where I present two functions Validate and GetToken only that now they will be done in C++ and the array of strings GetToken produces would be sent back to Delphi. 这个问题关系到这一个 ,在这里我提出了两种功能Validate ,并GetToken只,现在他们将用C ++做和字符串数组GetToken产生将被送回德尔福。

The problems is I don't know how to create the function in the dll that will return the array of string in c++, and I don't know how it would be stored for further use in Delphi. 问题是我不知道如何在dll中创建将返回c ++中的字符串数组的函数,我不知道它将如何存储以便在Delphi中进一步使用。

The declaration of the function is as follows: 该功能的声明如下:

function GetToken(Chain:string):Arrayofstring;

According to your code review, the Delphi code expects the function to have the following signature: 根据您的代码审查,Delphi代码期望该函数具有以下签名:

function GetToken(Chain: AnsiString): array of AnsiString;

You cannot write such a function in C++. 你不能用C ++编写这样的函数。 C++ doesn't know what Delphi strings are, and it doesn't know what Delphi dynamic arrays are, either. C ++不知道Delphi字符串是什么,也不知道Delphi动态数组是什么。 Both types need to be allocated from Delphi's memory manager, which your C++ DLL won't have access to. 这两种类型都需要从Delphi的内存管理器中分配,而C ++ DLL无法访问它。 Furthermore, C++ doesn't know how to use Delphi's register calling convention. 此外,C ++不知道如何使用Delphi的register调用约定。

The DLL interface was designed poorly. DLL接口设计得很差。 DLLs should never use language-specific types unless it was the designer's intention to exclude all other languages. DLL不应该使用特定于语言的类型,除非设计者打算排除所有其他语言。 (And in this case, even later versions of the same language are excluded because the definition of AnsiString changed in Delphi 2009 to include more metadata that Delphi 7 won't handle properly.) The safest calling convention to choose is generally stdcall . (在这种情况下,甚至更高版本的相同语言被排除在外,因为AnsiString的定义在Delphi 2009中发生了变化,包含了Delphi 7无法正确处理的更多元数据。)最安全的调用约定通常是stdcall It's what everything in the Windows API uses. 这就是Windows API中的所有内容。

A better interface would use types that are common to all languages, and it would dictate the use of memory management that's accessible universally. 更好的界面将使用所有语言共有的类型,并且它将规定使用可普遍访问的内存管理。 There are several common ways to do that. 有几种常见的方法可以做到这一点。 For example: 例如:

  • The strings are returned as simple nul-terminated arrays of characters — PAnsiChar in Delphi; 字符串作为简单的以字符结尾的字符数组返回 - Delphi中的PAnsiChar ; char* in C++. C ++中的char* The DLL allocates buffers for the strings, and also allocates a buffer for the array of those strings. DLL为字符串分配缓冲区,并为这些字符串的数组分配缓冲区。 When the host application is finished using the array and the strings, it calls another function exported by the DLL wherein the DLL frees the memory it allocated. 当主机应用程序使用数组和字符串完成时,它调用DLL导出的另一个函数,其中DLL释放它分配的内存。 This is the model used by, for example, FormatMessage ; 这是由例如FormatMessage使用的模型; when the host program is finished the with message string, it calls LocalFree . 当主机程序完成带消息字符串时,它调用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); 
  • Use COM to return a safearray of BStr objects. 使用COM返回BStr对象的safearray。 It's similar to the previous technique, but the memory management is defined by COM instead of by your DLL, so there's less stuff that needs to be defined by either party of the interface. 它类似于以前的技术,但内存管理由COM而不是DLL定义,因此需要由接口的任何一方定义的东西较少。

  • Pass a callback function to the DLL, so instead of returning an array of strings, the DLL just calls the function once for each string it identifies. 将回调函数传递给DLL,因此DLL只是为它识别的每个字符串调用函数一次,而不是返回一个字符串数组。 Then you don't have to define what any array looks like, and the lifetime of each string can be just the lifetime of the callback call — if the host application wants a copy, it can do so. 然后你不必定义任何数组的外观,并且每个字符串的生命周期可以只是回调调用的生命周期 - 如果主机应用程序想要一个副本,它就可以这样做。 The new function signature would look something like this: 新的函数签名看起来像这样:

     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); 

If you're not the one who designed the DLL interface, then you need to lean on the folks who did and get it changed to be more accessible to non-Delphi code. 如果您不是设计DLL接口的人,那么您需要依靠那些做过的人并将其更改为非Delphi代码更易于访问。 If you can't do that, then the final alternative is to write a DLL in Delphi that wraps your DLL to massage the parameters into something each side understands. 如果你不能这样做,那么最后的替代方法是在Delphi中编写一个DLL,它包装你的DLL以将参数按摩到每个方面都能理解的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM