2012-02-17 13 views
19

David's answer to another question pokazuje funkcję DLL Delphi zwracającą WideString. Nigdy nie sądziłem, że było to możliwe bez użycia ShareMem.Dlaczego biblioteki DLL Delphi mogą korzystać z WideString bez korzystania z ShareMem?

Moje testy DLL:

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

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

Mój program rozmówcy:

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; 

Działa, a ja nie rozumiem, jak to zrobić. Konwencja znam jest używany przez Windows API, na przykład w systemie Windows GetClassNameW:

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

Znaczenie rozmówcę zapewnia bufor, a maksymalna długość. Biblioteka DLL systemu Windows zapisuje do tego bufora z ograniczeniem długości. Wywołujący przydziela i zwalnia pamięć.

Inną opcją jest to, że biblioteka DLL przydziela pamięć, na przykład za pomocą LocalAlloc, a osoba wywołująca zwolni pamięć, dzwoniąc pod numer LocalFree.

jaki sposób alokacji pamięci i praca dealokacji z moim przykładzie DLL? Czy "magia" ma miejsce, ponieważ wynikiem jest WideString (BSTR)? I dlaczego nie są deklarowane interfejsy API Windows z tak wygodną konwencją? (Czy są jakieś znane API Win32, który używa takiej konwencji?)


EDIT:

testowałem DLL z C#.
Wywołanie SomeFunction1 powoduje AV (Attempted to read or write protected memory).
SomeFunction2 działa dobrze.

[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! 

Oto followup.

Odpowiedz

23

A WideString jest taki sam jak BSTR, to tylko nazwa Delphi dla tego. Przydzielanie pamięci jest obsługiwane przez udostępniony przydział COM, CoTaskMemAlloc. Ponieważ wszystkie strony używają tego samego alokatora, możesz bezpiecznie alokować w jednym module i zwolnić w innym.

Tak więc powodem, dla którego nie trzeba używać Sharemem jest to, że stos Delphi nie jest używany. Zamiast tego używana jest kupka COM. A to jest wspólne dla wszystkich modułów w procesie.

Jeśli spojrzysz na implementację Delphi WideString, zobaczysz połączenia z następującymi interfejsami API: SysAllocStringLen, SysFreeString i SysReAllocStringLen. Są to dostarczane przez system BSTR API functions.

Wiele interfejsów API systemu Windows odnieść się do pre-Date wynalezienie COM. Co więcej, istnieją zalety związane z wydajnością korzystania z bufora o stałej długości, przydzielonego przez dzwoniącego. Mianowicie, że może on zostać przydzielony na stosie zamiast sterty. Mogę sobie także wyobrazić, że projektanci systemu Windows nie chcą wymuszać na każdym procesie połączenia z OleAut32.dll i płacić ceny za utrzymanie stosu COM. Pamiętaj, że kiedy zaprojektowano większość interfejsu API systemu Windows, charakterystyka wydajności typowego sprzętu była zupełnie inna niż teraz.

Inną możliwą przyczyną nie korzystania BSTR szerzej jest to, że Windows API jest skierowany C i zarządzania żywotność BSTR od C jest bardzo dużo bardziej skomplikowane niż z wyższych językach poziomu takich jak C++, C#, Delphi itp

+0

Dzięki za odpowiedź. Spójrz [tutaj] (http://stackoverflow.com/a/5007216/937125). rozumiem, że biblioteka DLL powinna wywoływać 'CoTaskMemAlloc'. czy Delphi wywołuje to przy alokacji 'WideString'? także, kiedy wywoływane jest 'CoTaskMemFree'? – kobik

+1

Funkcje 'SysXXX' tworzą niezbędne wywołania do przydziału COM. –

+0

Link w twoim komentarzu po prostu stwierdza, że ​​element p/invoke marshaller zakłada, że ​​zwracana wartość typu 'string' została przydzielona z' CoTaskMemAlloc' i dlatego wywołuje 'CoTaskMemFree' po zakończeniu sortowania. –

Powiązane problemy