Pregunta

La respuesta de

David a espectáculos otra pregunta una función Delphi DLL devolver un WideString. Nunca pensé que eso era posible sin el uso de 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 la que utiliza 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 a la memoria intermedia con la limitación de longitud. La persona que llama es asigna y desasigna la memoria.

Otra opción es que el DLL de asignar la memoria, por ejemplo, mediante el uso de LocalAlloc, y el llamador desasigna la memoria llamando LocalFree.

¿Cómo funciona la asignación de memoria y el trabajo desasignación con mi ejemplo DLL? ¿La "magia" ocurre porque el resultado es WideString (BSTR)? Y por qué no son Windows API declara con dicha convención conveniente? (¿Hay alguna conocida Win32 API que utiliza dicha convención?)


EDIT:

He probado el DLL con C #.
Llamando 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í está una seguimiento .

¿Fue útil?

Solución

A WideString es lo mismo que un BSTR, es sólo el nombre de Delphi para ello. La asignación de memoria es manejada por el asignador COM compartida, CoTaskMemAlloc. Debido a que todas las partes utilizan el mismo asignador puede asignar de forma segura en un módulo y desasignar en otro.

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

Si nos fijamos en la implementación de Delphi WideString verá las llamadas a las API siguientes: SysAllocStringLen , SysFreeString y SysReAllocStringLen . Estos son el sistema proporcionado BSTR funciones API .

Muchas de las API de Windows que se refiere a la validez de la fecha de la invención de COM. Lo que es más, hay ventajas de rendimiento en el uso de un buffer de longitud fija, asignada por la persona que llama. A saber, que puede ser asignado en la pila en lugar de un montón. También puedo imaginar que los diseñadores de Windows no quieren obligar a todos los procesos que tienen que enlace a OleAut32.dll y pagar el precio de mantener el COM montón. Recuerde que cuando la mayor parte de la API de Windows fue diseñado, las características de rendimiento del hardware típico era muy distinta de la actual.

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 el de las lenguas de mayor nivel, como C ++, C #, Delphi, etc.

Hay una complicación adicional, sin embargo. El ABI Delphi para valores de retorno WideString no es compatible con las herramientas de Microsoft. No debe usar WideString como un tipo de retorno, que la devolverá a través de un parámetro out. Para más detalles ver Por qué puede un WideString no debe utilizarse como un valor devuelto por una función de interoperabilidad?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top