调用方如何知道VARIANT中是否存在小数?

COM VARIANT类型是使用tagvariant结构定义的,如下所示:

typedef struct tagvariant {
  union {
    struct {
      VARTYPE vt;
      WORD    wReserved1;
      WORD    wReserved2;
      WORD    wReserved3;
      union {
        LONGLONG     llVal;
        LONG         lVal;
        BYTE         bVal;
        SHORT        iVal;
        FLOAT        fltVal;
        DOUBLE       dblVal;
        VARIANT_BOOL boolVal;
        VARIANT_BOOL __OBSOLETE__VARIANT_BOOL;
        SCODE        scode;
        CY           cyVal;
        DATE         date;
        BSTR         bstrVal;
        iunknown     *punkVal;
        IDispatch    *pdispVal;
        SAFEARRAY    *parray;
        BYTE         *pbVal;
        SHORT        *piVal;
        LONG         *plVal;
        LONGLONG     *pllVal;
        FLOAT        *pfltVal;
        DOUBLE       *pdblVal;
        VARIANT_BOOL *pboolVal;
        VARIANT_BOOL *__OBSOLETE__VARIANT_PBOOL;
        SCODE        *pscode;
        CY           *pcyVal;
        DATE         *pdate;
        BSTR         *pbstrVal;
        iunknown     **ppunkVal;
        IDispatch    **ppdispVal;
        SAFEARRAY    **pparray;
        VARIANT      *pvarVal;
        PVOID        byref;
        CHAR         cVal;
        USHORT       uiVal;
        ULONG        ulVal;
        ULONGLONG    ullVal;
        INT          intVal;
        UINT         uintVal;
        DECIMAL      *pdecVal;
        CHAR         *pcVal;
        USHORT       *puiVal;
        ULONG        *pulVal;
        ULONGLONG    *pullVal;
        INT          *pintVal;
        UINT         *puintVal;
        struct {
          PVOID       pvRecord;
          IRecordInfo *pRecInfo;
        } __VARIANT_NAME_4;
      } __VARIANT_NAME_3;
    } __VARIANT_NAME_2;
    DECIMAL decVal;
  } __VARIANT_NAME_1;
} VARIANT;

通常,当调用者想使用Variant中的数据时,它使用VARTYPE vt标志来查看存储的数据类型,以及最终应如何解释这些1和0。

DECIMAL存储在变量中时会发生什么;定义位于包含struct的{​​{1}}之外,那么调用方如何确定是否存在有效的类型标志或仅十进制的某些字节?十进制需要 12 * 14个字节来存储,而Variant可以容纳16个字节,因此可能利用了此信息,但不是存储在较小的成员的2个备用字节中的信息工会的不确定行为?

gff138 回答:调用方如何知道VARIANT中是否存在小数?

这是一个有趣的问题。遗憾的是,我还没有找到任何关于此的可靠文档。我可以从一些思考和实验中得出一些推断。

尽管标头中有官方文档和类型定义,但存储在VARIANT中的DECIMAL确实使用了DECIMAL wReserved成员的字节作为重叠的vt VARIANT成员。因此,通过查看vt成员,可以以与其他任何VARIANT类型相同的方式识别VARIANT中的DECIMAL。

我提供了两个经验证明。

1)我编译了一个VB6程序,将DECIMAL存储在VARIANT中(本机代码,无优化,生成符号调试信息)。然后,我使用了旧版本的WinDbg来检查变量的位(当前版本的WinDbg与VB6的较早PDB格式不兼容-我想我可以尝试使用VC6代替它,但并未考虑)。

Dim v As Variant
v = CDec(24)

使用WinDbg检查v,我获得了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在奇怪的地方还没有作弊,而且微软不会将Decimal公开为完整类型似乎总是很奇怪(由于某些原因,您不能在VB6中声明Decimal类型的变量;它必须存储在Dim的文档听起来好像他们打算支持Decimal并出于某种原因不得不将其撤出)。因此,这可能只是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,VT_DECIMAL);

我确认hrS_OK。如果在VARIANT中将DECIMAL按值存储在形式上是非法的,那么我会预期会出现错误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中,而在wReserved1wReserved2wReserved3中。

DECIMAL似乎是VARIANT的更高版本。克雷格·布罗克施密特(Kraig Brockschmidt)的经典著作“ Inside Ole”(第二版,日期为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只是“联盟”的另一个成员,只不过它可能溢出到保留字的空间中。

可能还有另一个怪癖。汉斯·帕桑(Hans Passant)在上面的评论中提到,保留字段用于包含货币类型信息。听起来很可行,但我无法证实这一点,因为我没有找到有关DECIMAL的较早使用的任何信息。假设这是真的,Microsoft将会受到先前DECIMAL类型的布局的限制(即不可能考虑牺牲范围以使其适合常规成员)。另外,他们将不得不决定可以放弃“货币类型”信息以换取在变量中进行DECIMAL工作(或者他们可能早些时候已经放弃了货币类型信息,或出于其他原因)。我不能不知道在添加DECIMAL作为VARIANT类型之前如何使用的更多信息。

本文链接:https://www.f2er.com/2717241.html

大家都在问