我使用很少的CComVariant类型变量来存储接口指针。但是,有时我需要将接口指针传递为NULL。在这种情况下,当我这样做时:
CComVariant vAData,vBData;
......
....
CComQIPtr<IBData> pAData = vAData.punkVal; //vAData is {0, VT_I4} when I pass NULL
CComQIPtr<IBData>pBData = vBData.punkVal; //vBData is {0, VT_I4} when I pass NULL
第一行失败并抛出异常,因为vAData.punkVal = 0xffffffff00000000
但第二行没有错误,并且有效vBData.punkVal
值(0x0000000000000000)。
我想知道为什么两个punkVal都是不同的,当它们都是NULL? 有没有人知道为什么会这样? 这仅在64位计算机上引发异常。
答案 0 :(得分:4)
关于VARIANT的一些额外信息:它们有点奇怪,因为它们是复合类型:它基本上是几种类型的并集,每种类型的字段(bstrVal,lVal,punkVal等)都占用相同的空间。内存,以及指示哪个字段有效的vt字段。
您应该只尝试访问与vt值匹配的字段。所以如果vt是VT_BSTR,那么只有bstrVal有效; punkVal是禁区。如果vt是VT_I4,则只应使用lVal字段。
VT_EMPTY表示“此变体尚未设置为任何值,因此不代表任何内容”;当vt是VT_EMPTY时,所有字段都是禁止的 - 它们可能是之前发生在堆栈上的垃圾 - 这就是你在这里看到的。
在Win64中,整数仍为32位,但指针为64位。类型为VT_I4且值为0的变体将具有表示整数全部设置为0的32位,但变体中的其余内存可能是任何剩余的垃圾。如果您尝试将该内存视为64位指针 - 通过访问punkVal - 那么您最终会得到一个由32 0位构成的指针,但也来自相邻的32位,这些指针从未被正确初始化 - 这可能就是为什么你在这里看到两种情况之间存在差异。
在Win32中,你很幸运:指针与int的大小相同,所以如果你访问一个0的VT_I4并读取punkVal(你不应该!),你最终会得到一个NULL指针那种情况。
答案 1 :(得分:3)
CComVariant
在构造函数中调用VariantInit()
并将vt
设置为VT_EMPTY
,但保留punkVal
未初始化(不会使其为null)。
因此,尝试构建一个CComQIPtr
传递未初始化的指针时,您尝试执行的操作是未定义的行为。
如果您希望CComVariant
持有空IUnknown*
,则可以执行此操作:
CComVariant variant( static_cast<IUnknown*>( 0 ) ); // null IUnknown*, VT_UNKNOWN type
现在构建CComQIPtr
:
CComQIPtr<IWhatever> whatever( variant.punkVal ); //punknVal is null - legal