Question

David réponse aux spectacles d'une autre question, une fonction DLL Delphi retour d'un WideString. Je ne pensais jamais que c'était possible sans l'utilisation de ShareMem.

Mon DLL de test:

function SomeFunction1: Widestring; stdcall;
begin
  Result := 'Hello';
end;

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
  OutVar := 'Hello';
  Result := True;
end;

Mon programme de l'appelant:

function SomeFunction1: WideString; stdcall; external 'Test.dll';
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; external 'Test.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
  W: WideString;
begin
  ShowMessage(SomeFunction1);
  SomeFunction2(W);
  ShowMessage(W);
end;

Il fonctionne , et je ne comprends pas comment. La convention que je connaisse est celui utilisé par l'API Windows, par exemple de Windows GetClassNameW:

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;

Signification l'appelant fournit la mémoire tampon, et la longueur maximale. Windows DLL écrit à ce tampon avec la limitation de longueur. L'appelant est Alloue et désalloue la mémoire.

Une autre option est que la DLL alloue la mémoire par exemple en utilisant LocalAlloc, et l'appelant désalloue la mémoire en appelant LocalFree.

Comment l'allocation de mémoire et le travail de désallocation avec mon exemple de DLL? Est-ce que la « magie » se produit parce que le résultat est WideString (BSTR)? Et pourquoi ne sont pas des API de Windows déclaré avec une telle convention pratique? (Y at-il une API Win32 connue qui utilise cette convention?)


EDIT:

Je l'ai testé avec la DLL C #.
SomeFunction1 L'appel provoque une AV (Attempted to read or write protected memory).
SomeFunction2 fonctionne très bien.

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string SomeFunction1();

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res);

...

string s;
SomeFunction2(out s);
MessageBox.Show(s); // works ok
MessageBox.Show(SomeFunction1()); // fails with AV!

Voici un suivi.

Était-ce utile?

La solution

A WideString est identique à un BSTR, il est juste le nom Delphi pour elle. L'allocation de mémoire est gérée par l'allocateur partagé COM, CoTaskMemAlloc. Parce que toutes les parties utilisent la même allocateur vous pouvez en toute sécurité allouer dans un module et désaffecter dans un autre.

Alors, la raison pour laquelle vous n'avez pas besoin d'utiliser Sharemem est que le tas Delphi n'est pas utilisé. Au lieu de cela le tas COM est utilisé. Et qui est partagé entre tous les modules dans un processus.

Si vous regardez la mise en œuvre de Delphi WideString vous verrez les appels vers les API suivantes: SysAllocStringLen , SysFreeString et SysReAllocStringLen . Ce sont le système fourni BSTR fonctions API .

La plupart des API Windows reportez-vous à antidater l'invention de COM. De plus, il y a des avantages de performance à l'aide d'un tampon de longueur fixe, attribué par l'appelant. A savoir qu'il peut être alloué sur la pile plutôt qu'un tas. Je peux aussi imaginer que les concepteurs de Windows ne veulent pas forcer tous les processus d'avoir un lien vers OleAut32.dll et payer le prix de maintenir le tas COM. Rappelez-vous que lorsque la plupart de l'API Windows a été conçu, les caractéristiques de performance du matériel typique était très différent de maintenant.

Une autre raison possible pour ne pas utiliser BSTR plus est largement admis que l'API Windows est destiné à C. et gérer la durée de vie BSTR de C est beaucoup plus difficile que de langues de niveau supérieur comme C ++, C #, Delphi etc.

Il y a une complication supplémentaire cependant. L'ABI Delphi pour les valeurs de retour WideString ne sont pas compatibles avec les outils Microsoft. Vous ne devriez pas utiliser WideString comme type de retour, le retour à la place via un paramètre out. Pour plus de détails, voir Pourquoi une WideString pas être utilisé comme une valeur de retour de fonction pour interop?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top