diff --git a/dlls/oleaut32/oleaut32.spec b/dlls/oleaut32/oleaut32.spec index 71acd0d2ee1..6b6c39967ad 100644 --- a/dlls/oleaut32/oleaut32.spec +++ b/dlls/oleaut32/oleaut32.spec @@ -170,7 +170,7 @@ 172 stdcall VarInt(ptr ptr) 173 stdcall VarNeg(ptr ptr) 174 stdcall VarNot(ptr ptr) -175 stub VarRound # stdcall (ptr long ptr) +175 stdcall VarRound(ptr long ptr) 176 stdcall VarCmp(ptr ptr long long) 177 stdcall VarDecAdd(ptr ptr ptr) 178 stdcall VarDecDiv(ptr ptr ptr) diff --git a/dlls/oleaut32/tests/vartest.c b/dlls/oleaut32/tests/vartest.c index 9d774c4b278..eb80a977924 100644 --- a/dlls/oleaut32/tests/vartest.c +++ b/dlls/oleaut32/tests/vartest.c @@ -2548,6 +2548,129 @@ static void test_VarNeg(void) "VarNeg: VT_CY wrong, hres=0x%lX\n", hres); } +static HRESULT (WINAPI *pVarRound)(LPVARIANT,int,LPVARIANT); + +#define VARROUND(vt,val,deci,rvt,rval) V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + memset(&vDst,0,sizeof(vDst)); hres = pVarRound(&v,deci,&vDst); \ + ok(hres == S_OK && V_VT(&vDst) == VT_##rvt && V_##rvt(&vDst) == (rval), \ + "VarRound: expected 0x0,%d,%d, got 0x%lX,%d,%d\n", VT_##rvt, (int)(rval), \ + hres, V_VT(&vDst), (int)V_##rvt(&vDst)) + +#define VARROUNDF(vt,val,deci,rvt,rval) V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + memset(&vDst,0,sizeof(vDst)); hres = pVarRound(&v,deci,&vDst); \ + ok(hres == S_OK && V_VT(&vDst) == VT_##rvt && V_##rvt(&vDst) == (rval), \ + "VarRound: expected 0x0,%d,%f, got 0x%lX,%d,%f\n", VT_##rvt, rval, \ + hres, V_VT(&vDst), V_##rvt(&vDst)) + +static void test_VarRound(void) +{ + /* static const WCHAR szNumMin[] = {'-','1','.','4','5','\0' }; + static const WCHAR szNum[] = {'1','.','4','5','\0' }; */ + HRESULT hres; + VARIANT v, vDst; + CY *pcy = &V_CY(&v); + + CHECKPTR(VarRound); + + /* first check valid integer types */ + VARROUND(BOOL,VARIANT_TRUE,0,I2,-1); + VARROUND(BOOL,VARIANT_FALSE,0,I2,0); + VARROUND(BOOL,1,0,I2,1); + VARROUND(UI1,1,0,UI1,1); + VARROUND(UI1,254,0,UI1,254); + VARROUND(I2,-32768,0,I2,-32768); + VARROUND(I2,-1,0,I2,-1); + VARROUND(I2,1,0,I2,1); + VARROUND(I4,-((int)(~0u >> 1)) - 1,0,I4,-((int)(~0u >> 1)) - 1); + VARROUND(I4,-1,0,I4,-1); + VARROUND(I4,1,0,I4,1); + + + /* MSDN states that rounding of R4/R8 is dependent on the underlying + * bit pattern of the number and so is architecture dependent. In this + * case Wine returns .2 (which is more correct) and Native returns .3 + */ + + VARROUNDF(R4,1.0,0,R4,1.0); + VARROUNDF(R4,-1.0,0,R4,-1.0); + VARROUNDF(R8,1.0,0,R8,1.0); + VARROUNDF(R8,-1.0,0,R8,-1.0); + + /* floating point numbers aren't exactly equal and we can't just + * compare the first few digits. + todo_wine { + VARROUNDF(DATE,1.451,1,DATE,1.5); + VARROUNDF(DATE,-1.45,1,DATE,-1.4); + VARROUNDF(BSTR,(BSTR)szNumMin,1,R8,-1.40); + VARROUNDF(BSTR,(BSTR)szNum,1,R8,1.50); + + VARROUNDF(R4,1.23456,0,R4,1.0); + VARROUNDF(R4,1.23456,1,R4,1.2); + VARROUNDF(R4,1.23456,2,R4,1.23); + VARROUNDF(R4,1.23456,3,R4,1.235); + VARROUNDF(R4,1.23456,4,R4,1.2346); + VARROUNDF(R4,-1.23456,0,R4,-1.0); + VARROUNDF(R4,-1.23456,1,R4,-1.2); + VARROUNDF(R4,-1.23456,2,R4,-1.23); + VARROUNDF(R4,-1.23456,3,R4,-1.235); + VARROUNDF(R4,-1.23456,4,R4,-1.2346); + + VARROUNDF(R8,1.23456,0,R8,1.0); + VARROUNDF(R8,1.23456,1,R8,1.2); + VARROUNDF(R8,1.23456,2,R8,1.23); + VARROUNDF(R8,1.23456,3,R8,1.235); + VARROUNDF(R8,1.23456,4,R8,1.2346); + VARROUNDF(R8,-1.23456,0,R8,-1.0); + VARROUNDF(R8,-1.23456,1,R8,-1.2); + VARROUNDF(R8,-1.23456,2,R8,-1.23); + VARROUNDF(R8,-1.23456,3,R8,-1.235); + VARROUNDF(R8,-1.23456,4,R8,-1.2346); + } + */ + + V_VT(&v) = VT_EMPTY; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I2 && V_I2(&vDst) == 0, + "VarRound: expected 0x0,%d,0 got 0x%lX,%d,%d\n", VT_EMPTY, + hres, V_VT(&vDst), V_I2(&vDst)); + + V_VT(&v) = VT_NULL; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_NULL, + "VarRound: expected 0x0,%d got 0x%lX,%d\n", VT_NULL, hres, V_VT(&vDst)); + + /* not yet implemented so no use testing yet + todo_wine { + DECIMAL *pdec = &V_DECIMAL(&v); + V_VT(&v) = VT_DECIMAL; + pdec->u.s.sign = DECIMAL_NEG; + pdec->u.s.scale = 0; + pdec->Hi32 = 0; + pdec->u1.s1.Mid32 = 0; + pdec->u1.s1.Lo32 = 1; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && + V_DECIMAL(&vDst).u.s.sign == 0, + "VarRound: expected 0x0,%d,0x00, got 0x%lX,%d,%02x\n", VT_DECIMAL, + hres, V_VT(&vDst), V_DECIMAL(&vDst).u.s.sign); + + pdec->u.s.sign = 0; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && + V_DECIMAL(&vDst).u.s.sign == DECIMAL_NEG, + "VarRound: expected 0x0,%d,0x7f, got 0x%lX,%d,%02x\n", VT_DECIMAL, + hres, V_VT(&vDst), V_DECIMAL(&vDst).u.s.sign); + } + */ + + V_VT(&v) = VT_CY; + pcy->int64 = 10000; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == 10000, + "VarRound: VT_CY wrong, hres=0x%lX\n", hres); + +} + START_TEST(vartest) { hOleaut32 = LoadLibraryA("oleaut32.dll"); @@ -2573,4 +2696,5 @@ START_TEST(vartest) test_VarFix(); test_VarInt(); test_VarNeg(); + test_VarRound(); } diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index 7ee3cfcae73..5327700012e 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -3437,6 +3437,135 @@ HRESULT WINAPI VarNot(LPVARIANT pVarIn, LPVARIANT pVarOut) return hRet; } +/********************************************************************** + * VarRound [OLEAUT32.175] + * + * Perform a round operation on a variant. + * + * PARAMS + * pVarIn [I] Source variant + * deci [I] Number of decimals to round to + * pVarOut [O] Destination for converted value + * + * RETURNS + * Success: S_OK. pVarOut contains the converted value. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * - Floating point values are rounded to the desired number of decimals. + * - Some integer types are just copied to the return variable. + * - Some other integer types are not handled and fail. + */ +HRESULT WINAPI VarRound(LPVARIANT pVarIn, int deci, LPVARIANT pVarOut) +{ + VARIANT varIn; + HRESULT hRet = S_OK; + float factor; + + TRACE("(%p->(%s%s),%d)\n", pVarIn, debugstr_VT(pVarIn), debugstr_VF(pVarIn), deci); + + switch (V_VT(pVarIn)) + { + /* cases that fail on windows */ + case VT_I1: + case VT_I8: + case VT_UI2: + case VT_UI4: + hRet = DISP_E_BADVARTYPE; + break; + + /* cases just copying in to out */ + case VT_UI1: + V_VT(pVarOut) = V_VT(pVarIn); + V_UI1(pVarOut) = V_UI1(pVarIn); + break; + case VT_I2: + V_VT(pVarOut) = V_VT(pVarIn); + V_I2(pVarOut) = V_I2(pVarIn); + break; + case VT_I4: + V_VT(pVarOut) = V_VT(pVarIn); + V_I4(pVarOut) = V_I4(pVarIn); + break; + case VT_NULL: + V_VT(pVarOut) = V_VT(pVarIn); + /* value unchanged */ + break; + + /* cases that change type */ + case VT_EMPTY: + V_VT(pVarOut) = VT_I2; + V_I2(pVarOut) = 0; + break; + case VT_BOOL: + V_VT(pVarOut) = VT_I2; + V_I2(pVarOut) = V_BOOL(pVarIn); + break; + case VT_BSTR: + hRet = VarR8FromStr(V_BSTR(pVarIn), LOCALE_USER_DEFAULT, 0, &V_R8(&varIn)); + if (FAILED(hRet)) + break; + V_VT(&varIn)=VT_R8; + pVarIn = &varIn; + /* Fall through ... */ + + /* cases we need to do math */ + case VT_R8: + if (V_R8(pVarIn)>0) { + V_R8(pVarOut)=floor(V_R8(pVarIn)*pow(10, deci)+0.5)/pow(10, deci); + } else { + V_R8(pVarOut)=ceil(V_R8(pVarIn)*pow(10, deci)-0.5)/pow(10, deci); + } + V_VT(pVarOut) = V_VT(pVarIn); + break; + case VT_R4: + if (V_R4(pVarIn)>0) { + V_R4(pVarOut)=floor(V_R4(pVarIn)*pow(10, deci)+0.5)/pow(10, deci); + } else { + V_R4(pVarOut)=ceil(V_R4(pVarIn)*pow(10, deci)-0.5)/pow(10, deci); + } + V_VT(pVarOut) = V_VT(pVarIn); + break; + case VT_DATE: + if (V_DATE(pVarIn)>0) { + V_DATE(pVarOut)=floor(V_DATE(pVarIn)*pow(10, deci)+0.5)/pow(10, deci); + } else { + V_DATE(pVarOut)=ceil(V_DATE(pVarIn)*pow(10, deci)-0.5)/pow(10, deci); + } + V_VT(pVarOut) = V_VT(pVarIn); + break; + case VT_CY: + if (deci>3) + factor=1; + else + factor=pow(10, 4-deci); + + if (V_CY(pVarIn).int64>0) { + V_CY(pVarOut).int64=floor(V_CY(pVarIn).int64/factor)*factor; + } else { + V_CY(pVarOut).int64=ceil(V_CY(pVarIn).int64/factor)*factor; + } + V_VT(pVarOut) = V_VT(pVarIn); + break; + + /* cases we don't know yet */ + default: + FIXME("unimplemented part, V_VT(pVarIn) == 0x%X, deci == %d\n", + V_VT(pVarIn) & VT_TYPEMASK, deci); + hRet = DISP_E_BADVARTYPE; + } + + if (FAILED(hRet)) + V_VT(pVarOut) = VT_EMPTY; + + TRACE("returning 0x%08lx (%s%s),%f\n", hRet, debugstr_VT(pVarOut), + debugstr_VF(pVarOut), (V_VT(pVarOut) == VT_R4) ? V_R4(pVarOut) : + (V_VT(pVarOut) == VT_R8) ? V_R8(pVarOut) : 0); + + return hRet; +} + + /********************************************************************** * VarMod [OLEAUT32.154] *