J'ai, plus d'une fois, conseillé aux gens d'utiliser une valeur de retour de type WideString
à des fins d'interopérabilité.
- Accès à la DLL Delphi avec exception occasionnelle
- Une application Web ASP.NET appelant une DLL Delphi sur un serveur Web IIS se bloque lorsqu'elle renvoie une chaîne de caractères PChar.
- Pourquoi les DLL Delphi peuvent-elles 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ée sur le tas partagé de la COM, il n'y a aucun problème à allouer dans un module et à désallouer dans un autre module. En effet, toutes les parties ont convenu d'utiliser le même tas, le tas COM.
Cependant, il semble que WideString
ne peut pas être utilisé comme valeur de retour de fonction pour l'interopérabilité.
Considérons 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"%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);
L'appel à TestWideString
échoue avec cette erreur :
Exception non gérée à 0x772015de dans BSTRtest.exe : 0xC0000005 : Violation d'accès à la lecture de l'emplacement 0x00000000.
De même, si nous essayons de l'appeler depuis C# avec p/invoke, nous avons un échec :
[DllImport(@"path\to\my\dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
L'erreur est :
Une exception non gérée de type 'System.Runtime.InteropServices.SEHException' s'est produite dans ConsoleApplication10.exe.
Informations complémentaires : Un composant externe a déclenché une exception.
Appel à TestWideString
via p/invoke fonctionne comme prévu.
Il faut donc utiliser la méthode de pass-by-reference avec les paramètres WideString et les faire correspondre à BSTR
semble fonctionner parfaitement bien. Mais pas pour les valeurs de retour des fonctions. J'ai testé ceci sur Delphi 5, 2010 et XE2 et 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 comme suit :
CMP \[EAX\],EDX
Maintenant, EAX
es $00000000
et naturellement il y a une violation d'accès.
Quelqu'un peut-il l'expliquer ? Est-ce que je fais quelque chose de mal ? Suis-je déraisonnable en attendant WideString
les valeurs de la fonction pour être viable BSTR
s ? Ou s'agit-il simplement d'un défaut de Delphi ?