2012-02-17 21 views
19

David's answer to another question muestra una función DLL de Delphi que devuelve un WideString. Nunca pensé que eso fuera posible sin el uso de ShareMem.¿Por qué las DLL de Delphi pueden usar WideString sin usar ShareMem?

Mi DLL de prueba:

function SomeFunction1: Widestring; stdcall; 
begin 
    Result := 'Hello'; 
end; 

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; 
begin 
    OutVar := 'Hello'; 
    Result := True; 
end; 

Mi programa de llamadas:

function SomeFunction1: WideString; stdcall; external 'Test.dll'; 
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; external 'Test.dll'; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    W: WideString; 
begin 
    ShowMessage(SomeFunction1); 
    SomeFunction2(W); 
    ShowMessage(W); 
end; 

Funciona, y no entiendo cómo. La convención que conozco es el utilizado por la API de Windows, por ejemplo, Windows GetClassNameW:

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall; 

Significado la persona que llama proporciona la memoria intermedia, y la longitud máxima. La DLL de Windows escribe en ese búfer con la limitación de longitud. El llamador asigna y desasigna la memoria.

Otra opción es que la DLL asigne la memoria, por ejemplo, utilizando LocalAlloc, y la persona que llama desasigna la memoria llamando al LocalFree.

¿Cómo funciona la asignación de memoria y el trabajo desasignación con mi ejemplo DLL? ¿Se produce la "magia" porque el resultado es WideString (BSTR)? ¿Y por qué no se declaran las API de Windows con una convención tan conveniente? (¿Hay alguna API de Win32 conocidos que utiliza dicha convención?)


EDIT:

Probé la DLL con C#.
Calling SomeFunction1 provoca un AV (Attempted to read or write protected memory).
SomeFunction2 funciona bien.

[DllImport(@"Test.dll")] 
[return: MarshalAs(UnmanagedType.BStr)] 
static extern string SomeFunction1(); 

[DllImport(@"Test.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res); 

... 

string s; 
SomeFunction2(out s); 
MessageBox.Show(s); // works ok 
MessageBox.Show(SomeFunction1()); // fails with AV! 

Aquí hay un followup.

Respuesta

23

A WideString es lo mismo que BSTR, es solo el nombre Delphi. La asignación de memoria es manejada por el asignador COM compartido, CoTaskMemAlloc. Como todas las partes usan el mismo asignador, puede asignar de manera segura en un módulo y desasignar en otro.

Por lo tanto, la razón por la que no es necesario utilizar Sharemem es que no se está utilizando el montón de Delphi. En cambio, se usa el montón COM. Y eso se comparte entre todos los módulos en un proceso.

Si observa la implementación Delphi de WideString verá llamadas a las siguientes API: SysAllocStringLen, SysFreeString y SysReAllocStringLen. Estos son el sistema provisto BSTR API functions.

Muchas de las API de Windows que se refieren a la validez de la fecha de la invención de COM. Lo que es más, existen beneficios de rendimiento al usar un búfer de longitud fija, asignado por el que llama. A saber, que se puede asignar en la pila en lugar de un montón. También me puedo imaginar que los diseñadores de Windows no quieren obligar a cada proceso a vincularse al OleAut32.dll y pagar el precio de mantener el montón de COM. Recuerde que cuando se diseñó la mayor parte de la API de Windows, las características de rendimiento del hardware típico eran muy diferentes a las actuales.

Otra posible razón para no usar BSTR más ampliamente es que la API de Windows está dirigido a C. Y la gestión de la vida útil de BSTR de C es mucho más complicado que de lenguajes de alto nivel como C++, C#, Delphi, etc.

+0

Gracias por responder. Mira [aquí] (http://stackoverflow.com/a/5007216/937125). mi entendimiento es que la DLL debería llamar 'CoTaskMemAlloc'. Cómo lo llama Delphi cuando se asigna 'WideString'? también, ¿cuándo se llama el 'CoTaskMemFree'? – kobik

+1

Las funciones 'SysXXX' realizan las llamadas necesarias al asignador COM. –

+0

El enlace en su comentario simplemente indica el hecho de que el marshaller p/invoke asume que un valor de retorno de tipo 'string' fue asignado con' CoTaskMemAlloc' y por lo tanto llama a 'CoTaskMemFree' cuando ha finalizado la clasificación. –

Cuestiones relacionadas