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

问题描述 投票:1回答:1

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。

[[0]存储在变量中时会发生什么;定义位于包含DECIMALstruct之外,那么调用方如何确定是否存在有效的类型标志或仅是十进制的一些字节?小数需要12个字节来存储,而Variant可以容纳16个字节,因此可能利用了此信息,但是不是存储在联合体较小成员的未定义行为的剩余4个字节中的内容是什么?

c++ com decimal unions variant
1个回答
0
投票

这是一个有趣的问题。可悲的是,我还没有找到任何关于此的坚定文件。我可以从一些思考和实验中得出一些推论。

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

我提供了两个经验证明。

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

vt

使用WinDbg检查v,我获得了Dim v As Variant v = CDec(24) 变量的以下布局:

v

[好吧,VB6在奇怪的地方还没有作弊,而且微软总是不会将Decimal公开为完整类型(出于某些原因,您不能在VB6中声明Decimal类型的变量;它必须存储在变体。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) 的文档听起来像他们打算支持Decimal并出于某种原因不得不将其拔出)。因此,这可能只是VB6作弊。但是:

2)我测试了一下,如果我要求COM API将DECIMAL放入VARIANT中,它将执行什么操作。对于踢球,我使用VC6 ++对此进行了测试:

Dim

我确认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。如果在VARIANT中将DECIMAL按值存储在形式上是非法的,那么我会预期会出现错误HRESULT。相反,布局与我的VB6经验相匹配:

  • 监视窗口将S_OK的值报告为t
  • {24 VT_DECIMAL}成员设置为14(这是VT_DECIMAL)
  • t.vt成员被列为wReserved == 14; Lo64 == 24; Hi32 == 0

因此,尽管VARIANT的标头声明暗示了什么,但vt成员可以并且应该用于确定VARIANT何时包含DECIMAL。实际上,如果您从未详细检查过VARIANT声明,就不会知道DECIMAL会被区别对待。


我剩下的问题是“为什么不让DECIMAL像其他人一样适合工会?”]

如果不知道VARIANT和DECIMAL的完整历史,可能很难给出完整的答案;但是密钥可能不在t.decVal中,而是在vtwReserved1wReserved2中。

DECIMAL似乎是VARIANT的更高版本。克雷格·布罗克施密特(Kraig Brockschmidt)的经典著作“ Inside Ole”(第二版,1995年发行)给出了VARIANT的声明,但没有提及DECIMAL作为其中一种选择。这意味着之后在某些时候添加了DECIMAL作为VARIANT选项。不迟于Visual C ++ 6(1998),DECIMAL已经可以作为VARIANT类型使用。

但是DECIMAL的有趣部分(14字节)太大,无法容纳先前存在的VARIANT联合。 DECIMAL需要使用三个wReserved3字段占用的字节(可能最初打算用作填充)。我敢肯定,Microsoft不可能在不更改内存布局和破坏旧二进制文件的情况下重新定义VARIANT联合,以使保留字段可用于联合和DECIMAL。也许wReservedX最初是放在wReservedX字段之前的,但这不是这样做的。

因此,一个理论是Microsoft需要将此新的14字节长类型添加到VARIANT中,这可能无法容纳可用于联合的8字节。根据这种理论,当前的VARIANT布局将是在二进制级别潜行DECIMAL而不破坏VARIANT的原始声明的一种方法。编译时,DECIMAL只是“联盟”的另一个成员,除了它可以溢出到保留字的空间中。

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

© www.soinside.com 2019 - 2024. All rights reserved.