Это интригующий вопрос. К сожалению, я не смог найти никакой твердой документации по этому поводу. Я могу сделать некоторые выводы из небольшого количества размышлений и экспериментов.
Несмотря на официальную документацию и определения типов в заголовках - DECIMAL, хранящийся в VARIANT, действительно использует байты члена DECIMAL wReserved
для перекрывающийся vt
VARIANT член. Поэтому DECIMAL в VARIANT идентифицируется так же, как и любой другой тип VARIANT, глядя на член vt
.
Я представляю два эмпирических доказательства.
1) Я собрал VB6 программа для хранения DECIMAL в VARIANT (собственный код, без оптимизаций, генерирование символов c отладочная информация). Затем я использовал старую версию WinDbg для проверки битов переменной (текущие версии WinDbg не совместимы с более старым форматом PDB в VB6 - думаю, я мог бы вместо этого использовать VC6, но не думал об этом).
Dim v As Variant
v = CDec(24)
Проверяя v с помощью WinDbg, я получил следующий макет для переменной v
:
0e 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00
----- ----- ----------- -----------------------
| | | |
| | | Lo64
| | Hi32
| signscale
wReserved
(but note it's the same as v.vt == VT_DECIMAL)
Хорошо, VB6 не обманывает в странных местах, и это всегда кажется Странно, что Microsoft не представила бы Десятичное как полный тип (по какой-то причине вы не можете объявить переменную типа Десятичное в VB6; она должна храниться в Варианте. Документация для Dim
звучит так, как будто они предназначены для поддержки Десятичного и пришлось вытащить его по какой-то причине). Так что возможно это просто чит VB6. Однако:
2) Я проверил, что бы сделал COM API, если бы попросил поместить DECIMAL в VARIANT. Для ударов я использовал VC6 ++, чтобы проверить это:
VARIANT s;
VARIANT t;
VariantInit(&s);
VariantInit(&t);
V_VT(&s) = VT_I4;
V_I4(&s) = 24;
HRESULT hr = VariantChangeType(&t, &s, 0, VT_DECIMAL);
Я подтвердил, что hr
было S_OK
. Если было формально незаконно хранить DECIMAL по значению в VARIANT, я бы ожидал ошибку HRESULT. Вместо этого макет соответствовал моему опыту с VB6:
- Окно просмотра сообщило значение
t
как {24 VT_DECIMAL}
- Элемент
t.vt
был установлен в 14 (что VT_DECIMAL) - Элемент
t.decVal
был указан как wReserved == 14; Lo64 == 24; Hi32 == 0
Поэтому, несмотря на то, что подразумевается в объявлении заголовка VARIANT, член vt может и должен использоваться для определения, когда VARIANT содержит DECIMAL. На самом деле, если бы вы никогда не проверяли детально декларацию VARIANT, вы бы никогда не узнали, что с DECIMAL обращаются по-другому.
У меня остался вопрос: почему бы просто не сделать так, чтобы DECIMAL поместил его в союз? как и все остальные? ".
Может быть трудно дать полный ответ, не зная полной истории VARIANT и DECIMAL; но ключ, вероятно, не в vt
, а в wReserved1
, wReserved2
и wReserved3
.
DECIMAL представляется более поздним дополнением к VARIANT. Классическая книга Крейга Брокшмидта c "Inside Ole" (2-е издание от 1995 г.) дает объявление VARIANT, но не упоминает DECIMAL в качестве одного из вариантов. Это означает, что DECIMAL как опция VARIANT была добавлена в какой-то момент позже. Не позднее, чем Visual C ++ 6 (1998), DECIMAL уже был доступен как тип VARIANT.
Но интересные части DECIMAL (14 байт) слишком велики, чтобы вписаться в существовавший ранее союз VARIANT. DECIMAL должен использовать байты, занятые тремя полями wReservedX
(вероятно, изначально предназначенными для заполнения). Я почти уверен, что Microsoft не могла бы переопределить объединение VARIANT, чтобы сделать зарезервированные поля доступными объединению и DECIMAL, не меняя структуру памяти и не ломая старые двоичные файлы.
Итак, одна из теорий заключается в том, что Microsoft нужно было добавить этот новый 14-байтовый длинный тип в VARIANT, который не мог поместиться в 8 байтов, доступных для объединения. Согласно этой теории, текущая схема VARIANT будет способом проникнуть в DECIMAL на двоичном уровне, не нарушая первоначальное объявление VARIANT. При компиляции DECIMAL будет просто еще одним членом "союза", за исключением того, что он может переполниться в пространство зарезервированных СЛОВ.
Может быть еще одна причуда. Ханс Пассант упоминает в комментарии выше, что зарезервированные поля используются для хранения информации о типе валюты. Это звучит очень выполнимо, но я не могу подтвердить это, потому что я не нашел никакой информации о более старых применениях DECIMAL. Предполагая, что это правда, Microsoft была бы ограничена макетом ранее существовавшего типа DECIMAL (т. Е. Нельзя было пожертвовать диапазоном, чтобы он соответствовал обычному члену). Кроме того, им пришлось бы решить, что они могут обойтись без информации о «типе валюты» в обмен на то, чтобы заставить DECIMAL работать в VARIANT (или они могли уже отказаться от информации о типе валюты ранее, или по другой причине). Я не могу сказать без дополнительной информации о том, как DECIMAL использовался до того, как они были добавлены как тип VARIANT.