Dlaczego WideString nie może być używany jako wartość zwracana funkcji interop?
Wielokrotnie doradzałem ludziom, aby używali wartości zwracanej typu WideString
do celów interop.
- Accessing Delphi DLL throwing ocasional exception
- ASP.NET aplikacja internetowa wywołująca Delphi DLL na serwerze IIS, blokuje się podczas zwracania ciągu PChar
- dlaczego biblioteki DLL Delphi mogą używać WideString bez korzystania z ShareMem?
Chodzi o to, że a WideString
jest tym samym, co BSTR
. Ponieważ BSTR
jest przydziela się na współdzielonej stercie COM wtedy nie ma problemu przydziela się w jednym module i dealokuje w innym module. Dzieje się tak dlatego, że wszystkie strony zgodziły się używać tej samej sterty, sterty COM.
Wydaje się jednak, że WideString
nie może być użyta jako wartość zwracana funkcji interop.
Rozważ następującą bibliotekę DLL 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.
Oraz następujący kod 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);
Nie jest to jednak możliwe, ponieważ nie jest to możliwe.]}
Nieobsługiwany wyjątek w 0x772015de w Bsttest.exe: 0xc0000005: lokalizacja odczytu naruszenia dostępu 0x0000000.
Podobnie, jeśli spróbujemy wywołać to z C # za pomocą P / invoke, mamy błąd:
[DllImport(@"path\to\my\dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
Błąd to:
Nieobsługiwany wyjątek typu ' System.Runtime.InteropServices.SEHException " wystąpił w ConsoleApplication10.exe
Dodatkowe informacje: zewnętrzny komponent wyrzucił wyjątek.
Wywołanie TestWideString
przez P / invoke działa jako oczekiwane.
Więc, użycie pass-by-reference z parametrami WideString i mapowanie ich na BSTR
wydaje się działać doskonale. Ale nie dla wartości zwracanych przez funkcję. Testowałem to na Delphi 5, 2010 i XE2 i obserwowałem to samo zachowanie we wszystkich wersjach.
Egzekucja wchodzi do Delphi i kończy się niemal natychmiast. Przypisanie do Result
zamienia się w wywołanie do System._WStrAsg
, którego pierwsza linia brzmi:
CMP [EAX],EDX
Teraz, EAX
jest $00000000
i naturalnie jest dostęp naruszenie.
WideString
będą żywotne BSTR
s? Czy to tylko wada Delphi? 2 answers
W zwykłych funkcjach Delphi, zwracanie funkcji jest w rzeczywistości parametrem przekazywanym przez referencję, mimo że składniowo wygląda i wygląda jak parametr 'out'. Możesz to przetestować w ten sposób (może to być zależne od wersji):
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;
Aby zademonstrować wywołanie TestParameterPassingMechanismOfFunctions()
Twój kod zawodzi z powodu niedopasowania między Delphi A C++rozumieniem konwencji wywołania w odniesieniu do mechanizmu przekazywania wyników funkcji. W C++ funkcja return działa jak składnia sugeruje: parametr out
. Ale dla Delphi jest to parametr var
.
Aby naprawić, spróbuj tego:
function TestWideString: WideString; stdcall;
begin
Pointer(Result) := nil;
Result := 'TestWideString';
end;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-10 10:39:20
W C#/C++ musisz zdefiniować wynik jako parametr out
, aby zachować zgodność kodu binarnego z stdcall
wywołując konwencje:
Zwracanie łańcuchów i odniesień interfejsu z funkcji DLL
W konwencji wywołania
stdcall
wynik funkcji jest przekazywany przez rejestrEAX
procesora. Jednak Visual C++ i Delphi generują inny kod binarny dla tych procedur.
Kod Delphi pozostaje taki sam:
function TestWideString: WideString; stdcall;
begin
Result := 'TestWideString';
end;
C# kod:
// declaration
[DllImport(@"Test.dll")]
static extern void TestWideString([MarshalAs(UnmanagedType.BStr)] out string Result);
...
string s;
TestWideString(out s);
MessageBox.Show(s);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-24 23:08:25