gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 05:06:21 26 4
gpt4 key购买 nike

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存储在Variant中时会发生什么;定义位于包含 structvt之外,那么调用方如何确定是否存在有效的类型标志或仅十进制的某些字节?小数需要12个 * 14个字节来存储,而Variant可以容纳16个,因此可能利用了此信息,但是不是存储在联合体未定义行为的较小成员的备用2个字节中的内容吗?

最佳答案

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

尽管有 header 中的官方文档和类型定义,但存储在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在奇怪的地方并没有作弊,而且Microsoft不会将Decimal公开为完整类型似乎总是很奇怪(由于某些原因,您不能在VB6中声明Decimal类型的变量;它必须存储在Variant中。 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, 0, 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的 header 声明暗示了什么,但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类型之前,我无法不知道有关如何使用DECIMAL的更多信息。

    关于c++ - 调用方如何知道VARIANT中是否存在小数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59899919/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com