2009-08-19 8 views
6

Mam out-of-process serwera COM napisany w C++, który jest wywoływany przez jakiś kod klienta C#. Metoda na jednym z interfejsów serwera zwraca klientowi duży BSTR i podejrzewam, że powoduje to przeciek pamięci. Kod działa, ale szukam pomocy w wydzielaniu BSTR.Marshalling BSTRs od C++ do C# z interopem COM

Upraszczając nieco, IDL dla metody serwer jest

HRESULT ProcessRequest([in] BSTR request, [out] BSTR* pResponse); 

i realizacja wygląda następująco:

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse) 
{ 
    USES_CONVERSION; 
    char* pszRequest = OLE2A(request); 
    char* pszResponse = BuildResponse(pszRequest); 
    delete pszRequest; 
    *pResponse = A2BSTR(pszResponse); 
    delete pszResponse; 
    return S_OK; 
} 

A2BSTR wewnętrznie przydziela BSTR korzystając SysAllocStringLen().

W C# klienta po prostu wykonaj następujące czynności:

string request = "something"; 
string response = ""; 
myserver.ProcessRequest(request, out response); 
DoSomething(response); 

To działa, ponieważ ciągi żądanie wysyłane do serwera COM i prawidłowych ciągów odpowiedzi są zwracane do C# klienta. Jednak każda runda podróży do serwera powoduje wyciek pamięci w procesie serwera. Obsługa wykrywania przecieków crt nie wykazuje znaczących wycieków na stercie crt, więc podejrzewam, że przeciek został przydzielony za pomocą IMalloc.

Czy robię coś złego tutaj? Znalazłem niejasne komentarze, że "wszystkie parametry muszą zostać przydzielone za pomocą CoTaskMemAlloc, w przeciwnym razie interlokator nie uwolni ich", ale bez szczegółów.

Andy

+0

Dzięki za to pytanie i odpowiedzi, jak używam BSTR z obiektem COM ATL i C++. Jedną z rzeczy, którą odkryłem, było to, że jeśli podasz BSTR * jako [out] w IDL, to jeśli został pomyślnie wprowadzony BSTR *, dostaniesz wycieku pamięci. Dlatego musisz zadeklarować BSTR * jako [w, out] w pliku IDL. Zobacz http://msdn.microsoft.com/en-us/library/bdyd6xz6.aspx –

Odpowiedz

2

anelson pokrył to całkiem dobrze, ale chciałem dodać kilka punktów;

  • CoTaskMemAlloc nie jest jedynym COM-przyjazny podzielnik - BSTRs są rozpoznawane przez domyślną naziemnego i zostanie uwolniony/przeniesioną użyciu SysAllocString & przyjaciół.

  • Unikanie USES_CONVERSION (ze względu na stos ryzyka przepełnienia - patrz odpowiedź anelson użytkownika), Twój pełny kod powinien być coś takiego [1]

(zauważ, że A2BSTR jest bezpieczny w użyciu, jak to nazywa SysAllocString po konwersji, a nie używa dynamicznej alokacji stosu. można także skorzystać array-delete (delete []) jako BuildResponse prawdopodobne alokuje tablicę znaków)

  • BSTR podzielnik ma cache, które mogą się wydawać jak gdyby nastąpił wyciek pamięci. Zobacz: http://support.microsoft.com/kb/139071, aby uzyskać więcej informacji, lub Google dla OANOCACHE. Możesz spróbować wyłączyć pamięć podręczną i sprawdzić, czy "wyciek" zniknie.

[1]

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse) 
{ 
    char* pszResponse = BuildResponse(CW2A(request)); 
    *pResponse = A2BSTR(pszResponse); 
    delete[] pszResponse; 
    return S_OK; 
} 
+0

Dzięki. Wprowadziłem zmiany, które zasugerowałeś. Wygląda na to, że przeciek jest inny. –

-4

Chyba trzeba zniszczyć request z ::SysFreeString(). Ta pamięć jest przydzielana po stronie serwera.

Ponadto, OLE2A może przydzielić pamięć z powodu konwersji (spójrz). Ty też go nie uwolnisz.

+0

Dzięki za odpowiedź. Ponownego zniszczenia "prośby", myślałem, że parametry zostały przydzielone i uwolnione przez dzwoniącego? –

+0

Nie, zgodnie z regułami COM, kelner nigdy nie zwalnia parametrów [w]. – anelson

+0

OLE2A używa _alloca, która dynamicznie alokuje pamięć na stosie. Może to być niebezpieczne, jeśli łańcuch ma nieznany rozmiar, ale nie jest potrzebna ponowna alokacja - przestrzeń stosu jest odzyskiwana po zakończeniu funkcji. –

3

Nie widzę oczywistego problemu z kodem. Proponuję zmodyfikować metodę ProcessRequest wykluczyć COM współdziałanie jako źródło wycieku:

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse) 
{ 
    *psResponse = ::SysAllocStringLen(L"[suitably long string here]"); 
    return S_OK; 
} 

Podejrzewam, że nie będzie przeciekać, w którym to przypadku masz zawężony przeciek w kodzie.

Chciałbym również zauważyć, że OLE2A przydziela pamięć na stosie, więc nie powinieneś usuwać pszRequest, ale nie powinieneś w ogóle używać OLE2A, z powodu możliwości przepełnienia stosu. Aby uzyskać bezpieczniejsze alternatywy, patrz this article.

Chciałbym również proponujemy zastąpić A2BSTR z :: SysAllocString (CA2W (pszResponse))

+0

Dzięki. Wygląda na to, że wyciek nie dotyczy obsługi pamięci COM, chociaż nie jestem pewien, gdzie. Czas spróbować boundschecker ... –

Powiązane problemy