Wednesday, November 4, 2009

BSTR Wrapper Classes


3 4



BSTR Wrapper Classes



The basic_string template has no support for BSTRs, and CString does not provide much more. However, there is not one but two classes that ship with the Visual C++ environment whose sole focus is BSTR encapsulation. One, CComBSTR, is part of ATL, and the other, _bstr_t, belongs to the Visual C++ compiler COM support package and integrates with the #import facility. Similar to smart pointers in nature, these BSTR wrapper classes ensure proper BSTR resource management. You should hardly ever encounter the raw BSTR type or direct calls to the BSTR string manipulation APIs in a C++ project. Using the wrapper classes increases the likelihood of correct BSTR handling and makes your code ever so much more understandable and maintainable.



Feature Comparison



The BSTR wrapper classes do not offer full-featured string manipulation and analysis, as the string classes do. Converting a BSTR to a string class instance at the interface boundary, for example, therefore remains an effective as well as efficient approach. (The string classes can manipulate character data more efficiently than the BSTR wrappers because they need not resort to system APIs in as rigid a fashion to manage the underlying memory.) There are some differences in the approach to BSTR management that the two wrapper classes take. Let's see what these are.



Table 3-1 CComBSTR and _bstr_t comparison


















































































































FacilityCComBSTR_bstr_t
Error handling strategyAssertionsException: _com_error by value
BSTR creationThrough construction, via an LPCOLESTR or LPCSTR, or another CComBSTR (but not a LPCWSTR).Through construction, via an LPCSTR, an LPCWSTR, a Variant, another _bstr_t, or a BSTR.
Through operator =, from the same argument types as at construction time.Through operator =, from the same argument types as at construction time, except BSTR.
By reading from IStream.
BSTR acquisition, apart from creationAttachThe constructor taking a BSTR argument can be instructed not to copy it, but to attach to it.
DetachmentClass method Empty destroys the internal BSTR.Only through operator = and destruction.
Class method Detach returns the internal BSTR and releases it from encapsulation.
ComparisonNoneOperators ==, !=, <, > <=, and >=
Extractors and convertersClass method Copy.Class method copy.
Operator BSTR (no converters).Operators LPCWSTR, LPWSTR, LPCSTR, and LPSTR.
Variant integrationNoneConversion from _variant_t with constructor and operator =.
_variant_t provides a user-defined conversion operator _bstr_t.10
String size measurementClass method LengthClass method length
BSTR addressOperator &None
Encapsulated BSTR data, m_str, is public.
Boolean expression supportOperator !Operator ! only; testing for non-NULL requires negation of operator !, and thus the syntactic pattern "!!"
Testing for non-NULL is routed via public data member m_str
String concatenationClass method Append concate-nates with LPCOLESTR, LPCSTR, and another CComBSTR; the number of characters appended can be restricted with the LPCOLESTR form.Operator += concatenates with another _bstr_t.
Class method AppendBSTR concatenates with a BSTR argument.Operator + concatenates with LPCSTR, LPCWSTR, and another _bstr_t; it returns a new _bstr_t by value.
Operator += concatenates with another CComBSTR.
Const supportYesYes


Because _bstr_t lacks an operator & as well as BSTR conversion functionality, it is generally not well suited to interaction with raw COM+ interfaces from your own code. The former restriction means that it cannot be used with parameters containing direction attribute [out], and the latter makes it awkward for substitution with [in] parameters. Instead, _bstr_t is really designed for use by the #import facility. For example, #import wrappers pass a raw BSTR in arguments with attributes [out, retval] and then attach to the result with a _bstr_t constructor, while simultaneously leveraging the compiler's return value optimization capability. The underlying raw BSTR is therefore never copied unnecessarily before ultimate consumption by your client code. The strong encapsulation of the _bstr_t class means that you seldom want to instantiate it yourself, but this very feature makes the class useful and safe when used with #import.



Usage Pattern



Here are two examples of how CComBSTR is typically used in your code. The first example shows client code with parameters that have directional attributes [in], [in, out], and [out, retval], while the second shows server code implementing such a method.





struct IFoo : public IUnknown
{
HRESULT Foo(/*[in]*/ BSTR, /*[in, out]*/ BSTR*,
/*[out, retval]*/ BSTR*);
};

void Bar(IFoo& riFoo)
{

CComBSTR sInArg(OLESTR("My Message"));
CComBSTR sChangingArg(OLESTR("To be duplicated"));
CComBSTR sResult;

HRESULT hResult = riFoo.Foo(sInArg, &sChangingArg, &sResult);

}

std::wstring ComputeResponse(LPCOLESTR sInArg);

HRESULT CFoo::Foo(/*[in]*/ BSTR sInArg, /*[in, out]*/ BSTR* psChangingArg,
/*[out, retval]*/ BSTR* psResult)
{
USES_CONVERSION;

if (! psResult || ! psChangingArg)
return E_POINTER;

CComBSTR siDuplicator;
siDuplicator.Attach(*psChangingArg);
siDuplicator += siDuplicator;

*psResult = W2BSTR(ComputeResponse(sInArg).c_str());
*psChangingArg = siDuplicator.Detach();

return S_OK;
}



As you can see, leveraging BSTR wrapper classes makes C++ code that deals with COM+ strings short and relatively simple. Allocation and deallocation code is entirely encapsulated by CComBSTR in the first code sample. But as the second sample shows, you still need to understand the rules governing where and how BSTR interface method parameters are created and destroyed, based on their directional attributes.


No comments: