technologies reynosa parts mexico empresa autopartes automotive aptiv delphi

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.

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 registro EAX 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;