COM Strings |
||
|
COM uses Unicode exclusively. COM strings are called "OLE Strings" or "Basic Strings." COM objects that aren't built under Unicode must convert OLE strings to ASCII in order to pass them to Win32 and the standard C library.
See the information regarding passing strings between C++ functions.
The following are not the same:
CComBSTR b1, b2;
if (b1 != b2) // this is wrong
{
}
if (!(b1 == b2)) // this is right
{
}
b1 != b2
compares the underlying BSTR pointers and does not perform a string comparison.
CComBSTR bar;
BSTR foo, baf;
baf = L"baf";
foo = bar ? bar:baf;
This won't do what you think... You would think this code evaluates bar ?
by converting bar
to a BSTR. But this code ends up calling CComBSTR(baf)
, causing foo
to be assigned to a temporary object that is deleted whenever the compiler feels like it. Instead, use bar.Length() ? bar:baf
Empty()
before calling operator &
CComBSTR
with a BSTR or CComBSTR
uses _wcslen
to get the length of the BSTR CComBSTR
to a CComBSTR
also uses _wcslen
_wcslen
-- SysStringLen
is much faster SysAllocStringLen(bstr, SysStringLen(bstr));
CComBSTR
is:
CComBSTR(SysStringLen(bstr), bstr);
CComBSTR
is:bs.Empty();
bs.Attach(SysAllocStringLen(bstr, SysStringLen(bstr));
In non-Unicode builds, each invocation of these macros allocates memory on the stack. Therefore these macros should not be used in a loop. If used in a loop (i.e., that runs 1,000 times) the stack may be overrun. Stacks are typically only about 50k in size.
Secondly, you should be careful about assigning pointers to the return values of these macros. The following is a classic error:
WCHAR *pwz = NULL;
try {
pwz = OLE2T(L"Hello world");
}
catch(...){}
wprintf("%ls", pwz);
After the code in the try block completes, pwz
points to deallocated memory. The behavior of this code snippet is unspecified and varies from machine to machine and also may vary depending on the build type (debug vs. release vs. Unicode).
L"foo"
LPOLESTR wstr = bstr;
USES_CONVERSION;
TCHAR *sz = OLE2T(lpolestr_or_bstr);See also
WideCharToMultiByte
.WideCharToMultiByte
does not create a null-terminated ASCII string. You should avoid callingWideCharToMultiByte
in UNICODE builds - use#ifdef _UNICODE
. Here is the correct usage:
HRESULT appendBSTR(basic_string<TCHAR>& t, BSTR wsz)
{
if (!wsz) return S_OK;
#ifdef _UNICODE
t.append(wsz);
#else
long len = SysStringLen(wsz) + 1;
char * a = new CHAR[len];
if (!WideCharToMultiByte(CP_ACP, 0, wsz, len, (char*)a, len, NULL, FALSE))
{
delete[] a;
return E_FAIL;
}
*(a + len-1) = 0;
t.append(a);
delete[] a;
#endif
return S_OK;
}
USES_CONVERSION;
LPOLESTR lpolestr = T2OLE(szText);See also
MultiByteToWideChar
. You should avoid calling MultiByteToWideChar in UNICODE builds - use #ifdef _UNICODE.
CComBSTR bstr(_T("foo"));
CComBSTR bstr_copy(bstr);
Use bstr_copy.Detach() if you don't want the copy to get free'd.
This section describes how to pass/return BSTRs via COM.
Caller - Caller converts TSTR to BSTR and frees the BSTR:
CComBStr bstr(szThisIsA_TStr);
comObject -> someMethod(bstr);COM method (callee) converts BSTR to TSTR:
USES_CONVERSION;
LPCTSTR szParam = OLE2T(bstr);
COM method (callee) converts internal TSTR data to BSTR:
CComBSTR bstr("Hi");
// pbstrOut is a BSTR *
*pbstrOut = bstr.Detach();Caller - Caller converts BSTR to TSTR and frees the BSTR:
USES_CONVERSION;
BSTR bstrName;
foo->get_Name(&bstrName);
LPCTSTR szName = OLE2T(bstrName);
...
SysFreeString(bstrName);
-or- (String is free'd for you automatically...)
USES_CONVERSION;
CComBStr bstrName;
foo->get_Name(&bstrName);
LPCTSTR szName = OLE2T(bstrName);