C++ 来电者如何知道何时有';变量中有小数吗?
COMC++ 来电者如何知道何时有';变量中有小数吗?,c++,com,decimal,unions,variant,C++,Com,Decimal,Unions,Variant,COMVARIANT类型是使用tagVARIANT结构定义的,如下所示: typedef struct tagVARIANT { union { struct { VARTYPE vt; WORD wReserved1; WORD wReserved2; WORD wReserved3; union { LONGLONG llVal; LONG lVa
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;
通常,当调用者想要在变量中使用数据时,它会使用VARTYPE vt
标志查看存储的数据类型,以及最终如何解释这些1和0
当变量中存储了
十进制
时会发生什么;该定义位于包含vt
的struct
之外,因此调用方如何确定是否存在有效的类型标志或小数的某些字节?十进制需要12个14字节来存储,而变量可以容纳16个字节,因此可能利用了此信息,但工会较小成员的备用2字节中存储的内容不是未定义的行为吗?这是一个有趣的问题。遗憾的是,我还没有找到任何关于这方面的文件。我可以从一些思考和实验中做出一些推论
尽管有官方文档和标题中的类型定义,但存储在变体中的十进制似乎使用了重叠的vt
变体成员的十进制wReserved
成员字节。因此,通过查看vt
成员,变量中的小数点与任何其他变量类型的识别方式相同
我提出了两个经验证明
1) 我编译了一个VB6程序,将十进制数存储在变量中(本机代码,无优化,生成符号调试信息)。然后,我使用了旧版本的WinDbg来检查变量的位(当前版本的WinDbg与VB6的旧PDB格式不兼容-我想我可以尝试使用VC6来代替它,但没有考虑)
使用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,看看如果我要求它在变量中输入一个小数,它会做什么。对于kicks,我使用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\u OK
。如果在变量中按值存储十进制在形式上是非法的,我会预料到一个错误HRESULT。相反,该布局与我使用VB6的经验相匹配:
- 监视窗口将
的值报告为t
{24 VT_DECIMAL}
成员被设置为14(即vt\u十进制)t.vt
成员被列为wReserved==14;Lo64==24;Hi32==0t.decVal
留给我的问题是“为什么不让十进制像其他人一样适合工会?” 如果不知道变量和小数的完整历史,可能很难给出完整的答案;但关键可能不在
vt
中,而是在wReserved1
、wReserved2
和wReserved3
中
DECIMAL似乎是后来对VARIANT的添加。Kraig Brockschmidt的经典著作“Inside Ole”(1995年第二版)给出了变体声明,但没有提到十进制作为选项之一。这意味着十进制作为一个变量选项是在之后的某个点添加的。不迟于Visual C++ 6(1998),十进制已经可用作为变体类型。
但十进制的有趣部分(14字节)太大,无法容纳先前存在的变量联合。DECIMAL需要使用三个wReservedX
字段所占用的字节(可能最初用作填充)。我敢肯定,微软不可能在不改变内存布局和破坏旧二进制文件的情况下,重新定义变量union,使保留字段可用于union和DECIMAL
因此,有一种理论认为微软需要将这种新的14字节长的类型添加到VARIANT中,而VARIANT不可能适合工会可用的8字节。根据这一理论,当前的VARIANT布局将是一种在不破坏VARIANT原始声明的情况下在二进制级别偷偷输入十进制的方法。编译时,DECIMAL只是“union”的另一个成员,只是它可能溢出到保留字的空间中
可能还有另一个怪癖。Hans Passant在上面的评论中提到,保留字段用于包含货币类型信息。这听起来很可行,但我无法证实这一点,因为我还没有找到任何关于十进制的旧用法的信息。假设这是真的,微软将被约束在预先存在的十进制类型的布局上(即,不可能考虑牺牲范围以使其适合作为常规成员)。此外,他们还必须决定放弃“货币类型”信息,以换取在变量中使用十进制(或者他们可能已经放弃了货币类型信息,或者出于其他原因)。如果没有更多关于十进制在作为变量类型添加之前是如何使用的信息,我就说不出来了。@user253751哦,我明白了,我误解了它是什么,但是
十进制是一个12字节的无符号整数+2字节的符号/刻度+2字节的保留,所以完整的16字节确实被定义了。但是保留的2个字节中有什么?保留的2个字节恰好重叠vtVARIANT s;
VARIANT t;
VariantInit(&s);
VariantInit(&t);
V_VT(&s) = VT_I4;
V_I4(&s) = 24;
HRESULT hr = VariantChangeType(&t, &s, 0, VT_DECIMAL);