我可以对输入BSTR变量调用VariantChangeType吗? 我有一个C++对象,用下面的签名方法编写的。假设变量包含的BSTR只是VT_BSTR,而不是VT_BYREF | VT_BSTR HRESULT myfunc(/*[in]*/ VARIANT param)
我想将类型更改为其他类型。如果的第一个参数与第二个参数相同,则变量将就地转换 那么,我可以就地转换吗我可以对输入BSTR变量调用VariantChangeType吗? 我有一个C++对象,用下面的签名方法编写的。假设变量包含的BSTR只是VT_BSTR,而不是VT_BYREF | VT_BSTR HRESULT myfunc(/*[in]*/ VARIANT param),c++,com,variant,bstr,C++,Com,Variant,Bstr,我想将类型更改为其他类型。如果的第一个参数与第二个参数相同,则变量将就地转换 那么,我可以就地转换吗 HRESULT myfunc(/*[in]*/ VARIANT param) { VariantChangeType(¶m, param, 0, VT_I4); } 或者我应该复制到第二个变体 HRESULT myfunc(/*[in]*/ VARIANT param) { VARIANT temp; VariantInit(&temp);
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由客户机拥有,应该由客户机释放。制作副本会更安全,但正如我阅读的组件对象模型规则所示: •以下规则适用于接口成员函数的参数,包括未按值传递的返回值: ◦对于in参数,调用方应分配并释放内存 您正在讨论的情况是一个传递值,而不管该变量是否包含此调用的BSTR。因此,我相信在这种情况下,被调用方拥有该参数,如果要确保其值的持续有效性,则由调用方进行复制。需要与第二个变量一起使用,尽管这可能并不明显 即使变量是按值传递的,变量中包含的任何指针都指向相同的内存地址。因为BSTR是指针,这意味着BSTR的原始地址被传递到函数中,就像参数是BSTR而不是VARIANT一样 就地使用VariantChangeType或将触发,这意味着调用方拥有的原始变量仍然包含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%x\n", (unsigned)(V_BSTR(&input)));
wprintf(L"Contents of BSTR = %s\n", V_BSTR(&input));
myfuncbad(input);
wprintf(L"Memory location of BSTR = 0x%x\n", (unsigned)(V_BSTR(&input)));
wprintf(L"Contents of BSTR = %s\n", V_BSTR(&input));
}
但是为什么呢?原来是这样。因此,即使当VariantChangeType就位或调用VariantClear时,BSTR分配可能会保留一段时间。在传递到这些函数中的变量上,这一点很明显,但变量的任何按值副本可能仍会在一段时间内看到BSTR
无论如何,从技术上讲,BSTR已被myfuncbad释放,调用方不应再引用它。此外,对原始变体调用VariantClear可能会导致错误
补充阅读
就地转换被保存。您不需要时间变量 我在所有ATL COM代码中都使用了它,如下所示:
CComVariant v;
GetSomeData(v); // Assume v returns a VT_BSTR variant.
HRESULT hr = v.ChangeType(VT_I4);
if (FAILED(hr))
...
该代码以所讨论的方式转换为就地转换。
在内部,在旧变量BSTR使用计数值递减之前,使用VarI4FromBSTR计算结果
我在一些调试会话中验证了这一点,因为我也不确定这一点
编辑,最后我在中找到了该语句
对于VT_BSTR,字符串只有一个所有者。所有字符串都在
必须使用SysAllocString函数分配变量。什么时候
释放或更改具有VT_BSTR类型的变体的类型,
对包含的字符串调用SysFreeString
代码在@jveazey的答案中起作用,这与BSTR缓存无关。有一个真正的就地转换 否,VariantChangeType将结果存储到另一个变量中。作为第一个参数传递的参数。因此,它根本没有理由发布BSTR。除非对结果变量做了不明智的操作,使其不为空,例如使用VariantCopy:@HansPassant VariantChangeType的第一个参数可以与第二个参数相同,这意味着变量将被就地转换。所以,也许更好的说法是,我应该就地转换还是转换为第二个变体?我已经更新了问题以澄清。FWIW,我创建了一个要点来演示这个问题:BSTR是由SysAllocString分配的指针。所以,它是一个引用,即使它不是VT_BYREF。所以,从技术上讲,它不是按值传递的。我在这一点上支持@jveazey。调用者总是试图释放其变体,如果任何包含的内存已经释放,那将是一个崩溃。这里重要的是/*[in]*/注释。这意味着idl拥有它,因此传入的缓冲区不是您的。对被叫方来说是只读的。所以你不能改变缓冲区。仅此而已。不幸的是,这是一个危险的答案。在您提供的示例中,这是安全的,因为您正在从方法调用接收变量-一旦该方法调用完成,变量及其包含的BSTR的所有者就是调用方,如果调用方希望转换它,则可以。但是OP的问题是关于将变量及其包含的BSTR传递到方法中的情况。在这种情况下,更改类型是不安全的,因为被调用方将释放某些
调用方拥有的ng,调用方稍后将再次尝试解除分配它。MSDN明确表示,当释放或更改VT_BSTR类型的变量类型时,将对包含的字符串调用SysFreeString。示例中的被调用方myfuncbad是错误的,因为它正在释放调用方拥有的某些内容。被调用方不是OP中的所有者。在方法签名中,参数是[in]变量。在C++中,这是一个变体,而不是变体*。调用该方法时,变量结构将在复制BSTR指针时进行字节复制,而不是复制BSTR本身。这意味着当被调用方使用VariantChangeType时,引用的BSTR将被释放。但是,调用方是所有者,它不知道被调用方释放了BSTR,因为被调用方的VariantChangeType调用没有修改调用方的结构。然后调用方将释放它的变体,它将崩溃。最初的问题并不是问使用VariantChangeType和前两个参数相同是否通常是安全的——它只是在某些情况下是安全的。该问题询问特定场景中的安全性——即在接受[in]变量的被调用方中调用它。您正在为不同的场景提供答案。我已经编写了必要的代码来演示这个问题@MichaelGunter-你应该自己回答:-
CComVariant v;
GetSomeData(v); // Assume v returns a VT_BSTR variant.
HRESULT hr = v.ChangeType(VT_I4);
if (FAILED(hr))
...