如何最好地将 CString 转换为 BSTR 以将其作为“in”参数传递给 COM 方法?
Posted
技术标签:
【中文标题】如何最好地将 CString 转换为 BSTR 以将其作为“in”参数传递给 COM 方法?【英文标题】:How to best convert CString to BSTR to pass it as an "in" parameter into a COM method? 【发布时间】:2011-01-06 09:18:42 【问题描述】:我需要将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
。
有没有更好的方法来完成同样的任务?
【问题讨论】:
【参考方案1】:CComBSTR
为char*
和wchar_t*
重载了构造函数,它们代表您调用SysAllocString()
。因此,您的代码 sn-p 中的显式分配实际上是不必要的。以下方法也可以:
ATL::CComBSTR converted = sourceString;
interface->CallMethod(converted);
此外,如果您不需要在代码的其他地方使用转换后的BSTR
,您可以在方法调用中就地执行对象构造,如下所示:
interface->CallMethod(ATL::CComBSTR(sourceString));
这同样适用于 _bstr_t
类,如果您不想依赖 ATL,可以使用它来代替 CComBSTR
。
【讨论】:
这肯定会工作,除了使用 CComBSTR 构造函数我必须检查 BSTR 分配是否成功(CString::AllocSysString() 进行检查并引发异常)和 _bstr_t 我会必须处理 _com_issue_error() 函数 - 要么覆盖它,要么捕获它抛出的 _com_error。 为什么不让被调用的方法负责检查自己的参数的有效性呢?如果分配失败,CComBSTR::m_str
将为空。因此,被调用的方法将检查 null 并返回 E_INVALIDARG
,或者它不会,并且您已经为 CString::AllocSysString()
拥有的相同 catch
可以为您处理异常。恕我直言,这比自己明确执行检查更简洁。
@Phil Booth:被调用的方法可能会将空 BSTR 解释为特殊情况。例如,含义可能是“指定文件名,如果传递空字符串,则使用默认文件名”。所以被调用的方法可能没有机会知道有问题。
COM 的基本规则是空 BSTR 和零长度 BSTR 之间没有语义差异。如果您调用的某些组件将两者区分开来,则这是一个错误。有关更多信息,请参阅此答案:***.com/questions/171641/…
@Phil Booth:是的,我知道。问题是如果我只是调用 InvokeMethod( CComBSTR( sourceString ) ) 被调用的方法将不知道为什么传递的 BSTR 为空 - 因为它在那里表示一个空字符串或者因为在 CComBSTR 构造函数中调用的 SysAllocString() 导致内存不足短缺。【参考方案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.
【讨论】:
【参考方案3】:_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
constructor documentation 说:
_bstr_t(
BSTR bstr,
bool fCopy
);
fCopy
如果为 false,则bstr
参数将附加到新对象,而不通过调用SysAllocString
进行复制。
另一方面,CComBSTR
constructor 似乎没有对应的签名;虽然它也可以在BSTR
分配失败异常不是真的需要时使用,正如Phil Booth 在his answer 中提到的那样。
【讨论】:
以上是关于如何最好地将 CString 转换为 BSTR 以将其作为“in”参数传递给 COM 方法?的主要内容,如果未能解决你的问题,请参考以下文章
ATL 如何将 BSTR* str 转换为注册表项。 SetValue(LPCTSTR str 类型
如何最好地将 DbType 转换为 System.Type?