6 votes

Pré-allocation de mémoire entre HostApp et DLL

J'ai une DLL qui fournit une fonction de décodage, comme suit :

function MyDecode (Source: PChar; SourceLen: Integer; var Dest: PChar; DestLen: Integer): Boolean; stdcall; 

Le HostApp appelle "MyDecode", et transfère dans les paramètres Source, SourceLen et Dest, la DLL renvoie les paramètres décodés Dest et DestLen. Le problème est le suivant : L'application hôte ne peut pas connaître la longueur de Dest décodé, et ne sait donc pas comment pré-allouer la mémoire de Dest.

Je sais que l'on peut diviser "MyDecode" en deux fonctions :

function GetDecodeLen (Source: PChar; SourceLen: Integer): Integer; stdcall;  // Return the Dest's length
function MyDecodeLen (Source: PChar; SourceLen: Integer; var Dest: PChar): Boolean; stdcall; 

Mais mon processus de décodage est très compliqué, donc s'il est divisé en deux fonctions, l'efficacité en sera affectée.

Existe-t-il une meilleure solution ?


Oui, Alexander, c'est peut-être une bonne solution. Code HostApp :

//... 
MyDecode(....) 
try 
  // Use or copy Dest data 
finally 
  FreeDecodeResult(...) 
end;

Code DLL :

function MyDecode(...): Boolean;
begin
  // time-consuming calculate

  // Allocate memory
  GetMem(Dest, Size);   
  // or New()?
  // or HeapAlloc()?
end;

procedure FreeDecodeResult(Dest: PChar);
begin
  FreeMem(Dest);
  // or Dispose(Dest); ?
  // or HeapFree(Dest); ?
end;

Je devrais peut-être changer le type de Dest en Pointeur.

Quelle est la meilleure méthode d'allocation de mémoire ? GetMem/New ou HeapAlloc ?

8voto

Alexander Points 3468

Vous pouvez diviser "MyDecode" en deux routines d'une autre manière :

function  MyDecode(Source: PChar; SourceLen: Integer; out Dest: PChar; out DestSize: Integer): Boolean; stdcall;
procedure FreeDecodeResult(Dest: PChar); stdcall;

C'est-à-dire que vous allouez de la mémoire dans MyDecode au lieu de demander à l'appelant de le faire.

5voto

Ken Bourassa Points 2816

Vous pouvez utiliser la même technique que la plupart des API Windows, c'est-à-dire que si votre tampon n'est pas assez grand, la fonction renvoie la taille du tampon nécessaire. De cette manière, vous pouvez allouer un tampon de la bonne taille à partir de la fonction appelante.

function MyDecode (Source: PChar; SourceLen: Integer; Dest: PChar; var Len: Integer): Boolean; stdcall;

procedure SomeProc;
var iSourceLen, iLenNeeded : Integer;
    pSource, pDest : Pointer;
begin
  MyDecode(pSource, iSourceLen, nil, iLenNeeded);
  GetMem(pDest,iLenNeeded);
  try
    MyDecode(pSource, iSourceLen,pDest, iLenNeeded);
  finally
    FreeMem(pDest);
  end;
end;

E : Comme suggéré par mghie. Puisque le paramètre est un PCHAR, on supposera que l'iLenNeeded retourné par MyDecode sera le nombre de TCHAR requis, comme c'est (généralement ?) le cas dans l'API Windows.

function SomeProc(sEncode : String) : string;
var iLenNeeded : Integer;
begin
  MyDecode(PChar(sEncode), Length(sEncode), nil, iLenNeeded);
  SetLength(Result, iLenNeeded);  //if iLenNeeded include a null-terminating character, you can use (iLenNeeded - 1) instead
  if not MyDecode(PChar(sEncode), Length(sEncode), PChar(Result), iLenNeeded) then
    SetLength(Result, 0);
end;

2voto

dummzeuch Points 4579

Une autre option consiste à transmettre à la dll un pointeur de fonction pour allouer de la mémoire. La dll appelle cette fonction lorsqu'elle a besoin de mémoire et, comme la mémoire est allouée à l'aide du gestionnaire de mémoire de l'application, l'application peut simplement la libérer.

Malheureusement, cela ne résout pas vraiment votre problème, mais le déplace vers la dll qui doit alors déterminer la quantité de mémoire dont elle a besoin. Vous pourriez peut-être utiliser plusieurs tampons stockés dans une liste liée, de sorte qu'à chaque fois que la fonction de décodage manque de mémoire, elle alloue simplement un autre tampon.

1voto

Alexander Points 3468

Je ne sais pas si cela vous conviendra, mais (dans cette particulier ), vous pouvez utiliser WideString :

function MyDecode(Source: PChar; SourceLen: Integer; out Dest: WideString): Boolean; stdcall;

Ou bien :

function MyDecode(Source: PChar; SourceLen: Integer): WideString; stdcall;

L'utilisation de WideString permet d'éviter tout problème d'allocation de mémoire.

Pourquoi cela fonctionnera-t-il ? Parce que WideString est un alias du type BSTR du système. Et BSTR a une règle spéciale : sa mémoire doit être allouée par le biais d'un gestionnaire de mémoire système spécifique. Ainsi, lorsque vous travaillez avec WideString, Delphi fait appel à ce gestionnaire de mémoire système et non à son propre gestionnaire. Étant donné que le gestionnaire de mémoire système est accessible à partir de chaque module (et qu'il est le même pour chaque module), cela signifie que l'appelant (exe) et l'appelé (DLL) utiliseront le même gestionnaire de mémoire, ce qui leur permettra de transmettre des données sans problème.

Vous pouvez donc utiliser WideString et produire des résultats sans vous soucier de la mémoire. Notez simplement que les caractères dans WideString sont unicode - c'est-à-dire 2 octets. Vous aurez un peu de surcharge avec la conversion ANSI<->unicode, si vous utilisez D2007 et moins. Ce n'est (généralement) pas un problème, car une application typique fait une tonne d'appels WinAPI - et chaque appel WinAPI implique la même conversion ANSI<->unicode (puisque vous appelez des fonctions A).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X