delphi - 在 Freepascal 编译的 DLL 和 Delphi 编译的 EXE 之间交换字符串 (PChar)

标签 delphi string dll pascal freepascal

经过大量实验,我找到了一种方法,可以将 FreePascal 编译的 DLL 中的 PChar 与 Delphi 编译的 EXE 进行交换。我负责 DLL 和 EXE 源代码,但其中一个必须在 FreePascal 中,另一个在 Delphi 中。我的解决方案涉及DLL中的以下方法:

function GetAString(): PChar;
var aString: string;
begin
  aString := 'My String';
  result := StrAlloc(length(aString) + 1);
  StrPCopy(result, aString); 
end;

procedure FreeString(aString: PChar);
begin
  StrDispose(aString); 
end;

从 Delphi EXE 中,要调用 GetAString 方法,我需要调用 GetAString 方法,将 PChar 保存到实际的 Delphi String 并调用 FreeString 方法。

这是从 FreePascal DLL 与 Delphi EXE 交换字符串的最佳方式吗?我可以避免从 Delphi 调用 FreeString 吗?

最后,如果这是正确的解决方案,默认情况下它在 Delphi 2010 和 WideString 中的表现如何:我是否也需要在 FreePascal 中强制使用 WidePChar?

最佳答案

在 DLL 和 Delphi 应用程序之间交换字符串而不使用 FreeString 调用的一种方法是从调用应用程序中获取字符串缓冲区作为 PChar,并填充DLL 中的缓冲区。这就是 Windows API 函数在需要与调用应用程序交换字符串时的工作原理。

为此,您的调用应用程序创建一个字符串缓冲区,并将引用该缓冲区的 PChar 以及缓冲区大小发送到您的 DLL 函数。如果缓冲区大小小于 DLL 必须发送到应用程序的实际字符串,则 DLL 函数可以将缓冲区实际所需的大小发送到调用应用程序。

how will it behave with Delphi 2010 and the WideString by default: do I need to force WidePChar in FreePascal too ?

在 Delphi 2009 和 Delphi 2010 中,PChar 等于 PWideChar。在以前版本的 Delphi 中,据我所知,在 FreePascal 中,PChar 等于 PAnsiChar。因此,如果您从 DLL 返回 PChar,您的代码将无法在 Delphi 2010 中正常工作。您应该显式使用 PAnsiCharPWideChar。您可以再次遵循 Windows API 函数的模式:它们几乎为每个 API 函数提供两个版本 - 一个支持 WideChar,其名称以 W 字符作为后缀,另一个版本支持 AnsiChar,其名称以 A 字符作为后缀。

您的 DLL 函数声明将如下所示:

function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean;

function AStringFuncA(Buffer: PAnsiChar; var BufferSize: Integer): Boolean;

编辑:

这里是一个示例代码:

  1. WideChar 的 DLL 函数将如下所示:

    function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
    var
      MyOutputStr : WideString;
    begin
      Result := False;
    
      // Calculate your output string here.
      MyOutputStr := 'This is a sample output';
    
      // Check if buffer is assigned, and its given length is enough
      if Assigned(Buffer) and (BufferSize >= Length(MyOutputStr) + 1) then
      begin
        // Copy output string into buffer
        StrPCopy(Buffer,MyOutputStr);
        Result := True;
      end;
      // Return actual size of output string.
      BufferSize := Length(MyOutputStr) + 1;
    end;
    

    对于 AnsiChar 版本,您使用 PAnsiChar 作为参数,并使用几乎相似的代码以 AnsiString 作为变量,或者转换参数到 PWideChar 并在 AStringFuncA 函数中调用您自己的 AStringFuncW,其结果将转换回 PAnsiChar

  2. 以下是如何在接口(interface)单元中定义这些函数以供 DLL 客户端使用:

    unit TestDLLIntf;
    
    interface
    
    const
      TestDll = 'Test.dll';
    
      function AStringFuncW(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
      function AStringFuncA(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
      function AStringFunc(Buffer: PWideChar; var BufferSize: Integer): Boolean; stdcall;
    
    implementation
    
    function AStringFuncW; external TestDll name 'AStringFuncW';
    function AStringFuncA; external TestDll name 'AStringFuncA';
    {$IFDEF UNICODE}
     function AStringFunc; external TestDll name 'AStringFuncW';
    {$ELSE}
     function AStringFunc; external TestDll name 'AStringFuncA';
    {$ENDIF}
    
    end.
    

    在上面的代码中,AStringFuncWAStringFuncA 函数都被声明为外部函数。 AStringFunc 函数在 Delphi 2009 或 2010 中引用 WideChar 版本,在旧版本中引用 AnsiChar

  3. 在这里您可以看到 DLL 客户端如何使用您的函数:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      Str : string;
      Size : Integer;
    begin
      // Retrieve required buffer size
      AStringFunc(nil,Size);
      // Set buffer
      SetLength(Str,Size);
      // Retrieve output string from DLL function.
      if AStringFunc(PChar(Str),Size) then
        ShowMessage(Str);
    end;
    

    在上面的代码中,客户端应用程序首先从AStringFunc获取实际输出大小,然后设置字符串缓冲区,并从DLL中检索输出字符串。请注意,相同的代码应该适用于 Unicode 和非 Unicode 版本的 Delphi,因为 AStringFunc 引用 DLL 内的 AStringFuncAAStringFuncW ,具体取决于您的编译器是否支持 Unicode。

关于delphi - 在 Freepascal 编译的 DLL 和 Delphi 编译的 EXE 之间交换字符串 (PChar),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2638652/

相关文章:

delphi - RichEdit 控件在成为其他控件的父控件时停止绘制文本

delphi - 无法使用删除文件命令删除文件夹

string - 环绕字符串的唯一子字符串

c# - C++ 从 C# COM DLL 调用函数

delphi - 匿名方法 - 变量捕获与值捕获

delphi - TServerSocket 和 TClientSocket

python - 在 for 循环中运行 replace() 方法?

编辑剪贴板字符串的 Javascript

C# 获取非托管 C dll 导出列表

c# - DLL 函数调用不起作用