2013-03-15 17 views
5

Dal libro ATL Internals, sapevo che BSTR è diverso da OLECHAR * e ci sono CComBSTR e CString per BSTR.Trattiamo il tipo di BSTR in COM come valore o riferimento?

Secondo MSDN Allocating and Releasing Memory for a BSTR, conoscevo la responsabilità di gestione della memoria per il chiamante/chiamato.

Prendere questa linea da MSDN,

HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)

io ancora non so come gestire correttamente bstr nella mia implementazione. Poiché ho ancora una domanda di base per BSTR - dovremmo trattare bstr come un valore (come int) o come riferimento (come int *), almeno sul limite dell'interfaccia COM.

Voglio convertire BSTR il più presto possibile a CString/CComBSTR nella mia implementazione. La semantica di valore o di riferimento sarà totalmente diversa per la conversione. Ho inserito CComBSTR.Attach, CComBSTR.AssignBSTR, ecc. Ma il codice non può risolvere i miei dubbi.

MSDN CComBSTR.Attach ha qualche codice di snip, mi sembra che sia sbagliato poiché non è conforme allo Allocating and Releasing Memory for a BSTR. ATL Internals ha detto che SetSysString "libererà il BSTR originale passato", se lo usassi, violerebbe la convenzione dell'argomento BSTR, proprio come CComBSTR.Attach.

Tutto sommato, voglio utilizzare CString per gestire BSTR raw in fase di implementazione, ma non conosco il modo corretto ... Ho scritto alcuni codice di lavoro nei miei progetti, ma mi sento sempre nervoso da quando non lo faccio Non so se ho ragione.

Fammi parlare una marcatura di lingua

HRESULT CMyWebBrowser::put_StatusText(BSTR bstr) 
{ 
// What I do NOT know 
CString str1; // 1. copy bstr (with embeded NUL) 
CString str2; // 2. ref bstr 

// What I know 
CComBSTR cbstr1; 
cbstr1.AssignBSTR(bstr); // 3. copy bstr 
CComBSTR cbstr2; 
cbstr2.Attach(bstr); // 4. ref bstr, do not copy 

// What I do NOT know 
// Should we copy or ref bstr ??? 
} 

risposta

11

CComBSTR è solo un RAII wrapper intorno primeBSTR. Quindi sentitevi liberi di utilizzare CComBSTR al posto di materie prime BSTR per aiutare a scrivere codice che è sicuro rispetto alle eccezioni, che rende più difficile la fuoriuscita di risorse (cioè il BSTR grezzo), ecc

Se il BSTR è un parametro di ingresso , è proprio come un const wchar_t* (con lunghezza prefissata, e potenzialmente alcuni L'\0' s caratteri all'interno). Se lo BSTR non ha incorporato NUL s, è sufficiente passarlo a un costruttore CString, che ne eseguirà una copia approfondita e sarà possibile lavorare localmente con lo CString. Le modifiche a tale CString non saranno visibili nell'originale BSTR. Puoi anche usare std :: wstring (e notare che std::wstring può gestire anche gli NUL incorporati).

void DoSomething(BSTR bstrInput) 
{ 
    std::wstring myString(bstrInput); 
    // ... work with std::wstring (or CString...) inside here 
} 

Invece, se il BSTR è un'uscita parametro , allora è passata utilizzando un altro livello di riferimento indiretto, cioè BSTR*.In questo caso, è possibile utilizzare CComBSTR::Detach() all'interno del vostro metodo per rilasciare il BSTR tranquillamente spostata nella CComBSTR, e trasferire la proprietà al chiamante:

HRESULT DoSomething(BSTR* pbstrOut) 
{ 
    // Check parameter pointer 
    if (pbstrOut == nullptr) 
     return E_POINTER; 

    // Guard code with try-catch, since exceptions can't cross COM module boundaries. 
    try 
    { 
     std::wstring someString; 
     // ... work with std::wstring (or CString...) inside here 

     // Build a BSTR from the ordinary string  
     CComBSTR bstr(someString.c_str()); 

     // Return to caller ("move semantics", i.e. transfer ownership 
     // from current CComBSTR to the caller) 
     *pbstrOut = bstr.Detach(); 

     // All right 
     return S_OK; 
    } 
    catch(const std::exception& e) 
    { 
     // Log exception message... 
     return E_FAIL; 
    } 
    catch(const CAtlException& e) 
    { 
     return e; // implicit cast to HRESULT 
    } 
} 

In sostanza, l'idea è quella di utilizzare BSTR (avvolto in una classe Raii come CComBSTR) solo al confine e fare il lavoro locale utilizzando std::wstring o CString.

Come "lettura bouns", considerare Eric Lippert's guide to BSTR semantics.

+0

La mia grande domanda è Dovremmo copiare o ref bstr? La piccola qeustion è come refstrare con CString? come copiare bstr con NULL incorporato? – Raymond

+0

Cosa intendi con copia o ref? Se è un parametro di ingresso, passa 'BSTR' per valore, altrimenti passa" per puntatore "' BSTR * '. Se hai incorporato 'NUL's puoi copiarlo in' std :: wstring', non in un 'CString'. –

+0

Forse stai osservando il problema da un punto di vista sbagliato ... Pensa a un 'BSTR' come un' const wchar_t * 'allocato con uno speciale allocatore COM, e prefissato in lunghezza (quindi può avere incorporato' NUL's). –

4

Avendo BSTR in ingresso, non sei responsabile di rilasciarlo. Conversione in CString è facile:

CString sValue(bstr);

o, se si preferisce mantenere i caratteri Unicode in MBCS costruire:

CStringW sValue(bstr);

Se avete bisogno di convertire indietro quando si ha [out] parametro, è do (versione semplice):

VOID Foo(/*[out]*/ BSTR* psValue) 
{ 
    CString sValue; 
    *psValue = CComBSTR(sValue).Detach(); 
} 

La versione completa è:

STDMETHODIMP Foo(/*[out]*/ BSTR* psValue) 
{ 
    _ATLTRY 
    { 
     ATLENSURE_THROW(psValue, E_POINTER); // Parameter validation 
     *psValue = NULL; // We're responsible to initialize this no matter what 
     CString sValue; 
     // Doing our stuff to get the string value into variable 
     *psValue = CComBSTR(sValue).Detach(); 
    } 
    _ATLCATCH(Exception) 
    { 
     return Exception; 
    } 
    return S_OK; 
}