Pourquoi un WideString ne peut-il pas être utilisé comme valeur de retour de fonction pour interop?
j'ai, en plus d'une occasion, conseillé aux gens d'utiliser une valeur de retour de type WideString
pour l'interopérabilité des fins.
- accéder à Delphi DLL exception ocasional
- ASP.NET application web appelant Delphi DLL sur IIS webserver, verrouille lors de la restitution chaîne de caractères PChar
- pourquoi Delphi DLLs utiliser WideString sans utiliser ShareMem?
l'idée est qu'un WideString
est la même chose qu'un BSTR
. Parce qu'un BSTR
est alloué sur le tas de COM partagée alors il n'y a pas de problème pour allouer dans un module et désallouer dans un autre module. C'est parce que toutes les parties ont convenu d'utiliser le même tas, la COM tas.
cependant, il semble que WideString
ne puisse pas être utilisé comme valeur de retour de fonction pour Interop.
tenir compte de la DLL Delphi suivante.
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.
et le code C++ suivant:
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"%sn", str);
SysFreeString(str);
str = NULL;
TestWideStringOutParam(str);
wprintf(L"%sn", str);
SysFreeString(str);
str = NULL;
str = TestWideString();//fails here
wprintf(L"%sn", str);
SysFreeString(str);
l'appel à TestWideString
échoue avec cette erreur:
exception non Gérée à 0x772015de dans BSTRtest.exe: 0xC0000005: lieu de lecture de la violation de L'accès 0x00000000.
de même, si nous essayons d'appeler cela de C# avec P/invoquer, nous avoir un échec:
[DllImport(@"pathtomydll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
l'erreur est:
Une exception non gérée du type 'System.Runtime.InteropServices.La SEHException s'est produite dans la ConsoleApplication10.exe
information supplémentaire: le volet externe a fait exception.
Appel TestWideString
par l'intermédiaire de p/invoke fonctionne comme prévu.
Ainsi, l'utilisation par référence avec Élargir les paramètres et les cartographier sur BSTR
semble fonctionner parfaitement. Mais pas pour les valeurs de retour de fonction. J'ai testé cela sur Delphi 5, 2010 et XE2 et j'observe le même comportement sur toutes les versions.
L'exécution entre dans le Delphi et échoue presque immédiatement. L'affectation à Result
se transforme en un appel à System._WStrAsg
, dont la première ligne se lit:
CMP [EAX],EDX
maintenant, EAX
est "1519150920"000000
et naturellement, il y a une violation d'accès.
quelqu'un Peut-il expliquer cela? Suis-je en train de faire quelque chose de mal? Suis-je déraisonnable en m'attendant à ce que les valeurs de fonction WideString
soient viables BSTR
s? Ou est-ce juste un défaut de Delphi?
2 réponses
dans les fonctions Delphi régulières, le retour de la fonction est en fait un paramètre passé par référence, même si syntaxiquement il ressemble et se sent comme un paramètre 'out'. Vous pouvez le tester comme ceci (cela peut dépendre de la version):
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;
pour démontrer l'appel TestParameterPassingMechanismOfFunctions()
votre code est défaillant en raison d'un décalage entre la compréhension de Delphi et C++de la convention d'appel en relation avec le mécanisme de passage pour résultats de la fonction. Dans C++ Un Retour de fonction agit comme la syntaxe suggère: un paramètre out
. Mais pour Delphi, c'est un paramètre var
.
pour corriger, essayez ceci:
function TestWideString: WideString; stdcall;
begin
Pointer(Result) := nil;
Result := 'TestWideString';
end;
en C# / C++ vous devrez définir le résultat comme out
paramètre, afin de maintenir la compatibilité de code binaire de stdcall
conventions d'appel:
renvoi de chaînes et de références D'Interface des fonctions DLL
dans la convention d'appel
stdcall
, le résultat de la fonction est transmis via le registreEAX
du CPU. Cependant, Visual C++ et Delphi génèrent des code binaire pour ces routines.
le code Delphi reste le même:
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);