传递 wchar_t 而不是 BSTR 导致 E_OUTOFMEMORY 错误
Pass wchar_t instead of BSTR causes E_OUTOFMEMORY error
我有一个简单的 DirectShow 库,还有一个从该库调用函数的客户端应用程序。问题是有些函数接受 BSTR
作为参数,但我更喜欢传递 std::string
或 char*
。可以肯定的是,如果我使用其中一种类型,我会遇到很多编译器错误。
我问了原因,有人说我不能使用它们,因为它们不像 BSTR 那样不能保证 ABI。
我也试过 wchar_t
。我将它传递给我的函数而不是 BSTR。令人惊讶的是它没有产生任何编译器错误:
std::string username = "john";
wchar_t wcstring[4] = {0};
MultiByteToWideChar(0, 0, username.c_str(), 4, wcstring, 4);
但是当我调用我的函数时:
HRESULT hr = pFoo->myDummyFunction(wcstring);
和运行程序,它returnsE_OUTOFMEMORY
作为HRESULT。我很好奇为什么会这样。
如果您调用的方法被声明为采用 BSTR 参数,那么您必须传递一个真正的 BSTR 作为参数。别无选择。
很抱歉这不是您的喜好。我同意这不方便的原因有很多。但是,尝试传递 wchar_t*
而不是 BSTR
就像尝试将 std::vector<wchar_t>
传递给需要 std::wstring
的函数一样。希望您会同意,即使编译器没有阻止您,那也是无稽之谈。
BSTR 不仅仅是一个字符序列;它是一个 variable-sized 结构,具有非常明确的语义,恰好包含一个 null-terminated 序列 wchar_t 作为它的主要成员。
让您感到困惑的是,编译器似乎允许它们混合使用。这是因为 BSTR 的设计使得在某些非常有限的情况下,可以使用 BSTR
object 就像 it 只是一个 wchar_t*
。 有时可以使用 BSTR 代替 wchar_t*,但反之则不行。
这只是意味着 C++ 编译器无法区分 BSTR 和 wchar_t*。事实上,Windows headers 本质上将 BSTR 定义为 typedef wchar_t* BSTR
所以它们对编译器来说是一样的,但编译器不知道 BSTR 结构的其余部分。这意味着编译器无法告诉您什么时候使用 BSTR 是合法的,什么时候使用 wchar_t* 是合法的:您有责任自己选择正确的。
除了 BSTR 核心的 null-terminated 字符序列之外,BSTR 还有一个大小前缀 存储在第一个字符 之前;并且 BSTR 必须分配到由 Windows 中的 COM 基础结构控制的特定堆中,这就是为什么它只能通过调用 SysAllocString() 和类似函数来创建的原因。
在本应使用 BSTR 的地方使用 wchar_t* 会导致错误、崩溃和未定义的行为,具体取决于具体情况。我们不知道为什么会出现 E_OUTOFMEMORY 错误,但不难猜测:该函数可能在尝试复制 BSTR 时尝试使用大小前缀。因为您传递了 wchar_t* 而不是 BSTR,所以前缀中有垃圾,可能是一个大得离谱的数字,并且该函数尝试使用该大数字为副本分配大量内存,但失败了。
如果您想为自己的代码使用 std::wstring 或 std::string,请继续。我会做同样的事情。但是在调用该方法之前创建一个 BSTR,将其作为参数传递,然后释放 BSTR 并继续。
我有一个简单的 DirectShow 库,还有一个从该库调用函数的客户端应用程序。问题是有些函数接受 BSTR
作为参数,但我更喜欢传递 std::string
或 char*
。可以肯定的是,如果我使用其中一种类型,我会遇到很多编译器错误。
我问了原因,有人说我不能使用它们,因为它们不像 BSTR 那样不能保证 ABI。
我也试过 wchar_t
。我将它传递给我的函数而不是 BSTR。令人惊讶的是它没有产生任何编译器错误:
std::string username = "john";
wchar_t wcstring[4] = {0};
MultiByteToWideChar(0, 0, username.c_str(), 4, wcstring, 4);
但是当我调用我的函数时:
HRESULT hr = pFoo->myDummyFunction(wcstring);
和运行程序,它returnsE_OUTOFMEMORY
作为HRESULT。我很好奇为什么会这样。
如果您调用的方法被声明为采用 BSTR 参数,那么您必须传递一个真正的 BSTR 作为参数。别无选择。
很抱歉这不是您的喜好。我同意这不方便的原因有很多。但是,尝试传递 wchar_t*
而不是 BSTR
就像尝试将 std::vector<wchar_t>
传递给需要 std::wstring
的函数一样。希望您会同意,即使编译器没有阻止您,那也是无稽之谈。
BSTR 不仅仅是一个字符序列;它是一个 variable-sized 结构,具有非常明确的语义,恰好包含一个 null-terminated 序列 wchar_t 作为它的主要成员。
让您感到困惑的是,编译器似乎允许它们混合使用。这是因为 BSTR 的设计使得在某些非常有限的情况下,可以使用 BSTR
object 就像 it 只是一个 wchar_t*
。 有时可以使用 BSTR 代替 wchar_t*,但反之则不行。
这只是意味着 C++ 编译器无法区分 BSTR 和 wchar_t*。事实上,Windows headers 本质上将 BSTR 定义为 typedef wchar_t* BSTR
所以它们对编译器来说是一样的,但编译器不知道 BSTR 结构的其余部分。这意味着编译器无法告诉您什么时候使用 BSTR 是合法的,什么时候使用 wchar_t* 是合法的:您有责任自己选择正确的。
除了 BSTR 核心的 null-terminated 字符序列之外,BSTR 还有一个大小前缀 存储在第一个字符 之前;并且 BSTR 必须分配到由 Windows 中的 COM 基础结构控制的特定堆中,这就是为什么它只能通过调用 SysAllocString() 和类似函数来创建的原因。
在本应使用 BSTR 的地方使用 wchar_t* 会导致错误、崩溃和未定义的行为,具体取决于具体情况。我们不知道为什么会出现 E_OUTOFMEMORY 错误,但不难猜测:该函数可能在尝试复制 BSTR 时尝试使用大小前缀。因为您传递了 wchar_t* 而不是 BSTR,所以前缀中有垃圾,可能是一个大得离谱的数字,并且该函数尝试使用该大数字为副本分配大量内存,但失败了。
如果您想为自己的代码使用 std::wstring 或 std::string,请继续。我会做同样的事情。但是在调用该方法之前创建一个 BSTR,将其作为参数传递,然后释放 BSTR 并继续。