如何最佳地将CString转换为BSTR以将其作为“in”参数传递到COM方法中?

10
我需要将一个CString实例转换成适当分配的BSTR,并将该BSTR传递给COM方法。为了编译并在ANSI和Unicode之间工作,我使用CString::AllocSysString()将任何格式的CString转换为Unicode BSTR。
由于没有人拥有返回的BSTR,我需要以最少的代码和最安全的方式在调用完成后释放它。
目前,我使用ATL::CComBSTR进行生命周期管理:
 ATL::CComBSTR converted;
 converted.Attach( sourceString.AllocSysString() ); //simply attaches to BSTR, doesn't reallocate it
 interface->CallMethod( converted );

我不喜欢这里的地方就是我需要两个单独语句来构建绑定到转换结果的ATL::CComBSTR

有更好的方法来完成同样的任务吗?

3个回答

17

CComBSTR有为char*wchar_t*两种类型的构造函数重载,它们会代表你调用SysAllocString()。因此,在你的代码片段中显式分配内存实际上是不必要的。以下代码也可以正常工作:

ATL::CComBSTR converted = sourceString;
interface->CallMethod(converted);
此外,如果您的代码中没有其他地方需要使用转换后的 BSTR,您可以在方法调用中就地进行对象构造,如下所示:
interface->CallMethod(ATL::CComBSTR(sourceString));

如果你不想依赖ATL,那么可以使用_bstr_t类替代CComBSTR


那肯定可以运行,但是使用 CComBSTR 构造函数时,我必须检查 BSTR 分配是否成功(CString::AllocSysString() 进行检查并抛出异常),而对于 _bstr_t,我必须处理 _com_issue_error() 函数 - 要么覆盖它,要么捕获从中抛出的 _com_error。 - sharptooth
为什么不让被调用的方法负责检查其自身参数的有效性呢?如果分配失败,CComBSTR::m_str 将为空。因此,被调用的方法将检查 null 并返回 E_INVALIDARG,或者不会检查并且您已经为 CString::AllocSysString() 拥有的相同 catch 可以为您处理异常。在我看来,这比显式执行自己的检查更清晰。 - Phil Booth
1
@Phil Booth:被调用的方法可能会将空BSTR解释为特殊情况。例如,其含义可能是“如果传递了空字符串,则指定文件名使用默认文件名”。因此,被调用的方法可能没有机会知道存在问题。 - sharptooth
2
COM 的一个基本规则是,空 BSTR 和零长度 BSTR 之间没有语义差异。如果您调用的某个组件区分这两者,则这是一个错误。有关更多信息,请参见此答案:https://dev59.com/9HVC5IYBdhLWcg3w1Exq - Phil Booth
2
@Phil Booth:是的,我知道。问题在于,如果我只调用InvokeMethod(CComBSTR(sourceString)),被调用的方法将不知道传递的BSTR为什么为空——因为它存在于指示空字符串或因CComBSTR构造函数内部调用SysAllocString()而导致内存短缺的情况下。 - sharptooth
哦,我的错,我误解了你,抱歉。你说得很有道理。 - Phil Booth

2

_bstr_t 的其中一个构造函数允许您简单地附加到现有的 BSTR,因此当 BSTR 分配失败时,您可以从 CString::AllocSysString 获得所需的异常。

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

_bstr_t构造函数文档中提到:

_bstr_t(
   BSTR bstr,
   bool fCopy 
);

fCopy
如果为false,则通过调用SysAllocString将参数附加到新对象而不进行复制。

另一方面,CComBSTR构造函数似乎没有相应的签名;尽管如Phil Booth他的回答中提到的那样,如果不真正需要BSTR分配失败异常,也可以使用它。


2
Windows编程中令人困惑的一点是管理Visual Basic风格字符串与C语言风格字符串的转换。并不是说这很难,而是很难记住细节。通常不会经常使用,MSDN文档如此庞大,以至于难以找到问题的答案。但最糟糕的是,您可能会执行某些类型转换,它可以编译通过,但不会按您的预期工作。这导致代码不起作用,错误难以跟踪。有了一些经验后,您就会学会确保字符串转换正在按照您的预期进行。
C字符串是由以NULL字符结尾的字符数组组成。Visual Basic字符串的不同之处在于字符串中的字符之前有字符串长度。因此,VB字符串知道自己的长度。此外,所有VB字符串都是Unicode(每个字符16位)。
如果需要BSTR/C字符串转换,则需要:
You are doing COM programming in C/C++
You are writing multiple language applications, such as C++ DLL's accessed by Visual Basic applications.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接