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 ?
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.
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.
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.