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.
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
Las funciones 'SysXXX' realizan las llamadas necesarias al asignador COM. –
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. –