Echange de chaînes (PChar) entre une DLL compilée par Freepascal et une EXE compilée par Delphi

après beaucoup d'expérimentations, j'ai trouvé un moyen d'échanger PChar à partir D'une DLL compilée par FreePascal avec une EXE compilée par Delphi. Je suis responsable du code source DLL et EXE, mais L'un doit être à FreePascal et L'autre à Delphi. Ma solution implique les méthodes suivantes dans la 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;

et de L'EXE Delphi, pour appeler la méthode GetAString, je dois appeler la méthode GetAString, enregistrer le PChar à une chaîne de Delphi réelle et appeler le FreeString méthode.

est-ce la meilleure façon d'échanger une corde D'un FreePascal DLL avec un Delphi EXE ? Puis-je éviter L'appel à la libération de Delphi ?

et enfin, si c'est la bonne solution, comment se comportera-t-elle avec Delphi 2010 et le WideString par défaut: dois-je forcer WidePChar dans FreePascal aussi ?

4
demandé sur jonjbar 2010-04-14 19:21:13

3 réponses

une façon d'échanger des chaînes entre votre application DLL et Delphi sans utiliser FreeString call est de prendre le tampon de chaîne de l'application appelante comme un PChar, et de remplir le tampon dans la DLL. C'est ainsi que les fonctions de L'API Windows fonctionnent lorsqu'elles ont besoin d'échanger des chaînes avec des applications appelant.

pour ce faire, votre application d'appel crée un tampon de chaîne, et envoie un PChar se référant à ce tampon avec la taille du tampon à votre fonction DLL. Si la mémoire tampon la taille est plus petite que la chaîne actuelle DLL doit envoyer à l'application, votre fonction DLL peut envoyer la taille réelle requise pour le tampon à l'application appelante.

comment se comportera-t-il avec Delphi 2010 et le WideString par défaut: do I besoin de forcer WidePChar dans FreePascal trop ?

En Delphi 2009 et Delphi 2010, PChar est égal à PWideChar. Dans les versions précédentes de Delphi, et pour autant que je sache, en FreePascal, PChar égale PAnsiChar. Donc, si vous revenez PChar de votre DLL, votre code ne fonctionnera pas correctement dans Delphi 2010. Vous devez utiliser explicitement PAnsiChar ou PWideChar. Vous pouvez à nouveau suivre les fonctions de L'API Windows. Ils fournissent deux versions de nombreuses fonctions API, l'une avec le support WideChar dont le nom a un caractère W comme suffixe, et l'autre avec le support ANSI dont le nom a un caractère A comme suffixe.

votre déclaration de fonction DLL serait quelque chose comme ceci:

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

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

EDIT:

voici un exemple de code:

1-Votre fonction DLL pour widechar serait comme ceci:

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;

pour la version AnsiChar, vous pouvez utiliser soit le même code avec AnsiString et PAnsiChar, ou convertir le paramètre ANSI string en Unicode, et appeler AStringFuncW à l'intérieur de votre fonction AStringFuncA, puis convertir la chaîne de retour de AStringFuncW à PAnsiChar.

2-Voici comment vous pouvez définir ces fonctions dans votre unité d'interface pour être utilisé par vos clients 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.

dans le code ci-dessus, les fonctions AStringFuncW et AStringFuncA sont déclarées comme des fonctions externes. La fonction AStringFunc se réfère à la version WideChar dans Delphi 2009-2010, et se réfère à la version AnsiChar dans d'autres versions.

3-Ici, vous pouvez voir comment vos clients DLL peuvent utiliser votre de la fonction:

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;

dans le code ci-dessus, l'application client obtient d'abord la taille réelle de sortie D'AStringFunc, puis définit un tampon de chaîne, et récupère la chaîne de production de DLL. Prenez note que le même code devrait fonctionner à la fois dans les versions Unicode et Non-Unicode de Delphi, parce que AStringFunc se réfère soit à AStringFuncA ou AStringFuncW à l'intérieur de votre DLL selon que votre compilateur soutient Unicode ou non.

7
répondu vcldeveloper 2010-08-16 13:17:41

selon les données que vous transmettez, vous pouvez utiliser WideStrings à la place. Ils sont alloués sur le tas de fenêtres, donc si vous allouer un dans la DLL et le libérer dans L'EXE, ils vont tous les deux passer par le même gestionnaire de mémoire.

vous devriez pouvoir utiliser une déclaration de fonction comme celle-ci:

procedure GetAString(var Result: WideString); stdcall;

et Delphi et FreePascal géreront l'allocation et la libération automatiquement. WideString est le même dans le Delphis compatible Unicode comme les pré-Unicode, donc vous n'aurez pas besoin de changer les choses pour cela non plus.

2
répondu Zoë Peterson 2010-04-14 16:31:45

la règle empirique pour allouer/désallouer la mémoire dans EXE et DLL est: le module qui attribue la mémoire est responsable de la désallocation de la même mémoire. Dans votre exemple, C'est DLL qui attribue et désalloque la mémoire, donc c'est correct.

Il y a une exception à la règle générale. Delphi et les dernières versions libres Pascal mettent en œuvre WideStrings comme BSTR-un type de données chaîne qui est utilisé par COM. Les WideStrings sont attribués et libérés par Windows, pas par Delphi (ou Pascal libre) gestionnaire de mémoire. Il n'est donc pas nécessaire d'utiliser PWideChar pour passer des arguments de WideString entre EXE et DLL - WideStrings peuvent être passés directement.


mise à Jour:

j'ai testé le code suivant:

Free Pascal (version 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 (main form):

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.

Il est apparu que la procédure GetAStringProc fonctionne bien tandis que la fonction GetAStringFunc mène à la violation d'accès. Delphi et Pascal libre semblent rendre le résultat du type string différemment.

2
répondu kludg 2010-04-15 11:21:42