delphi dll widestring

¿Por qué las DLL de Delphi pueden usar WideString sin usar ShareMem?



(1)

La respuesta de David a otra pregunta muestra una función DLL de Delphi que devuelve un WideString. Nunca pensé que eso fuera posible sin el uso de ShareMem .

Mi prueba DLL:

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 la que utiliza la API de Windows, por ejemplo, Windows GetClassNameW :

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

Lo que significa que la persona que llama proporciona el búfer 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 el DLL asigne la memoria, por ejemplo, utilizando LocalAlloc , y el llamador desasigna la memoria llamando a LocalFree .

¿Cómo funciona la asignación y desasignación de memoria con mi ejemplo de 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 Win32 conocida que use dicha convención?)

EDITAR:

Probé la DLL con C #.
Llamar a SomeFunction1 causa 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 .


Un WideString es lo mismo que un BSTR , es solo el nombre Delphi para él. 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.

Entonces, la razón por la que no necesita usar Sharemem es porque el montón Delphi no se está utilizando. 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 . Estas son las funciones API de BSTR proporcionadas por el sistema.

Muchas de las API de Windows a las que se refiere son anteriores a 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 con 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á dirigida a C. Y gestionar la vida útil de BSTR desde C es mucho más complicado que desde lenguajes de nivel superior como C ++, C #, Delphi, etc.

Sin embargo, hay una complicación adicional. Delphi ABI para valores de retorno WideString no es compatible con las herramientas de Microsoft. No debe usar WideString como un tipo de retorno, sino que debe devolverlo a través de un parámetro de out . Para obtener más detalles, consulte ¿Por qué no se puede utilizar WideString como valor de retorno de función para interoperabilidad?