将 BSTR 管道到 wstringstream 中?

Pipe BSTR into wstringstream?

提问人:Mr. Boy 提问时间:11/16/2023 最后编辑:Mr. Boy 更新时间:11/18/2023 访问量:116

问:

这里有一个久经考验的答案如何转换 -> .但我想将 BSTR 包含在我正在建立的中。BSTRstd::wstringwstringstream

显然我可以做到(作为 MRE 有点做作):

std::wstring proc(BSTR bStr)
{
 std::wstringstream ss;
 std::wstring ws(bStr, SysStringLen(bStr));
 
 ss << "The string is: " << ws; 
 return ss.str();
}

但是有没有办法避免直接创建临时对象和管道呢?wsbStr

C++ winapi com bstr

评论

5赞 Alan Birtles 11/16/2023
ss.write(bStr, SysStringLen(bStr))?
1赞 Simon Mourier 11/16/2023
ss << "The string is: " << bStr应该工作,除非它不是以 null 结尾的(这种情况很少见,但在技术上是可行的)。
0赞 Mr. Boy 11/16/2023
谢谢@SimonMourier这是我不确定的事情 - 一个快速测试,看起来不错,但留下了边缘情况(比如如果为空/空怎么办?bStr
0赞 Simon Mourier 11/16/2023
只要您正常转换或使用它,BSTR 就是一个字节数组,即:wchar_t*(或 Windows 术语中的 LP(C)WSTR)。通常,这些字节对 Unicode 字符串进行编码。通常它们以空结尾。
0赞 Remy Lebeau 11/16/2023
@SimonMourier根据定义,非 null BSTR 始终以 null 结尾,任何处理 BSTR 内存的 API 都希望存在终止符。那么,在没有终结器的情况下拥有 BSTR 如何“技术上可行”呢?

答:

2赞 Remy Lebeau 11/16/2023 #1

BSTR就编译器而言,它只是一个 typedef(它只是在运行时的分配方式与普通 C 字符串不同)。wchar_t*wchar_t

根据定义,非空总是以 null 结尾(即使它也是以长度为前缀的)。BSTR

因此,除非您的字符串中嵌入了 nul 字符(这是可能的),否则您可以只对宽字符串使用普通字符。只需注意 the 为 null 指针的情况,这将为运算符显示未定义的行为operator<<BSTR

试试这个:

std::wstring proc(BSTR bStr)
{
 std::wstringstream ss; 
 ss << L"The string is: ";
 if (bStr) ss << bStr; 
 return ss.str();
}

评论

4赞 Raymond Chen 11/16/2023
边缘情况是 when ,它表示空字符串。请参阅 ericlippert.com/2003/09/12/...用于修复和嵌入的 null 问题。bStr == nullptrstd::wstring_view(bStr, SysStringLen(bStr))nullptr
1赞 IInspectable 11/17/2023 #2

[我]有没有办法避免创建临时对象并直接通过管道进入?wsbStr

是的,当然!

BSTR 是一种伪装,它支持使 basic_ostream::operator 满意的所有要求<<:它保证是指向值序列的指针,以字符结尾。wchar_t*nullptrCharTNUL

写作

    ss << "The string is: " << bstr;

是完全有效的。

尽管如此,这样做会忽略承认两种边缘情况,其中 a 比传统的 C 样式字符串更宽松:BSTR

  • 它可以包含字符作为序列的一部分,从而导致截断NUL
  • 它可以是在输出流中生成实现定义的字符串nullptr

两者都不可取,这两个问题都可以通过引入作为中间人来解决。这不是我们可以放弃的。由于中介是一个不可协商的要求,问题是:我们能否在不支付(不必要的)分配价格的情况下获得服务?std::wstringwsstd::wstring

从 C++17 开始,我们可以。该工具称为 std::wstring_view,能够生成“现代”悬空指针并解决手头的问题:

    ss << "The string is: " << std::wstring_view(bstr, ::SysStringLen(bStr));

这带回了没有价格标签的细节。std::wstring

0赞 Larry 11/18/2023 #3

我会将其作为答案发布,即使它应该是评论(针对您自己的帖子和其他回复),并且它实际上并没有解决您的问题(其他人正确地回答),而只是一个(题外话)仅供参考,BSTR 在技术上不是“wchar_t”指针。它实际上是一个 OLECHAR 指针,但由于 OLECHAR 在现代 Windows 上映射到“wchar_t”,因此您可以有效地以这种方式处理它(并且可能在这一点上永远摆脱它,至少在 Windows 上是这样)。

因此,在这一点上(很长一段时间以来),这是一个迂腐的(真正没有实际意义)的问题,但为了在技术上正确(并且老派开发人员经常会这样做),您应该显式地将 BSTR 转换为“wchar_t”指针(甚至是 Windows 上的 TCHAR 指针)然后再将其视为“wchar_t”指针或任何 TCHAR 对于仍在使用它的人来说, 通常也是“wchar_t”)。

How you do this is off-topic here (ancient MFC has ancient macros for it as one possible way - search for "OLE conversion macros" here), but again, in reality you can safely (usually) get away with just treating it as a "wchar_t" pointer on (modern) Windows as you're now doing (since going through the hoops of explicitly converting it to a "wchar_t" pointer on modern Windows will just treat a BSTR as a "wchar_t" pointer anyway - no conversion necessary and the above MFC macros handle things this way).

If you ever need to target an environment where OLECHAR doesn't map to "wchar_t" however, religiously doing the conversion now will correctly handle things in the future (that's what the conversion is for), compared to how you're now handling it (though most do treat a BSTR as a "wchar_t" pointer these days, though in the context that it's still a BSTR so needs to be treated that way, and even Microsoft itself treats it as a "wchar_t" pointer in some of its own sample code and documentation, but it's still an OLECHAR pointer nevertheless so such code will break if you ever target an environment where OLECHAR doesn't map to "wchar_t").

评论

0赞 IInspectable 11/18/2023
An has always been a on Windows.OLECHARWCHAR
0赞 Larry 11/18/2023
@IInspectable In 32-bit Windows that's true (assuming the obsolete OLE2ANSI isn't #defined - not normally of course), though not in 16-bit Windows but that obviously has no relevance anymore either. OLECHAR is not technically a "wchar_t" however so purists would argue it's cleaner to treat it as the opaque character type it really is (not a "wchar_t" even though it always maps to that in Win32)
0赞 IInspectable 11/18/2023
I see, so in 16-bit Windows, was an alias for . This means that Remy's answer may silently do what wasn't intended, while my answer won't (it will not compile for 16-bit Windows).OLECHARCHAR
0赞 IInspectable 11/18/2023
This is another way of saying that judiciously confining support and actively preventing unsupported scenarios from compiling is a compelling alternative to "religiously doing the conversion". The alternative is compelling since "doing the conversion" is not something you can get right and have it be correct in the future. Multibyte character strings represented as are a mess, with several competing character encodings. No religion (or sorcery) is going to alleviate this.char*
0赞 Larry 11/19/2023
I'm not sure I entirely follow what you said, and don't want to get into an off-topic treatise about the situation, but what I posted is endorsed by the lead authors of the ATL at Microsoft (such as Jim Springfield, who IIRC was the primary developer of the ATL). I'd have to dig my old manuals up for the actual citations (possibly the book "ATL Internals" which was the bible for ATL development at the time, though I haven't read it in many years).