2013-03-15 37 views
5

從書ATL內部,我知道BSTR是不同於OLECHAR *,並且有BSTR的CComBSTR和CString。我們可以把COM中的BSTR類型作爲值還是參考?

根據MSDN Allocating and Releasing Memory for a BSTR,我知道主叫/被叫的內存管理責任。

走這條線從MSDN,

HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)

我仍然不知道如何在我的實現處理bstr正常。由於我仍然有BSTR的一個基本問題 - 我們應該將bstr作爲一個值(如int)還是作爲引用(如int *),至少在COM接口邊界上。

我想在我的實現中儘快將BSTR轉換爲CString/CComBSTR。值或引用語義對於轉換來說是完全不同的情況。我已經深入CComBSTR.Attach,CComBSTR.AssignBSTR等,但代碼無法解決我的疑惑。

MSDN CComBSTR.Attach有一些代碼片斷,我覺得它是錯誤的,因爲它不服從Allocating and Releasing Memory for a BSTR。 ATL Internals表示SetSysString會'釋放原來傳入的BSTR',如果我使用它,它將違反BSTR參數約定,就像CComBSTR.Attach一樣。

總而言之,我想使用CString來處理原始BSTR的實現,但不知道正確的方法......我在項目中編寫了一些只是工作代碼,但我總是感到緊張,因爲我不知道不知道我是否正確。

讓我談談編碼語言

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 ??? 
} 

回答

11

CComBSTR只是一個RAII wrapper各地原料BSTR。所以隨意使用CComBSTR而不是原始BSTR幫助編寫代碼是異常安全的,這使得它更難泄漏資源(即原始BSTR)等

如果BSTR輸入參數,它就像一個const wchar_t*(帶有長度前綴,並且可能有一些NUL s L'\0'字符在裏面)。如果BSTR沒有嵌入NUL,您可以簡單地將它傳遞給CString構造函數,這將構成深層副本,並且您可以在本地使用CString。原BSTR上將不會顯示對CString的修改。您也可以使用std :: wstring(並注意std::wstring也可以處理嵌入的NUL s)。

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

相反,如果BSTR輸出參數,則它是使用間接的另一個水平通過,即BSTR*。在這種情況下,你可以使用CComBSTR::Detach()你的方法內釋放BSTR安全地裹入CComBSTR,其所有權轉移給調用者:

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 
    } 
} 

基本上,這個想法是使用BSTR(包裹在一個RAII類如CComBSTR只有在邊界,並使用std::wstringCString做本地工作。

作爲「bouns閱讀」,請考慮Eric Lippert's guide to BSTR semantics

+0

我的大問題是我們應該複製或參考bstr?小qeustion是如何使用CString參考bstr?如何用嵌入的NULL複製bstr? – Raymond 2013-03-15 20:51:50

+0

複製或參考是什麼意思?如果它是一個輸入參數通過'BSTR'通過值,否則通過「指針」'BSTR *'通過。如果你嵌入了'NUL',你可以將它複製到'std :: wstring'中,而不是'CString'中。 – 2013-03-15 21:24:55

+0

也許你從一個錯誤的觀點來看待問題......想像一個''const' wchar_t *'分配一個特殊的COM分配器,長度爲前綴(因此它可以嵌入'NUL's )。 – 2013-03-15 21:53:27

4

如果輸入的是BSTR,則不承擔責任。轉換爲CString很簡單:

CString sValue(bstr);

,或者,如果你喜歡保持Unicode字符上MBCS建設:

CStringW sValue(bstr);

如果您需要轉換回來時,你有[out]參數, (簡單版):

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

完整版是:

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; 
} 
相關問題