oleaut32: Reimplement VarCmp().
- Supports now all WinXP allowed combinations of input variants. - VT_RESERVED on input variants is handled now. - Removed complicated VT_DATE comparision; that's not how Windows does it. - Improved documentation.
This commit is contained in:
parent
e47b813fc6
commit
f73b68b016
|
@ -2500,200 +2500,232 @@ HRESULT WINAPI VarCat(LPVARIANT left, LPVARIANT right, LPVARIANT out)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
/* Wrapper around VariantChangeTypeEx() which permits changing a
|
||||
variant with VT_RESERVED flag set. Needed by VarCmp. */
|
||||
static HRESULT _VarChangeTypeExWrap (VARIANTARG* pvargDest,
|
||||
VARIANTARG* pvargSrc, LCID lcid, USHORT wFlags, VARTYPE vt)
|
||||
{
|
||||
HRESULT res;
|
||||
VARTYPE flags;
|
||||
|
||||
flags = V_VT(pvargSrc) & ~VT_TYPEMASK;
|
||||
V_VT(pvargSrc) &= ~VT_RESERVED;
|
||||
res = VariantChangeTypeEx(pvargDest,pvargSrc,lcid,wFlags,vt);
|
||||
V_VT(pvargSrc) |= flags;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* VarCmp [OLEAUT32.176]
|
||||
*
|
||||
* flags can be:
|
||||
* NORM_IGNORECASE, NORM_IGNORENONSPACE, NORM_IGNORESYMBOLS
|
||||
* Compare two variants.
|
||||
*
|
||||
* PARAMS
|
||||
* left [I] First variant
|
||||
* right [I] Second variant
|
||||
* lcid [I] LCID (locale identifier) for the comparison
|
||||
* flags [I] Flags to be used in the comparision:
|
||||
* NORM_IGNORECASE, NORM_IGNORENONSPACE, NORM_IGNORESYMBOLS,
|
||||
* NORM_IGNOREWIDTH, NORM_IGNOREKANATYPE, NORM_IGNOREKASHIDA
|
||||
*
|
||||
* RETURNS
|
||||
* VARCMP_LT: left variant is less than right variant.
|
||||
* VARCMP_EQ: input variants are equal.
|
||||
* VARCMP_LT: left variant is greater than right variant.
|
||||
* VARCMP_NULL: either one of the input variants is NULL.
|
||||
* Failure: An HRESULT error code indicating the error.
|
||||
*
|
||||
* NOTES
|
||||
* Native VarCmp up to and including WinXP dosn't like as input variants
|
||||
* I1, UI2, VT_UI4, UI8 and UINT. INT is accepted only as left variant.
|
||||
*
|
||||
* If both input variants are ERROR then VARCMP_EQ will be returned, else
|
||||
* an ERROR variant will trigger an error.
|
||||
*
|
||||
* Both input variants can have VT_RESERVED flag set which is ignored
|
||||
* unless one and only one of the variants is a BSTR and the other one
|
||||
* is not an EMPTY variant. All four VT_RESERVED combinations have a
|
||||
* different meaning:
|
||||
* - BSTR and other: BSTR is always greater than the other variant.
|
||||
* - BSTR|VT_RESERVED and other: a string comparision is performed.
|
||||
* - BSTR and other|VT_RESERVED: If the BSTR is a number a numeric
|
||||
* comparision will take place else the BSTR is always greater.
|
||||
* - BSTR|VT_RESERVED and other|VT_RESERVED: It seems that the other
|
||||
* variant is ignored and the return value depends only on the sign
|
||||
* of the BSTR if it is a number else the BSTR is always greater. A
|
||||
* positive BSTR is greater, a negative one is smaller than the other
|
||||
* variant.
|
||||
*
|
||||
* SEE
|
||||
* VarBstrCmp for the lcid and flags usage.
|
||||
*/
|
||||
HRESULT WINAPI VarCmp(LPVARIANT left, LPVARIANT right, LCID lcid, DWORD flags)
|
||||
{
|
||||
BOOL lOk = TRUE;
|
||||
BOOL rOk = TRUE;
|
||||
BOOL l_isR = FALSE;
|
||||
BOOL r_isR = FALSE;
|
||||
LONGLONG lVal = -1;
|
||||
LONGLONG rVal = -1;
|
||||
VARTYPE lvt, rvt, vt;
|
||||
VARIANT rv,lv;
|
||||
DWORD xmask;
|
||||
HRESULT rc;
|
||||
double lDouble =0.0,rDouble=0.0;
|
||||
|
||||
TRACE("(%p->(%s%s),%p->(%s%s),0x%08lx,0x%08lx)\n", left, debugstr_VT(left),
|
||||
debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), lcid, flags);
|
||||
|
||||
VariantInit(&lv);VariantInit(&rv);
|
||||
V_VT(right) &= ~0x8000; /* hack since we sometime get this flag. */
|
||||
V_VT(left) &= ~0x8000; /* hack since we sometime get this flag. */
|
||||
lvt = V_VT(left) & VT_TYPEMASK;
|
||||
rvt = V_VT(right) & VT_TYPEMASK;
|
||||
xmask = (1 << lvt) | (1 << rvt);
|
||||
|
||||
/* If either are null, then return VARCMP_NULL */
|
||||
if ((V_VT(left)&VT_TYPEMASK) == VT_NULL ||
|
||||
(V_VT(right)&VT_TYPEMASK) == VT_NULL)
|
||||
/* If we have any flag set except VT_RESERVED bail out.
|
||||
Same for the left input variant type > VT_INT and for the
|
||||
right input variant type > VT_I8. Yes, VT_INT is only supported
|
||||
as left variant. Go figure */
|
||||
if (((V_VT(left) | V_VT(right)) & ~VT_TYPEMASK & ~VT_RESERVED) ||
|
||||
lvt > VT_INT || rvt > VT_I8) {
|
||||
return DISP_E_BADVARTYPE;
|
||||
}
|
||||
|
||||
/* Don't ask me why but native VarCmp cannot handle: VT_I1, VT_UI2, VT_UI4,
|
||||
VT_UINT and VT_UI8. Tested with DCOM98, Win2k, WinXP */
|
||||
if (rvt == VT_INT || xmask & (VTBIT_I1 | VTBIT_UI2 | VTBIT_UI4 | VTBIT_UI8 |
|
||||
VTBIT_DISPATCH | VTBIT_VARIANT | VTBIT_UNKNOWN | VTBIT_15))
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
|
||||
/* If both variants are VT_ERROR return VARCMP_EQ */
|
||||
if (xmask == VTBIT_ERROR)
|
||||
return VARCMP_EQ;
|
||||
else if (xmask & VTBIT_ERROR)
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
|
||||
if (xmask & VTBIT_NULL)
|
||||
return VARCMP_NULL;
|
||||
|
||||
/* Strings - use VarBstrCmp */
|
||||
if ((V_VT(left)&VT_TYPEMASK) == VT_BSTR &&
|
||||
(V_VT(right)&VT_TYPEMASK) == VT_BSTR) {
|
||||
VariantInit(&lv);
|
||||
VariantInit(&rv);
|
||||
|
||||
/* Two BSTRs, ignore VT_RESERVED */
|
||||
if (xmask == VTBIT_BSTR)
|
||||
return VarBstrCmp(V_BSTR(left), V_BSTR(right), lcid, flags);
|
||||
|
||||
/* A BSTR and an other variant; we have to take care of VT_RESERVED */
|
||||
if (xmask & VTBIT_BSTR) {
|
||||
VARIANT *bstrv, *nonbv;
|
||||
VARTYPE nonbvt;
|
||||
int swap = 0;
|
||||
|
||||
/* Swap the variants so the BSTR is always on the left */
|
||||
if (lvt == VT_BSTR) {
|
||||
bstrv = left;
|
||||
nonbv = right;
|
||||
nonbvt = rvt;
|
||||
} else {
|
||||
swap = 1;
|
||||
bstrv = right;
|
||||
nonbv = left;
|
||||
nonbvt = lvt;
|
||||
}
|
||||
|
||||
xmask = (1<<(V_VT(left)&VT_TYPEMASK))|(1<<(V_VT(right)&VT_TYPEMASK));
|
||||
if (xmask & VTBIT_DECIMAL) {
|
||||
rc = VariantChangeType(&lv,left,0,VT_DECIMAL);
|
||||
if (FAILED(rc)) return rc;
|
||||
rc = VariantChangeType(&rv,right,0,VT_DECIMAL);
|
||||
if (FAILED(rc)) return rc;
|
||||
/* BSTR and EMPTY: ignore VT_RESERVED */
|
||||
if (nonbvt == VT_EMPTY)
|
||||
rc = (!V_BSTR(bstrv) || !*V_BSTR(bstrv)) ? VARCMP_EQ : VARCMP_GT;
|
||||
else {
|
||||
VARTYPE breserv = V_VT(bstrv) & ~VT_TYPEMASK;
|
||||
VARTYPE nreserv = V_VT(nonbv) & ~VT_TYPEMASK;
|
||||
|
||||
if (!breserv && !nreserv)
|
||||
/* No VT_RESERVED set ==> BSTR always greater */
|
||||
rc = VARCMP_GT;
|
||||
else if (breserv && !nreserv) {
|
||||
/* BSTR has VT_RESERVED set. Do a string comparision */
|
||||
rc = VariantChangeTypeEx(&rv,nonbv,lcid,0,VT_BSTR);
|
||||
if (FAILED(rc))
|
||||
return rc;
|
||||
rc = VarBstrCmp(V_BSTR(bstrv), V_BSTR(&rv), lcid, flags);
|
||||
} else if (V_BSTR(bstrv) && *V_BSTR(bstrv)) {
|
||||
/* Non NULL nor empty BSTR */
|
||||
/* If the BSTR is not a number the BSTR is greater */
|
||||
rc = _VarChangeTypeExWrap(&lv,bstrv,lcid,0,VT_R8);
|
||||
if (FAILED(rc))
|
||||
rc = VARCMP_GT;
|
||||
else if (breserv && nreserv)
|
||||
/* FIXME: This is strange: with both VT_RESERVED set it
|
||||
looks like the result depends only on the sign of
|
||||
the BSTR number */
|
||||
rc = (V_R8(&lv) >= 0) ? VARCMP_GT : VARCMP_LT;
|
||||
else
|
||||
/* Numeric comparision, will be handled below.
|
||||
VARCMP_NULL used only to break out. */
|
||||
rc = VARCMP_NULL;
|
||||
VariantClear(&lv);
|
||||
VariantClear(&rv);
|
||||
} else
|
||||
/* Empty or NULL BSTR */
|
||||
rc = VARCMP_GT;
|
||||
}
|
||||
/* Fixup the return code if we swapped left and right */
|
||||
if (swap) {
|
||||
if (rc == VARCMP_GT)
|
||||
rc = VARCMP_LT;
|
||||
else if (rc == VARCMP_LT)
|
||||
rc = VARCMP_GT;
|
||||
}
|
||||
if (rc != VARCMP_NULL)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (xmask & VTBIT_DECIMAL)
|
||||
vt = VT_DECIMAL;
|
||||
else if (xmask & VTBIT_BSTR)
|
||||
vt = VT_R8;
|
||||
else if (xmask & VTBIT_R4)
|
||||
vt = VT_R4;
|
||||
else if (xmask & (VTBIT_R8 | VTBIT_DATE))
|
||||
vt = VT_R8;
|
||||
else if (xmask & VTBIT_CY)
|
||||
vt = VT_CY;
|
||||
else
|
||||
/* default to I8 */
|
||||
vt = VT_I8;
|
||||
|
||||
/* Coerce the variants */
|
||||
rc = _VarChangeTypeExWrap(&lv,left,lcid,0,vt);
|
||||
if (rc == DISP_E_OVERFLOW && vt != VT_R8) {
|
||||
/* Overflow, change to R8 */
|
||||
vt = VT_R8;
|
||||
rc = _VarChangeTypeExWrap(&lv,left,lcid,0,vt);
|
||||
}
|
||||
if (FAILED(rc))
|
||||
return rc;
|
||||
rc = _VarChangeTypeExWrap(&rv,right,lcid,0,vt);
|
||||
if (rc == DISP_E_OVERFLOW && vt != VT_R8) {
|
||||
/* Overflow, change to R8 */
|
||||
vt = VT_R8;
|
||||
rc = _VarChangeTypeExWrap(&lv,left,lcid,0,vt);
|
||||
if (FAILED(rc))
|
||||
return rc;
|
||||
rc = _VarChangeTypeExWrap(&rv,right,lcid,0,vt);
|
||||
}
|
||||
if (FAILED(rc))
|
||||
return rc;
|
||||
|
||||
#define _VARCMP(a,b) \
|
||||
(((a) == (b)) ? VARCMP_EQ : (((a) < (b)) ? VARCMP_LT : VARCMP_GT))
|
||||
|
||||
switch (vt) {
|
||||
case VT_CY:
|
||||
return VarCyCmp(V_CY(&lv), V_CY(&rv));
|
||||
case VT_DECIMAL:
|
||||
return VarDecCmp(&V_DECIMAL(&lv), &V_DECIMAL(&rv));
|
||||
}
|
||||
if (xmask & VTBIT_R8) {
|
||||
rc = VariantChangeType(&lv,left,0,VT_R8);
|
||||
if (FAILED(rc)) return rc;
|
||||
rc = VariantChangeType(&rv,right,0,VT_R8);
|
||||
if (FAILED(rc)) return rc;
|
||||
|
||||
if (V_R8(&lv) == V_R8(&rv)) return VARCMP_EQ;
|
||||
if (V_R8(&lv) < V_R8(&rv)) return VARCMP_LT;
|
||||
if (V_R8(&lv) > V_R8(&rv)) return VARCMP_GT;
|
||||
return E_FAIL; /* can't get here */
|
||||
}
|
||||
if (xmask & VTBIT_R4) {
|
||||
rc = VariantChangeType(&lv,left,0,VT_R4);
|
||||
if (FAILED(rc)) return rc;
|
||||
rc = VariantChangeType(&rv,right,0,VT_R4);
|
||||
if (FAILED(rc)) return rc;
|
||||
|
||||
if (V_R4(&lv) == V_R4(&rv)) return VARCMP_EQ;
|
||||
if (V_R4(&lv) < V_R4(&rv)) return VARCMP_LT;
|
||||
if (V_R4(&lv) > V_R4(&rv)) return VARCMP_GT;
|
||||
return E_FAIL; /* can't get here */
|
||||
}
|
||||
|
||||
/* Integers - Ideally like to use VarDecCmp, but no Dec support yet
|
||||
Use LONGLONG to maximize ranges */
|
||||
lOk = TRUE;
|
||||
switch (V_VT(left)&VT_TYPEMASK) {
|
||||
case VT_I1 : lVal = V_I1(left); break;
|
||||
case VT_I2 : lVal = V_I2(left); break;
|
||||
case VT_I4 :
|
||||
case VT_INT : lVal = V_I4(left); break;
|
||||
case VT_UI1 : lVal = V_UI1(left); break;
|
||||
case VT_UI2 : lVal = V_UI2(left); break;
|
||||
case VT_UI4 :
|
||||
case VT_UINT : lVal = V_UI4(left); break;
|
||||
case VT_BOOL : lVal = V_BOOL(left); break;
|
||||
case VT_EMPTY : lVal = 0; break;
|
||||
case VT_R4 : lDouble = V_R4(left); lOk = FALSE; l_isR= TRUE; break;
|
||||
case VT_R8 : lDouble = V_R8(left); lOk = FALSE; l_isR= TRUE; break;
|
||||
default: lOk = FALSE;
|
||||
}
|
||||
|
||||
rOk = TRUE;
|
||||
switch (V_VT(right)&VT_TYPEMASK) {
|
||||
case VT_I1 : rVal = V_I1(right); break;
|
||||
case VT_I2 : rVal = V_I2(right); break;
|
||||
case VT_I4 :
|
||||
case VT_INT : rVal = V_I4(right); break;
|
||||
case VT_UI1 : rVal = V_UI1(right); break;
|
||||
case VT_UI2 : rVal = V_UI2(right); break;
|
||||
case VT_UI4 :
|
||||
case VT_UINT : rVal = V_UI4(right); break;
|
||||
case VT_BOOL : rVal = V_BOOL(right); break;
|
||||
case VT_EMPTY : rVal = 0; break;
|
||||
case VT_R4 : rDouble = V_R4(right); rOk = FALSE;r_isR= TRUE; break;
|
||||
case VT_R8 : rDouble = V_R8(right); rOk = FALSE;r_isR= TRUE; break;
|
||||
default: rOk = FALSE;
|
||||
}
|
||||
|
||||
if (lOk && rOk) {
|
||||
if (lVal < rVal) {
|
||||
return VARCMP_LT;
|
||||
} else if (lVal > rVal) {
|
||||
return VARCMP_GT;
|
||||
} else {
|
||||
return VARCMP_EQ;
|
||||
}
|
||||
}
|
||||
else if (l_isR && r_isR) {
|
||||
if (lDouble < rDouble) {
|
||||
return VARCMP_LT;
|
||||
} else if (lDouble > rDouble) {
|
||||
return VARCMP_GT;
|
||||
} else {
|
||||
return VARCMP_EQ;
|
||||
}
|
||||
}
|
||||
else if (lOk && r_isR) {
|
||||
if (lVal < rDouble) {
|
||||
return VARCMP_LT;
|
||||
} else if (lVal > rDouble) {
|
||||
return VARCMP_GT;
|
||||
} else {
|
||||
return VARCMP_EQ;
|
||||
}
|
||||
}
|
||||
else if (l_isR && rOk) {
|
||||
if (lDouble < rVal) {
|
||||
return VARCMP_LT;
|
||||
} else if (lDouble > rVal) {
|
||||
return VARCMP_GT;
|
||||
} else {
|
||||
return VARCMP_EQ;
|
||||
}
|
||||
}
|
||||
if ((V_VT(left)&VT_TYPEMASK) == VT_BSTR ) {
|
||||
if(((V_VT(right)&VT_TYPEMASK) == VT_EMPTY ) && !(V_BSTR(left))) {
|
||||
return VARCMP_EQ;
|
||||
} else {
|
||||
return VARCMP_GT;
|
||||
}
|
||||
}
|
||||
if ((V_VT(right)&VT_TYPEMASK) == VT_BSTR ) {
|
||||
if(((V_VT(left)&VT_TYPEMASK) == VT_EMPTY ) && !(V_BSTR(right))) {
|
||||
return VARCMP_EQ;
|
||||
} else {
|
||||
return VARCMP_LT;
|
||||
}
|
||||
}
|
||||
/* Dates */
|
||||
if ((V_VT(left)&VT_TYPEMASK) == VT_DATE &&
|
||||
(V_VT(right)&VT_TYPEMASK) == VT_DATE) {
|
||||
|
||||
if (floor(V_DATE(left)) == floor(V_DATE(right))) {
|
||||
/* Due to floating point rounding errors, calculate varDate in whole numbers) */
|
||||
double wholePart = 0.0;
|
||||
double leftR;
|
||||
double rightR;
|
||||
|
||||
/* Get the fraction * 24*60*60 to make it into whole seconds */
|
||||
wholePart = (double) floor( V_DATE(left) );
|
||||
if (wholePart == 0) wholePart = 1;
|
||||
leftR = floor(fmod( V_DATE(left), wholePart ) * (24*60*60));
|
||||
|
||||
wholePart = (double) floor( V_DATE(right) );
|
||||
if (wholePart == 0) wholePart = 1;
|
||||
rightR = floor(fmod( V_DATE(right), wholePart ) * (24*60*60));
|
||||
|
||||
if (leftR < rightR) {
|
||||
return VARCMP_LT;
|
||||
} else if (leftR > rightR) {
|
||||
return VARCMP_GT;
|
||||
} else {
|
||||
return VARCMP_EQ;
|
||||
}
|
||||
|
||||
} else if (V_DATE(left) < V_DATE(right)) {
|
||||
return VARCMP_LT;
|
||||
} else if (V_DATE(left) > V_DATE(right)) {
|
||||
return VARCMP_GT;
|
||||
}
|
||||
}
|
||||
else if((V_VT(right)&VT_TYPEMASK) == VT_EMPTY)
|
||||
return VARCMP_GT;
|
||||
FIXME("VarCmp partial implementation, doesn't support %s / %s\n",wine_vtypes[V_VT(left)], wine_vtypes[V_VT(right)]);
|
||||
case VT_I8:
|
||||
return _VARCMP(V_I8(&lv), V_I8(&rv));
|
||||
case VT_R4:
|
||||
return _VARCMP(V_R4(&lv), V_R4(&rv));
|
||||
case VT_R8:
|
||||
return _VARCMP(V_R8(&lv), V_R8(&rv));
|
||||
default:
|
||||
/* We should never get here */
|
||||
return E_FAIL;
|
||||
}
|
||||
#undef _VARCMP
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* VarAnd [OLEAUT32.142]
|
||||
|
|
Loading…
Reference in New Issue