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

oymdgrw7  于 2022-11-29  发布在  其他
关注(0)|答案(3)|浏览(156)

经过大量的实验,我找到了一种方法来交换PChar从FreePascal编译的DLL与 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字符串并调用FreeString方法。
这是用 Delphi EXE交换来自FreePascal DLL的字符串的最好方法吗?我可以避免从Delphi调用FreeString吗?
最后,如果这是正确的解决方案,它在 Delphi 2010和WideString的默认情况下将如何表现:我需要在FreePascal中强制使用WidePChar吗?

pxy2qtax

pxy2qtax1#

在DLL和 Delphi 应用程序之间交换字符串而不使用FreeString调用的一种方法是从调用应用程序中获取一个字符串缓冲区作为PChar,并将该缓冲区填充到DLL中。这就是Windows API函数在需要与调用应用程序交换字符串时的工作方式。
为此,调用应用程序创建一个字符串缓冲区,并将引用该缓冲区的PChar以及缓冲区大小发送给DLL函数。如果缓冲区大小小于DLL必须发送给应用程序的实际字符串,则DLL函数可以将实际需要的缓冲区大小发送给调用应用程序。
默认情况下,它在 Delphi 2010和WideString中的表现如何:我需要在FreePascal中强制使用WidePChar吗?
在 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
1.下面是如何在接口单元中定义这些函数以供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
1.在这里,您可以看到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。

lyfkaqu1

lyfkaqu12#

根据你传递的数据,你可以使用WideString。它们被分配在Windows堆上,所以如果你在DLL中分配一个并在EXE中释放它,它们都将通过同一个内存管理器。
您应该能够使用如下所示的函数声明:

procedure GetAString(var Result: WideString); stdcall;

Delphi 和FreePascal都将自动处理分配和释放。WideString在支持Unicode的Delphi中和在Unicode之前的Delphi中是一样的,所以你也不需要为此做任何改变。

s8vozzvw

s8vozzvw3#

在EXE和DLL中分配/释放内存的经验法则是:分配内存的模块负责释放相同的内存。2在你的例子中,是DLL分配和释放内存,所以它是正确的。
有一个例外。 Delphi 和最新的Free Pascal版本都将WideString实现为BSTR -一种由COM使用的字符串数据类型。WideString是由Windows分配和释放的,而不是由Delphi(或Free Pascal)内存管理器分配和释放的。因此不需要使用PWideChar在EXE和DLL之间传递WideString参数-WideString可以直接传递。
更新日期:
我已经测试了以下代码:
免费Pascal(版本2.2.4)DLL:

library TestLib1;

{$mode objfpc}{$H+}

{$IFDEF WINDOWS}{$R TestLib1.rc}{$ENDIF}

procedure GetAStringProc(var S: WideString); stdcall;
begin
  S:= '12345';
end;

function GetAStringFunc: WideString; stdcall;
begin
  Result:= '12345';
end;

exports
  GetAStringProc, GetAStringFunc;

begin
end.

Delphi 2009 EXE(主窗体):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    S: WideString;
  end;

var
  Form1: TForm1;

type
  TGetAStringProc = procedure(var S: WideString); stdcall;
  TGetAStringFunc = function: WideString; stdcall;

var
  GetAStringProc: TGetAStringProc;
  GetAStringFunc: TGetAStringFunc;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
    GetAStringProc(S);
    Caption:= S;
    S:= '';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    S:= GetAStringFunc;
    Caption:= S;
    S:= '';
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  LibHandle: THandle;

begin
  LibHandle:= LoadLibrary('TestLib1.dll');
  if LibHandle <> 0 then begin
    @GetAStringProc:= GetProcAddress(LibHandle, 'GetAStringProc');
    @GetAStringFunc:= GetProcAddress(LibHandle, 'GetAStringFunc');
  end;
  if not Assigned(GetAStringProc) or not Assigned(GetAStringFunc) then
      ShowMessage('Error!');
end;

end.

过程GetAStringProc运行良好,而函数GetAStringFunc导致访问冲突。 Delphi 和Free Pascal返回字符串类型的结果似乎不同。

相关问题