2010-04-12 13 views
6

Tengo un DLL que proporciona una función de decodificación, como sigue:Pre-asignar memoria entre HostApp y DLL

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

El HostApp llaman "MyDecode", y la transfiere en los parámetros de la fuente, SourceLen y Dest, la La DLL devuelve Dest y DestLen decodificados. El problema es que la HostApp no ​​puede saber la longitud del Dest decodificada, y por lo tanto no sabría cómo preasignar la memoria de Dest.

sé que puede dividir "MyDecode" en dos funciones:

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

Pero, Mi proceso de decodificación es muy complicado, por lo que si se divide en dos funciones afectará a la eficacia.

¿Existe una solución mejor?


Sí Alexander, esta puede ser una buena solución. código HostApp:

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

código 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; 

Tal vez debería cambiar el tipo de Dest al puntero.

¿Cuál es un mejor método de memoria asignada? GetMem/New o HeapAlloc?

+0

¿Qué problema está tratando de resolver ?: 1) cómo calcular la cantidad a asignar por adelantado; 2) cómo coordinar la gestión de la memoria dinámica entre la persona que llama y la persona que llama? –

+0

Para Marcelo: 1) La persona que llama no puede determinar la cantidad a asignar por adelantado. 2) Sí. – Leo

+0

> ¿Cuál es un mejor método de memoria asignada? En su caso, esto no importa. Utiliza el método con el que estás familiarizado (prefiero GetMem/FreeMem). – Alex

Respuesta

8

Puede dividir "MyDecode" en dos rutinas por la otra manera:

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

es decir, - asigna memoria en MyDecode en lugar de pedirle a una persona que llama que haga eso.

+0

Tenga en cuenta que no puede implementar FreeDecodeResult, si usa algún asignador común tanto para el llamador como para el destinatario. Por ejemplo, si asigna memoria a través de LocalAlloc en lugar de GetMem. Entonces la persona que llama debe llamar a LocalFree en lugar de FreeDecodeResult. – Alex

5

Puede utilizar la misma técnica que usa la mayoría del API de Windows, es decir, si su memoria intermedia no es lo suficientemente grande, la función devuelve el tamaño del buffer necesario. De esta forma, puede asignar un búfer del tamaño correcto desde la función de llamada.

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; 

EDITAR: Como sugiere mghie. Dado que el parámetro es PCHAR, se asumirá que iLenNeeded devuelto por MyDecode será el número de TCHAR requerido como es (¿sobre todo?) Estándar por la API de 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; 
+0

+1, pero esto tiene cierto potencial de confusión, ya que 'Len' podría significar la longitud del búfer o la longitud de la cadena. Para estar seguro, no usaría 'GetMem()' sino 'SetLength()' en una cadena, esto se haría cargo del carácter nulo posterior. En cuanto a la eficiencia: si el archivo DLL almacena en caché los datos decodificados en un 'threadvar', no necesita ser menos eficiente. – mghie

+0

Dependiendo de la implementación exacta de MyDecode, usar pchar podría no ser la mejor idea tampoco ... Pero lo editaré y mostraré un uso alternativo. –

+0

Sí, sé que esta es la solución API de Windows. Puede ser una mejor solución si la función dll es simple. Pero mi "MyDecode" es una función que consume mucho tiempo, así que no quiero llamarlo dos veces. – Leo

2

Otra opción es pasar al dll un puntero a la función para asignar memoria. El dll llama a esta función cuando necesita memoria y dado que la memoria se asigna utilizando el administrador de memoria de la aplicación, la aplicación puede liberarla.

Desafortunadamente, esto no soluciona realmente su problema, pero solo lo mueve a la dll, que luego debe determinar cuánta memoria necesita. Tal vez podría usar varios almacenamientos intermedios almacenados en una lista vinculada, de modo que cada vez que la función de decodificación se quede sin memoria, solo asigna otro almacenamiento intermedio.

1

no estoy seguro si esto le conviene, pero (en este particular, ejemplo) se puede utilizar WideString:

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

O:

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

Mediante el uso que puede WideString evitar problemas de asignación de memoria en absoluto.

¿Por qué funciona? Porque WideString es un alias para el tipo de sistema BSTR. Y BSTR tiene regla especial: su memoria debe asignarse a través del administrador de memoria del sistema específico. Es decir. cuando trabajas con WideString, Delphi llama a este administrador de memoria del sistema y no al suyo. Como el administrador de memoria del sistema es accesible desde cada módulo (y es el mismo para cada módulo), esto significa que tanto el llamador (exe) como el destinatario (DLL) usarán el mismo administrador de memoria, lo que les permitirá pasar datos sin problemas.

Entonces, puede usar WideString y solo producir resultados sin preocuparse por la memoria. Solo tenga en cuenta que los caracteres en WideString son unicode, es decir, 2 bytes. Tendrá un poco de sobrecarga al convertir ANSI < -> unicode, si está utilizando D2007 y siguientes. Esto (generalmente) no es un problema, ya que la aplicación típica genera toneladas de llamadas WinAPI, y cada llamada WinAPI significa la misma conversión ANSI < -> unicode (ya que está llamando a funciones A).

+0

¿funcionaría eso incluso si hostapp no ​​es un programa delphi? –

+0

Sí, dado que WideString es BSTR, la aplicación no Delphi necesitará usar BSTR, que es el tipo de sistema (en realidad, un tipo COM). Vea aquí: http://msdn.microsoft.com/en-us/library/ms221069(VS.100).aspx Ejemplo de uso de BSTR desde C++: http://msdn.microsoft.com/en- us/library/xda6xzx7 (VS.100) .aspx – Alex

Cuestiones relacionadas