reynosa - delphi technologies mexico
¿Por qué no se puede utilizar WideString como valor de retorno de función para interoperabilidad? (2)
En más de una ocasión, he aconsejado a las personas que utilicen un valor de retorno de tipo WideString
para fines de interoperabilidad.
- Acceder a la DLL de Delphi arrojando una excepción ocasional
- La aplicación web ASP.NET que llama a Delphi DLL en el servidor web IIS, se bloquea al devolver la cadena PChar
- ¿Por qué las DLL de Delphi pueden usar WideString sin usar ShareMem?
La idea es que un WideString
es lo mismo que un BSTR
. Debido a que un BSTR
se asigna en el montón COM compartido, entonces no es un problema asignarlo en un módulo y desasignar en un módulo diferente. Esto se debe a que todas las partes han acordado usar el mismo montón, el montón COM.
Sin embargo, parece que WideString
no se puede usar como un valor de retorno de función para la interoperabilidad.
Considere la siguiente DLL de Delphi.
library WideStringTest;
uses
ActiveX;
function TestWideString: WideString; stdcall;
begin
Result := ''TestWideString'';
end;
function TestBSTR: TBstr; stdcall;
begin
Result := SysAllocString(''TestBSTR'');
end;
procedure TestWideStringOutParam(out str: WideString); stdcall;
begin
str := ''TestWideStringOutParam'';
end;
exports
TestWideString, TestBSTR, TestWideStringOutParam;
begin
end.
y el siguiente código C ++:
typedef BSTR (__stdcall *Func)();
typedef void (__stdcall *OutParam)(BSTR &pstr);
HMODULE lib = LoadLibrary(DLLNAME);
Func TestWideString = (Func) GetProcAddress(lib, "TestWideString");
Func TestBSTR = (Func) GetProcAddress(lib, "TestBSTR");
OutParam TestWideStringOutParam = (OutParam) GetProcAddress(lib,
"TestWideStringOutParam");
BSTR str = TestBSTR();
wprintf(L"%s/n", str);
SysFreeString(str);
str = NULL;
TestWideStringOutParam(str);
wprintf(L"%s/n", str);
SysFreeString(str);
str = NULL;
str = TestWideString();//fails here
wprintf(L"%s/n", str);
SysFreeString(str);
La llamada a TestWideString
falla con este error:
Excepción no controlada en 0x772015de en BSTRtest.exe: 0xC0000005: ubicación de lectura de violación de acceso 0x00000000.
Del mismo modo, si tratamos de llamar esto desde C # con p / invoke, tenemos un error:
[DllImport(@"path/to/my/dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
El error es:
Se produjo una excepción no controlada del tipo ''System.Runtime.InteropServices.SEHException'' en ConsoleApplication10.exe
Información adicional: componente externo ha lanzado una excepción.
Llamar a TestWideString
través de p / TestWideString
funciona como se espera.
Entonces, use pass-by-reference con los parámetros de WideString y mapearlos en BSTR
parece funcionar perfectamente. Pero no para valores de retorno de función. He probado esto en Delphi 5, 2010 y XE2 y observo el mismo comportamiento en todas las versiones.
La ejecución ingresa a Delphi y falla casi de inmediato. La asignación al Result
convierte en una llamada a System._WStrAsg
, System._WStrAsg
primera línea dice:
CMP [EAX],EDX
Ahora, EAX
es $00000000
y, naturalmente, hay una violación de acceso.
¿Alguien puede explicar esto? ¿Estoy haciendo algo mal? ¿No soy razonable al esperar que los valores de la función WideString
sean BSTR
s viables? ¿O es solo un defecto de Delphi?
En C # / C ++, necesitará definir el parámetro Resultado como out
, para mantener la compatibilidad del código binario de las convenciones de llamadas stdcall
:
Devolución de cadenas y referencias de interfaz desde funciones de DLL
En la convención de llamadas
stdcall
, el resultado de la función se pasa a través del registroEAX
la CPU. Sin embargo, Visual C ++ y Delphi generan diferentes códigos binarios para estas rutinas.
El código Delphi permanece igual:
function TestWideString: WideString; stdcall;
begin
Result := ''TestWideString'';
end;
C # code:
// declaration
[DllImport(@"Test.dll")]
static extern void TestWideString([MarshalAs(UnmanagedType.BStr)] out string Result);
...
string s;
TestWideString(out s);
MessageBox.Show(s);
En las funciones regulares de Delphi, el retorno de la función es en realidad un parámetro pasado por referencia, aunque sintácticamente se ve y se siente como un parámetro ''fuera''. Puede probar esto como tal (puede depender de la versión):
function DoNothing: IInterface;
begin
if Assigned(Result) then
ShowMessage(''result assigned before invocation'')
else
ShowMessage(''result NOT assigned before invocation'');
end;
procedure TestParameterPassingMechanismOfFunctions;
var
X: IInterface;
begin
X := TInterfaceObject.Create;
X := DoNothing;
end;
Para demostrar llamada TestParameterPassingMechanismOfFunctions()
Su código está fallando debido a un desajuste entre la comprensión de Delphi y C ++ de la convención de llamadas en relación con el mecanismo de aprobación para los resultados de las funciones. En C ++, un retorno de función actúa como sugiere la sintaxis: un parámetro de out
. Pero para Delphi es un parámetro var
.
Para solucionarlo, intente esto:
function TestWideString: WideString; stdcall;
begin
Pointer(Result) := nil;
Result := ''TestWideString'';
end;