Вариант типа punkVal дает неопределенные значения? - PullRequest
2 голосов
/ 09 июня 2011

Я использую несколько переменных типа 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-битных машинах.

Ответы [ 2 ]

4 голосов
/ 10 июня 2011

Некоторая дополнительная информация о 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 будет иметь 32 бита, представляющих целое число, установленное в 0, но остальная часть памяти в варианте может быть любой оставшейся фигней.Если вы попытаетесь обработать эту память как 64-битный указатель - путем доступа к punkVal - тогда вы получите указатель, созданный из этих 32 0 битов, но также из смежных 32 битов, которые никогда не были должным образом инициализированы - вероятно, поэтомувы видите различия между этими двумя случаями здесь.

В Win32 вам повезло: указатель имеет тот же размер, что и int, так что если вы обращаетесь к VT_I4, равному 0, и читаете punkVal (что вам не следует't!), в этом случае вы получите нулевой указатель.

3 голосов
/ 09 июня 2011

CComVariant вызывает VariantInit() в конструкторе и устанавливает vt на VT_EMPTY, но оставляет punkVal неинициализированным (не делает его нулевым).

То, что вы пытаетесь сделать, это поэтому неопределенное поведение, так как вы пытаетесь создать CComQIPtr, передавая ему неинициализированный указатель.

Если вы хотите, чтобы CComVariant содержал ноль IUnknown*, вы можете сделать это:

CComVariant variant( static_cast<IUnknown*>( 0 ) ); // null IUnknown*, VT_UNKNOWN type

теперь совершенно законно построить CComQIPtr:

CComQIPtr<IWhatever> whatever( variant.punkVal ); //punknVal is null - legal
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...