Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/164.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我可以对输入BSTR变量调用VariantChangeType吗? 我有一个C++对象,用下面的签名方法编写的。假设变量包含的BSTR只是VT_BSTR,而不是VT_BYREF | VT_BSTR HRESULT myfunc(/*[in]*/ VARIANT param)_C++_Com_Variant_Bstr - Fatal编程技术网

我可以对输入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(&param, param, 0, VT_I4); } 或者我应该复制到第二个变体 HRESULT myfunc(/*[in]*/ VARIANT param) { VARIANT temp; VariantInit(&temp);

我想将类型更改为其他类型。如果的第一个参数与第二个参数相同,则变量将就地转换

那么,我可以就地转换吗

HRESULT myfunc(/*[in]*/ VARIANT param)
{
    VariantChangeType(&param, 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(&param, &param, 0, VT_I4);
    return S_OK;
}

HRESULT myfuncgood(/*[in]*/ VARIANT param)
{
    VARIANT temp;
    VariantInit(&temp);
    // Copy and convert into a new VARIANT
    VariantChangeType(&temp, &param, 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))
    ...