我可以在输入BSTR变体上调用variantChangeType吗?
Can I call VariantChangeType on an input BSTR VARIANT?
i具有使用以下签名的方法编写的com对象。假设该变体包含一个BSTR(仅VT_BSTR,而不是VT_BYREF | VT_BSTR)。
HRESULT myfunc(/*[in]*/ VARIANT param)
我想将类型更改为其他东西。如果VariantChangeType的第一个参数与第二个参数相同,则"变体将被转换到位"
。所以,我可以转换到位吗?
HRESULT myfunc(/*[in]*/ VARIANT param)
{
VariantChangeType(¶m, param, 0, VT_I4);
}
还是我应该复制到第二个变体?
HRESULT myfunc(/*[in]*/ VARIANT param)
{
VARIANT temp;
VariantInit(&temp);
VariantChangeType(&temp, param, 0, VT_I4);
}
我的理解是后者是必需的,因为前者将释放bstr,该BSTR由客户拥有,应由客户释放。
使用VariantChangeType
与第二个变体需要使用,尽管可能并不明显。
即使变体是按值传递的,但在变体点中包含的任何指针都包含到同一内存地址。由于BSTR是指针,这意味着BSTR的原始地址已传递到函数中,就像参数为BSTR而不是变体一样。
使用VariantChangeType
(现场)或VariantClear
将触发SysFreeString
,这意味着原始变体(呼叫者拥有)仍然包含BSTR的地址,但该地址不再保留BSTR。
来自"变体操纵函数"文档...
使用VT_BSTR类型释放或更改变体的类型时,在包含的字符串上调用sysfreestring。
这并不明显的原因是该代码似乎可以起作用,即使我上面描述的所有内容都不应该。
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif // !WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <OleAuto.h>
HRESULT myfuncbad(/*[in]*/ VARIANT param)
{
// In-place conversion
VariantChangeType(¶m, ¶m, 0, VT_I4);
return S_OK;
}
HRESULT myfuncgood(/*[in]*/ VARIANT param)
{
VARIANT temp;
VariantInit(&temp);
// Copy and convert into a new VARIANT
VariantChangeType(&temp, ¶m, 0, VT_I4);
VariantClear(&temp);
return S_OK;
}
int main()
{
VARIANT input;
VariantInit(&input);
V_BSTR(&input) = SysAllocString(L"1");
V_VT(&input) = VT_BSTR;
myfuncgood(input);
wprintf(L"Memory location of BSTR = 0x%xn", (unsigned)(V_BSTR(&input)));
wprintf(L"Contents of BSTR = %sn", V_BSTR(&input));
myfuncbad(input);
wprintf(L"Memory location of BSTR = 0x%xn", (unsigned)(V_BSTR(&input)));
wprintf(L"Contents of BSTR = %sn", V_BSTR(&input));
}
此代码运行并输出类似以下
Memory location of BSTR = 0x2d1af0c
Contents of BSTR = 1
Memory location of BSTR = 0x2d1af0c
Contents of BSTR = 1
但是为什么?事实证明,BSTR分配是缓存的。因此,即使调用了VariantChangeType
(就地)或VariantClear
,BSTR分配也可能持续一段时间。在传递到这些函数的变体上,这将是很明显的,但是该变体的任何"副价值"副本都可能仍然在一段时间内看到BSTR。
无论如何,从技术上讲,BSTR已被myfuncbad
释放,不应再由呼叫者引用。另外,在原始变体上调用VariantClear
可能会导致错误。
其他阅读
- 为什么无论如何都有BSTR缓存?
- 缓存很好,但是它们会混淆内存泄漏检测工具
- Eric的BSTR语义指南
- 扩展视觉基本 - 终极数据类型
保存的内置转换。您不需要时间变体。
我在所有ATL COM代码中都使用它:
CComVariant v;
GetSomeData(v); // Assume v returns a VT_BSTR variant.
HRESULT hr = v.ChangeType(VT_I4);
if (FAILED(hr))
...
此代码以讨论的方式转化为内置转换。在内部,在旧的VARIANT
BSTR
使用计数值降低之前,将用VarI4FromBSTR
计算结果。
我在某些调试会话中对此进行了验证,因为我也不确定。
编辑最后,我在MSDN中找到了确认此的语句。
对于VT_BSTR,该字符串只有一个所有者。所有字符串 必须将变体分配给Sysallocstring函数。什么时候 使用VT_BSTR类型释放或更改变体的类型, sysfreestring在包含的字符串上被调用。
代码在@jveazey的答案中起作用,它与BSTR缓存无关。有一个真正的位置转换!
制作副本会更安全,但是当我阅读"组件对象模型的规则"时(https://msdn.microsoft.com/en-en-us/library/MS810016.aspx):
•以下规则适用于接口成员函数的参数,包括返回值,这些函数未传递"副价值":◦对于参数,呼叫者应分配并释放内存。
您正在谈论的情况是逐个价值(不管发生了该呼叫的BSTR的变体如何)。因此,我相信Callee在这种情况下拥有参数,如果要确保其价值的持续可行性,则由呼叫者制作副本。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 函数调用中参数的顺序重要吗
- OpenGL - 在抛出"__gnu_cxx::recursive_init_error"实例后终止调用?
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在c++类上调用void函数
- 为什么 std::unique 不调用 std::sort?
- 调用专用模板时出错"no matching function for call to [...]"
- 选择要调用的构造函数
- C++为什么尽管我调用了void函数,它却不起作用
- 构造函数正在调用一个使用当前类类型的函数
- 变量没有改变?通过向量的函数调用
- 没有为自己的结构调用列表推回方法
- 调用'begin(int [n])'没有匹配函数
- 什么时候调用析构函数
- 如何用参数值调用函数(仅在运行时已知)
- std::cout.imbue()多重调用
- 函数何时会在c++中包含stack_Unwind_Resume调用