How to best convert CString to BSTR to pass it as an "in" parameter into a COM method?
Asked Answered
F

3

10

I need to convert a CString instance into a properly allocated BSTR and pass that BSTR into a COM method. To have code that compiles and works indentically for both ANSI and Unicode I use CString::AllocSysString() to convert whatever format CString to a Unicode BSTR.

Since noone owns the returned BSTR I need to take care of it and release it after the call is done in the most exception-safe manner posible and with as little code as possible.

Currently I use ATL::CComBSTR for lifetime management:

 ATL::CComBSTR converted;
 converted.Attach( sourceString.AllocSysString() ); //simply attaches to BSTR, doesn't reallocate it
 interface->CallMethod( converted );

what I don't like here is that I need two separate statements to just construct the ATL::CComBSTR bound to the convertion result.

Is there a better way to accomplish the same task?

Factional answered 18/1, 2010 at 14:5 Comment(0)
I
17

CComBSTR has overloaded constructors for both char* and wchar_t*, which make the call to SysAllocString() on your behalf. So the explicit allocation in your code snippet is actually unnecessary. The following would work just as well:

ATL::CComBSTR converted = sourceString;
interface->CallMethod(converted);

Furthermore, if you have no need to use the converted BSTR elsewhere in your code, you can perform the object construction in-place in the method call, like so:

interface->CallMethod(ATL::CComBSTR(sourceString));

The same applies to the _bstr_t class, which can be used instead of CComBSTR if you don't want a dependency on the ATL.

Iguanodon answered 18/1, 2010 at 15:43 Comment(6)
That will definitely work, except that with using CComBSTR constructor I will have to check that BSTR allocation was successful (CString::AllocSysString() does the check and throws an exception) and with _bstr_t I'll have to take care of _com_issue_error() function - either override it or catch _com_error thrown from it.Factional
Why not let the called method be responsible for checking the validity of its own arguments instead? If the allocation fails, CComBSTR::m_str will be null. So either the called method will check for null and return E_INVALIDARG, or it won't and the same catch that you already have for CString::AllocSysString() can handle the exception for you. It is a cleaner idiom than explicitly performing the check yourself, imho.Iguanodon
@Phil Booth: The method being called might interpret null BSTR as a special case. For example, the meaning could be "specifies a filename, if empty string is passed the default filename is used". So the method being called might have no chance to know that there's a problem.Factional
It is a fundamental rule of COM that there is no semantic difference between a null BSTR and a zero-length BSTR. If some component that you call differentiates between the two, it is a bug. See this answer for more information: #172141Iguanodon
@Phil Booth: Yeap, I know. The problem is if I just call InvokeMethod( CComBSTR( sourceString ) ) the method being called will have no idea of why is the passed BSTR null - because it is there to indicate an empty string or because SysAllocString() called inside CComBSTR constructor faiced memory shortage.Factional
Oh, my bad, I misunderstood you, sorry. You've got a good point.Iguanodon
B
2

One of the confusing aspects of Windows programming is managing the conversion of Visual Basic style strings to/from C language style strings. It isn't that it is so difficult, it is just difficult to remember the details. It is usually not done often, and the MSDN documentation is so voluminous that it is difficult to find answers to your questions. But, the worst part is that you could perform some typecast that compiles fine, but doesn't work the way you expect. This results in code that doesn't work, and the bugs are hard to track down. After some experience, you learn to make sure your string conversions are doing what you expect.

C strings are arrays of characters terminated by a NULL character. Visual Basic strings differ in that the length of the string precede the characters in the string. So, a VB string knows its own length. In addition, all VB strings are Unicode (16 bits per character). String Types

BSTR/C String conversions are required if:

You are doing COM programming in C/C++
You are writing multiple language applications, such as C++ DLL's accessed by Visual Basic applications.
Bijou answered 8/2, 2012 at 19:2 Comment(0)
V
2

One of _bstr_t constructors allows you to simply attach to existing BSTR so that you can have the exception that you want from CString::AllocSysString when BSTR allocation fails.

// _bstr_t simply attaches to BSTR, doesn't reallocate it
interface->CallMethod( _bstr_t(sourceString.AllocSysString(), false) );

The _bstr_t constructor documentation says:

_bstr_t(
   BSTR bstr,
   bool fCopy 
);

fCopy
If false, the bstr argument is attached to the new object without making a copy by calling SysAllocString.

On the other hand, CComBSTR constructor doesn't seem to have the corresponding signature; although it can be used as well if BSTR allocation failure exception is not really needed, as mentioned by Phil Booth in his answer.

Vandenberg answered 23/1, 2013 at 2:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.