2010-04-12 8 views
6

Ich habe eine DLL, die eine Decodierungsfunktion vorgesehen, wie folgt:vorbelegen Speicher zwischen HostApp und DLL

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

Die HostApp nennen „MyDecode“ und übertragen in die Quelle, SourceLen und Dest Parameter, die DLL gibt decodiertes Dest und DestLen zurück. Das Problem ist: Die HostApp unmöglich decodierte Dest-Länge zu wissen, und daher nicht wissen, wie Dest Speicher vorab zugewiesen werden.

Ich weiß, dass „MyDecode“ in zwei Funktionen aufgeteilt werden:

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

Aber, Mein Decodierungsprozess sehr kompliziert, so dass, wenn in zwei Funktionen aufgeteilt wird die Effizienz beeinflussen.

Gibt es eine bessere Lösung?


Ja Alexander, das kann eine gute Lösung sein. HostApp Code:

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

DLL-Code:

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; 

Vielleicht sollte ich Dest den Typ Pointer ändern.

Welches ist eine bessere Speicherzuweisungsmethode? GetMem/New oder HeapAlloc?

+0

Welches Problem versuchen Sie zu lösen: 1) wie Sie den im Voraus zuzuteilenden Betrag ermitteln; 2) Wie man dynamische Speicherverwaltung zwischen Anrufer und Angerufenem koordiniert? –

+0

Um Marcelo: 1) Anrufer kann den Betrag im Voraus nicht zuordnen. 2) Ja. – Leo

+0

> Was ist eine bessere Speicherzuweisungsmethode? In Ihrem Fall spielt dies keine Rolle. Verwenden Sie die Methode, mit der Sie vertraut sind (Ich bevorzuge GetMem/FreeMem). – Alex

Antwort

8

Sie können durch andere Weise "MyDecode" in zwei Routinen aufgeteilt:

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

D.h. - Sie ordnen Speicher in MyDecode zu, anstatt einen Anrufer zu bitten, dies zu tun.

+0

Beachten Sie, dass Sie FreeDecodeResult nicht implementieren können, wenn Sie einen gemeinsamen Allokator für Aufrufer und Aufrufer verwenden. Zum Beispiel, wenn Sie Speicher über LocalAlloc statt GetMem zuweisen. Dann sollte der Anrufer LocalFree anstelle von FreeDecodeResult aufrufen. – Alex

5

Sie können die gleiche Technik verwenden, die die meisten Windows-API verwenden, dh wenn Ihr Puffer nicht groß genug ist, gibt die Funktion die Größe des benötigten Puffers zurück. Auf diese Weise können Sie einen Puffer der richtigen Größe von der aufrufenden Funktion zuweisen.

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; 

EDIT: Wie mghie vorgeschlagen. Da der Parameter PCHAR ist, wird angenommen, dass die von MyDecode zurückgegebene iLenNeeded die Anzahl der erforderlichen TCHAR ist, wie es (meistens?) Standard von der Windows-API ist.

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; 
+0

+1, aber dies hat etwas Verwirrungspotential, da 'Len' entweder Pufferlänge oder Stringlänge bedeuten kann. Um auf der sicheren Seite zu sein, würde ich 'GetMem()' außer 'SetLength()' in einer Zeichenkette verwenden, dies würde sich um das nachfolgende Nullzeichen kümmern. Zur Effizienz: Wenn die DLL die dekodierten Daten in einer 'threadvar' zwischenspeichert, muss sie nicht weniger effizient sein. – mghie

+0

Abhängig von der genauen Implementierung von MyDecode ist die Verwendung von pchar möglicherweise nicht die beste Idee ... Aber ich werde es bearbeiten und eine alternative Verwendung anzeigen. –

+0

Ja, ich weiß, dass dies die Windows-API-Lösung ist. Es kann eine beste Lösung sein, wenn die DLL-Funktion einfach ist. Aber mein "MyDecode" ist eine zeitaufwendige Funktion, also möchte ich es nicht zweimal anrufen. – Leo

2

Eine andere Option ist, in die DLL einen Funktionszeiger zu übergeben, um Speicher zuzuweisen. Die DLL ruft diese Funktion auf, wenn sie Speicher benötigt, und da der Speicher über den Speichermanager der Anwendung zugewiesen wird, kann die Anwendung sie einfach freigeben.

Leider löst dies Ihr Problem nicht wirklich, sondern verschiebt es nur auf die DLL, die dann herausfinden muss, wie viel Speicher sie benötigt. Vielleicht könnten Sie mehrere Puffer verwenden, die in einer verknüpften Liste gespeichert sind, so dass jedes Mal, wenn die Dekodierungsfunktion nicht genügend Speicher hat, nur ein weiterer Puffer zugewiesen wird.

1

Ich bin mir nicht sicher, ob dies zu Ihnen passen, aber (in diesem insbesondere Beispiel) können Sie Wide verwenden:

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

Oder:

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

Durch die Verwendung von Wide Sie können Vermeiden Sie Speicherzuweisungsprobleme überhaupt.

Warum das funktioniert? Da WideString ein Alias ​​für den Systemtyp BSTR ist. Und BSTR haben spezielle Regel: Ihr Speicher muss über spezifische Systemspeicher-Manager zugewiesen werden. I.e. Wenn Sie mit WideString arbeiten, ruft Delphi diesen Systemspeichermanager auf und nicht seinen eigenen. Da der Systemspeichermanager von jedem Modul aus zugreifbar ist (und für jedes Modul gleich ist), bedeutet dies, dass sowohl der Aufrufer (exe) als auch der Aufrufer (DLL) den gleichen Speichermanager verwenden, so dass sie Daten problemlos weiterleiten können.

So können Sie WideString verwenden und nur Ergebnisse produzieren, ohne sich Gedanken über Speicher machen zu müssen. Beachten Sie, dass die Zeichen in WideString Unicode sind - also 2 Bytes. Sie werden mit ANSI < -> Unicode ein wenig Overhead haben, wenn Sie D2007 und darunter verwenden. Dies ist (normalerweise) kein Problem, da eine typische Anwendung eine Unzahl von WinAPI-Aufrufen erzeugt - und jeder WinAPI-Aufruf dieselbe ANSI < -> Unicode-Konvertierung bedeutet (da Sie A-Funktionen aufrufen).

+0

würde das funktionieren, selbst wenn Hostapp kein Delphi-Programm ist? –

+0

Ja, da WideString BSTR ist, muss Nicht-Delphi-Anwendung BSTR verwenden, was Systemtyp ist (eigentlich ein COM-Typ). Siehe hier: http://msdn.microsoft.com/en-us/library/ms221069(VS.100).aspx Beispiel für die Verwendung von BSTR aus C++: http://msdn.microsoft.com/en- us/library/xda6xzx7 (VS.100) .aspx – Alex

Verwandte Themen