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
Facility | CComBSTR | _bstr_t |
---|---|---|
Error handling strategy | Assertions | Exception: _com_error by value |
BSTR creation | Through 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 creation | Attach | The constructor taking a BSTR argument can be instructed not to copy it, but to attach to it. |
Detachment | Class method Empty destroys the internal BSTR. | Only through operator = and destruction. |
Class method Detach returns the internal BSTR and releases it from encapsulation. | ||
Comparison | None | Operators ==, !=, <, > <=, and >= |
Extractors and converters | Class method Copy. | Class method copy. |
Operator BSTR (no converters). | Operators LPCWSTR, LPWSTR, LPCSTR, and LPSTR. | |
Variant integration | None | Conversion from _variant_t with constructor and operator =. |
_variant_t provides a user-defined conversion operator _bstr_t.10 | ||
String size measurement | Class method Length | Class method length |
BSTR address | Operator & | None |
Encapsulated BSTR data, m_str, is public. | ||
Boolean expression support | Operator ! | 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 concatenation | Class 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 support | Yes | Yes |
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:
Post a Comment