Logo Search packages:      
Sourcecode: wine version File versions

vartype.c

/*
 * Low level variant functions
 *
 * Copyright 2003 Jon Griffiths
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define COBJMACROS
#define NONAMELESSUNION
#define NONAMELESSSTRUCT

#include "wine/debug.h"
#include "wine/unicode.h"
#include "winbase.h"
#include "winuser.h"
#include "winnt.h"
#include "variant.h"
#include "resource.h"

WINE_DEFAULT_DEBUG_CHANNEL(variant);

extern HMODULE OLEAUT32_hModule;

static const WCHAR szFloatFormatW[] = { '%','.','7','G','\0' };
static const WCHAR szDoubleFormatW[] = { '%','.','1','5','G','\0' };

/* Copy data from one variant to another. */
static inline void VARIANT_CopyData(const VARIANT *srcVar, VARTYPE vt, void *pOut)
{
  switch (vt)
  {
  case VT_I1:
  case VT_UI1: memcpy(pOut, &V_UI1(srcVar), sizeof(BYTE)); break;
  case VT_BOOL:
  case VT_I2:
  case VT_UI2: memcpy(pOut, &V_UI2(srcVar), sizeof(SHORT)); break;
  case VT_R4:
  case VT_INT:
  case VT_I4:
  case VT_UINT:
  case VT_UI4: memcpy(pOut, &V_UI4(srcVar), sizeof (LONG)); break;
  case VT_R8:
  case VT_DATE:
  case VT_CY:
  case VT_I8:
  case VT_UI8: memcpy(pOut, &V_UI8(srcVar), sizeof (LONG64)); break;
  case VT_INT_PTR: memcpy(pOut, &V_INT_PTR(srcVar), sizeof (INT_PTR)); break;
  case VT_DECIMAL: memcpy(pOut, &V_DECIMAL(srcVar), sizeof (DECIMAL)); break;
  default:
    FIXME("VT_ type %d unhandled, please report!\n", vt);
  }
}


/* Coerce VT_BSTR to a numeric type */
HRESULT VARIANT_NumberFromBstr(OLECHAR* pStrIn, LCID lcid, ULONG ulFlags,
                               void* pOut, VARTYPE vt)
{
  VARIANTARG dstVar;
  HRESULT hRet;
  NUMPARSE np;
  BYTE rgb[1024];

  /* Use VarParseNumFromStr/VarNumFromParseNum as MSDN indicates */
  np.cDig = sizeof(rgb) / sizeof(BYTE);
  np.dwInFlags = NUMPRS_STD;

  hRet = VarParseNumFromStr(pStrIn, lcid, ulFlags, &np, rgb);

  if (SUCCEEDED(hRet))
  {
    /* 1 << vt gives us the VTBIT constant for the destination number type */
    hRet = VarNumFromParseNum(&np, rgb, 1 << vt, &dstVar);
    if (SUCCEEDED(hRet))
      VARIANT_CopyData(&dstVar, vt, pOut);
  }
  return hRet;
}

/* Coerce VT_DISPATCH to another type */
HRESULT VARIANT_FromDisp(IDispatch* pdispIn, LCID lcid, void* pOut, VARTYPE vt)
{
  static const DISPPARAMS emptyParams = { NULL, NULL, 0, 0 };
  VARIANTARG srcVar, dstVar;
  HRESULT hRet;

  if (!pdispIn)
    return DISP_E_BADVARTYPE;

  /* Get the default 'value' property from the IDispatch */
  hRet = IDispatch_Invoke(pdispIn, DISPID_VALUE, &IID_NULL, lcid, DISPATCH_PROPERTYGET,
                          (DISPPARAMS*)&emptyParams, &srcVar, NULL, NULL);

  if (SUCCEEDED(hRet))
  {
    /* Convert the property to the requested type */
    V_VT(&dstVar) = VT_EMPTY;
    hRet = VariantChangeTypeEx(&dstVar, &srcVar, lcid, 0, vt);
    VariantClear(&srcVar);

    if (SUCCEEDED(hRet))
    {
      VARIANT_CopyData(&dstVar, vt, pOut);
      VariantClear(&srcVar);
    }
  }
  else
    hRet = DISP_E_TYPEMISMATCH;
  return hRet;
}

/* I1
 */

/************************************************************************
 * VarI1FromUI1 (OLEAUT32.244)
 *
 * Convert a VT_UI1 to a VT_I1.
 *
 * PARAMS
 *  bIn     [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromUI1(BYTE bIn, signed char* pcOut)
{
  return _VarI1FromUI1(bIn, pcOut);
}

/************************************************************************
 * VarI1FromI2 (OLEAUT32.245)
 *
 * Convert a VT_I2 to a VT_I1.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromI2(SHORT sIn, signed char* pcOut)
{
  return _VarI1FromI2(sIn, pcOut);
}

/************************************************************************
 * VarI1FromI4 (OLEAUT32.246)
 *
 * Convert a VT_I4 to a VT_I1.
 *
 * PARAMS
 *  iIn     [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromI4(LONG iIn, signed char* pcOut)
{
  return _VarI1FromI4(iIn, pcOut);
}

/************************************************************************
 * VarI1FromR4 (OLEAUT32.247)
 *
 * Convert a VT_R4 to a VT_I1.
 *
 * PARAMS
 *  fltIn   [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromR4(FLOAT fltIn, signed char* pcOut)
{
  return _VarI1FromR4(fltIn, pcOut);
}

/************************************************************************
 * VarI1FromR8 (OLEAUT32.248)
 *
 * Convert a VT_R8 to a VT_I1.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarI1FromR8(double dblIn, signed char* pcOut)
{
  if (dblIn < (double)I1_MIN || dblIn > (double)I1_MAX)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(CHAR, dblIn, *pcOut);
  return S_OK;
}

/************************************************************************
 * VarI1FromDate (OLEAUT32.249)
 *
 * Convert a VT_DATE to a VT_I1.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromDate(DATE dateIn, signed char* pcOut)
{
  return _VarI1FromDate(dateIn, pcOut);
}

/************************************************************************
 * VarI1FromCy (OLEAUT32.250)
 *
 * Convert a VT_CY to a VT_I1.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromCy(CY cyIn, signed char* pcOut)
{
  LONG i = I1_MAX + 1;

  _VarI4FromCy(cyIn, &i);
  return _VarI1FromI4(i, pcOut);
}

/************************************************************************
 * VarI1FromStr (OLEAUT32.251)
 *
 * Convert a VT_BSTR to a VT_I1.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI1FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, signed char* pcOut)
{
  return _VarI1FromStr(strIn, lcid, dwFlags, pcOut);
}

/************************************************************************
 * VarI1FromDisp (OLEAUT32.252)
 *
 * Convert a VT_DISPATCH to a VT_I1.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pcOut    [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI1FromDisp(IDispatch* pdispIn, LCID lcid, signed char* pcOut)
{
  return _VarI1FromDisp(pdispIn, lcid, pcOut);
}

/************************************************************************
 * VarI1FromBool (OLEAUT32.253)
 *
 * Convert a VT_BOOL to a VT_I1.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI1FromBool(VARIANT_BOOL boolIn, signed char* pcOut)
{
  return _VarI1FromBool(boolIn, pcOut);
}

/************************************************************************
 * VarI1FromUI2 (OLEAUT32.254)
 *
 * Convert a VT_UI2 to a VT_I1.
 *
 * PARAMS
 *  usIn    [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromUI2(USHORT usIn, signed char* pcOut)
{
  return _VarI1FromUI2(usIn, pcOut);
}

/************************************************************************
 * VarI1FromUI4 (OLEAUT32.255)
 *
 * Convert a VT_UI4 to a VT_I1.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI1FromUI4(ULONG ulIn, signed char* pcOut)
{
  return _VarI1FromUI4(ulIn, pcOut);
}

/************************************************************************
 * VarI1FromDec (OLEAUT32.256)
 *
 * Convert a VT_DECIMAL to a VT_I1.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromDec(DECIMAL *pdecIn, signed char* pcOut)
{
  LONG64 i64;
  HRESULT hRet;

  hRet = _VarI8FromDec(pdecIn, &i64);

  if (SUCCEEDED(hRet))
    hRet = _VarI1FromI8(i64, pcOut);
  return hRet;
}

/************************************************************************
 * VarI1FromI8 (OLEAUT32.376)
 *
 * Convert a VT_I8 to a VT_I1.
 *
 * PARAMS
 *  llIn  [I] Source
 *  pcOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromI8(LONG64 llIn, signed char* pcOut)
{
  return _VarI1FromI8(llIn, pcOut);
}

/************************************************************************
 * VarI1FromUI8 (OLEAUT32.377)
 *
 * Convert a VT_UI8 to a VT_I1.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pcOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI1FromUI8(ULONG64 ullIn, signed char* pcOut)
{
  return _VarI1FromUI8(ullIn, pcOut);
}

/* UI1
 */

/************************************************************************
 * VarUI1FromI2 (OLEAUT32.130)
 *
 * Convert a VT_I2 to a VT_UI1.
 *
 * PARAMS
 *  sIn   [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromI2(SHORT sIn, BYTE* pbOut)
{
  return _VarUI1FromI2(sIn, pbOut);
}

/************************************************************************
 * VarUI1FromI4 (OLEAUT32.131)
 *
 * Convert a VT_I4 to a VT_UI1.
 *
 * PARAMS
 *  iIn   [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromI4(LONG iIn, BYTE* pbOut)
{
  return _VarUI1FromI4(iIn, pbOut);
}

/************************************************************************
 * VarUI1FromR4 (OLEAUT32.132)
 *
 * Convert a VT_R4 to a VT_UI1.
 *
 * PARAMS
 *  fltIn [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI1FromR4(FLOAT fltIn, BYTE* pbOut)
{
  return _VarUI1FromR4(fltIn, pbOut);
}

/************************************************************************
 * VarUI1FromR8 (OLEAUT32.133)
 *
 * Convert a VT_R8 to a VT_UI1.
 *
 * PARAMS
 *  dblIn [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarUI1FromR8(double dblIn, BYTE* pbOut)
{
  if (dblIn < -0.5 || dblIn > (double)UI1_MAX)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(BYTE, dblIn, *pbOut);
  return S_OK;
}

/************************************************************************
 * VarUI1FromCy (OLEAUT32.134)
 *
 * Convert a VT_CY to a VT_UI1.
 *
 * PARAMS
 *  cyIn     [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  Negative values >= -5000 will be converted to 0.
 */
HRESULT WINAPI VarUI1FromCy(CY cyIn, BYTE* pbOut)
{
  ULONG i = UI1_MAX + 1;

  _VarUI4FromCy(cyIn, &i);
  return _VarUI1FromUI4(i, pbOut);
}

/************************************************************************
 * VarUI1FromDate (OLEAUT32.135)
 *
 * Convert a VT_DATE to a VT_UI1.
 *
 * PARAMS
 *  dateIn [I] Source
 *  pbOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromDate(DATE dateIn, BYTE* pbOut)
{
  return _VarUI1FromDate(dateIn, pbOut);
}

/************************************************************************
 * VarUI1FromStr (OLEAUT32.136)
 *
 * Convert a VT_BSTR to a VT_UI1.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI1FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, BYTE* pbOut)
{
  return _VarUI1FromStr(strIn, lcid, dwFlags, pbOut);
}

/************************************************************************
 * VarUI1FromDisp (OLEAUT32.137)
 *
 * Convert a VT_DISPATCH to a VT_UI1.
 *
 * PARAMS
 *  pdispIn [I] Source
 *  lcid    [I] LCID for conversion
 *  pbOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI1FromDisp(IDispatch* pdispIn, LCID lcid, BYTE* pbOut)
{
  return _VarUI1FromDisp(pdispIn, lcid, pbOut);
}

/************************************************************************
 * VarUI1FromBool (OLEAUT32.138)
 *
 * Convert a VT_BOOL to a VT_UI1.
 *
 * PARAMS
 *  boolIn [I] Source
 *  pbOut  [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI1FromBool(VARIANT_BOOL boolIn, BYTE* pbOut)
{
  return _VarUI1FromBool(boolIn, pbOut);
}

/************************************************************************
 * VarUI1FromI1 (OLEAUT32.237)
 *
 * Convert a VT_I1 to a VT_UI1.
 *
 * PARAMS
 *  cIn   [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromI1(signed char cIn, BYTE* pbOut)
{
  return _VarUI1FromI1(cIn, pbOut);
}

/************************************************************************
 * VarUI1FromUI2 (OLEAUT32.238)
 *
 * Convert a VT_UI2 to a VT_UI1.
 *
 * PARAMS
 *  usIn  [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromUI2(USHORT usIn, BYTE* pbOut)
{
  return _VarUI1FromUI2(usIn, pbOut);
}

/************************************************************************
 * VarUI1FromUI4 (OLEAUT32.239)
 *
 * Convert a VT_UI4 to a VT_UI1.
 *
 * PARAMS
 *  ulIn  [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromUI4(ULONG ulIn, BYTE* pbOut)
{
  return _VarUI1FromUI4(ulIn, pbOut);
}

/************************************************************************
 * VarUI1FromDec (OLEAUT32.240)
 *
 * Convert a VT_DECIMAL to a VT_UI1.
 *
 * PARAMS
 *  pDecIn [I] Source
 *  pbOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromDec(DECIMAL *pdecIn, BYTE* pbOut)
{
  LONG64 i64;
  HRESULT hRet;

  hRet = _VarI8FromDec(pdecIn, &i64);

  if (SUCCEEDED(hRet))
    hRet = _VarUI1FromI8(i64, pbOut);
  return hRet;
}

/************************************************************************
 * VarUI1FromI8 (OLEAUT32.372)
 *
 * Convert a VT_I8 to a VT_UI1.
 *
 * PARAMS
 *  llIn  [I] Source
 *  pbOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromI8(LONG64 llIn, BYTE* pbOut)
{
  return _VarUI1FromI8(llIn, pbOut);
}

/************************************************************************
 * VarUI1FromUI8 (OLEAUT32.373)
 *
 * Convert a VT_UI8 to a VT_UI1.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pbOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI1FromUI8(ULONG64 ullIn, BYTE* pbOut)
{
  return _VarUI1FromUI8(ullIn, pbOut);
}


/* I2
 */

/************************************************************************
 * VarI2FromUI1 (OLEAUT32.48)
 *
 * Convert a VT_UI2 to a VT_I2.
 *
 * PARAMS
 *  bIn     [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI2FromUI1(BYTE bIn, SHORT* psOut)
{
  return _VarI2FromUI1(bIn, psOut);
}

/************************************************************************
 * VarI2FromI4 (OLEAUT32.49)
 *
 * Convert a VT_I4 to a VT_I2.
 *
 * PARAMS
 *  iIn     [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromI4(LONG iIn, SHORT* psOut)
{
  return _VarI2FromI4(iIn, psOut);
}

/************************************************************************
 * VarI2FromR4 (OLEAUT32.50)
 *
 * Convert a VT_R4 to a VT_I2.
 *
 * PARAMS
 *  fltIn   [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromR4(FLOAT fltIn, SHORT* psOut)
{
  return _VarI2FromR4(fltIn, psOut);
}

/************************************************************************
 * VarI2FromR8 (OLEAUT32.51)
 *
 * Convert a VT_R8 to a VT_I2.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarI2FromR8(double dblIn, SHORT* psOut)
{
  if (dblIn < (double)I2_MIN || dblIn > (double)I2_MAX)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(SHORT, dblIn, *psOut);
  return S_OK;
}

/************************************************************************
 * VarI2FromCy (OLEAUT32.52)
 *
 * Convert a VT_CY to a VT_I2.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromCy(CY cyIn, SHORT* psOut)
{
  LONG i = I2_MAX + 1;

  _VarI4FromCy(cyIn, &i);
  return _VarI2FromI4(i, psOut);
}

/************************************************************************
 * VarI2FromDate (OLEAUT32.53)
 *
 * Convert a VT_DATE to a VT_I2.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromDate(DATE dateIn, SHORT* psOut)
{
  return _VarI2FromDate(dateIn, psOut);
}

/************************************************************************
 * VarI2FromStr (OLEAUT32.54)
 *
 * Convert a VT_BSTR to a VT_I2.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if any parameter is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI2FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, SHORT* psOut)
{
  return _VarI2FromStr(strIn, lcid, dwFlags, psOut);
}

/************************************************************************
 * VarI2FromDisp (OLEAUT32.55)
 *
 * Convert a VT_DISPATCH to a VT_I2.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  psOut    [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pdispIn is invalid,
 *           DISP_E_OVERFLOW, if the value will not fit in the destination,
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI2FromDisp(IDispatch* pdispIn, LCID lcid, SHORT* psOut)
{
  return _VarI2FromDisp(pdispIn, lcid, psOut);
}

/************************************************************************
 * VarI2FromBool (OLEAUT32.56)
 *
 * Convert a VT_BOOL to a VT_I2.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI2FromBool(VARIANT_BOOL boolIn, SHORT* psOut)
{
  return _VarI2FromBool(boolIn, psOut);
}

/************************************************************************
 * VarI2FromI1 (OLEAUT32.205)
 *
 * Convert a VT_I1 to a VT_I2.
 *
 * PARAMS
 *  cIn     [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI2FromI1(signed char cIn, SHORT* psOut)
{
  return _VarI2FromI1(cIn, psOut);
}

/************************************************************************
 * VarI2FromUI2 (OLEAUT32.206)
 *
 * Convert a VT_UI2 to a VT_I2.
 *
 * PARAMS
 *  usIn    [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromUI2(USHORT usIn, SHORT* psOut)
{
  return _VarI2FromUI2(usIn, psOut);
}

/************************************************************************
 * VarI2FromUI4 (OLEAUT32.207)
 *
 * Convert a VT_UI4 to a VT_I2.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromUI4(ULONG ulIn, SHORT* psOut)
{
  return _VarI2FromUI4(ulIn, psOut);
}

/************************************************************************
 * VarI2FromDec (OLEAUT32.208)
 *
 * Convert a VT_DECIMAL to a VT_I2.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  psOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromDec(DECIMAL *pdecIn, SHORT* psOut)
{
  LONG64 i64;
  HRESULT hRet;

  hRet = _VarI8FromDec(pdecIn, &i64);

  if (SUCCEEDED(hRet))
    hRet = _VarI2FromI8(i64, psOut);
  return hRet;
}

/************************************************************************
 * VarI2FromI8 (OLEAUT32.346)
 *
 * Convert a VT_I8 to a VT_I2.
 *
 * PARAMS
 *  llIn  [I] Source
 *  psOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromI8(LONG64 llIn, SHORT* psOut)
{
  return _VarI2FromI8(llIn, psOut);
}

/************************************************************************
 * VarI2FromUI8 (OLEAUT32.347)
 *
 * Convert a VT_UI8 to a VT_I2.
 *
 * PARAMS
 *  ullIn [I] Source
 *  psOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI2FromUI8(ULONG64 ullIn, SHORT* psOut)
{
  return _VarI2FromUI8(ullIn, psOut);
}

/* UI2
 */

/************************************************************************
 * VarUI2FromUI1 (OLEAUT32.257)
 *
 * Convert a VT_UI1 to a VT_UI2.
 *
 * PARAMS
 *  bIn    [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI2FromUI1(BYTE bIn, USHORT* pusOut)
{
  return _VarUI2FromUI1(bIn, pusOut);
}

/************************************************************************
 * VarUI2FromI2 (OLEAUT32.258)
 *
 * Convert a VT_I2 to a VT_UI2.
 *
 * PARAMS
 *  sIn    [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromI2(SHORT sIn, USHORT* pusOut)
{
  return _VarUI2FromI2(sIn, pusOut);
}

/************************************************************************
 * VarUI2FromI4 (OLEAUT32.259)
 *
 * Convert a VT_I4 to a VT_UI2.
 *
 * PARAMS
 *  iIn    [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromI4(LONG iIn, USHORT* pusOut)
{
  return _VarUI2FromI4(iIn, pusOut);
}

/************************************************************************
 * VarUI2FromR4 (OLEAUT32.260)
 *
 * Convert a VT_R4 to a VT_UI2.
 *
 * PARAMS
 *  fltIn  [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromR4(FLOAT fltIn, USHORT* pusOut)
{
  return _VarUI2FromR4(fltIn, pusOut);
}

/************************************************************************
 * VarUI2FromR8 (OLEAUT32.261)
 *
 * Convert a VT_R8 to a VT_UI2.
 *
 * PARAMS
 *  dblIn  [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarUI2FromR8(double dblIn, USHORT* pusOut)
{
  if (dblIn < -0.5 || dblIn > (double)UI2_MAX)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(USHORT, dblIn, *pusOut);
  return S_OK;
}

/************************************************************************
 * VarUI2FromDate (OLEAUT32.262)
 *
 * Convert a VT_DATE to a VT_UI2.
 *
 * PARAMS
 *  dateIn [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromDate(DATE dateIn, USHORT* pusOut)
{
  return _VarUI2FromDate(dateIn, pusOut);
}

/************************************************************************
 * VarUI2FromCy (OLEAUT32.263)
 *
 * Convert a VT_CY to a VT_UI2.
 *
 * PARAMS
 *  cyIn   [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  Negative values >= -5000 will be converted to 0.
 */
HRESULT WINAPI VarUI2FromCy(CY cyIn, USHORT* pusOut)
{
  ULONG i = UI2_MAX + 1;

  _VarUI4FromCy(cyIn, &i);
  return _VarUI2FromUI4(i, pusOut);
}

/************************************************************************
 * VarUI2FromStr (OLEAUT32.264)
 *
 * Convert a VT_BSTR to a VT_UI2.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pusOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI2FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, USHORT* pusOut)
{
  return _VarUI2FromStr(strIn, lcid, dwFlags, pusOut);
}

/************************************************************************
 * VarUI2FromDisp (OLEAUT32.265)
 *
 * Convert a VT_DISPATCH to a VT_UI2.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pusOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI2FromDisp(IDispatch* pdispIn, LCID lcid, USHORT* pusOut)
{
  return _VarUI2FromDisp(pdispIn, lcid, pusOut);
}

/************************************************************************
 * VarUI2FromBool (OLEAUT32.266)
 *
 * Convert a VT_BOOL to a VT_UI2.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  pusOut  [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI2FromBool(VARIANT_BOOL boolIn, USHORT* pusOut)
{
  return _VarUI2FromBool(boolIn, pusOut);
}

/************************************************************************
 * VarUI2FromI1 (OLEAUT32.267)
 *
 * Convert a VT_I1 to a VT_UI2.
 *
 * PARAMS
 *  cIn    [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromI1(signed char cIn, USHORT* pusOut)
{
  return _VarUI2FromI1(cIn, pusOut);
}

/************************************************************************
 * VarUI2FromUI4 (OLEAUT32.268)
 *
 * Convert a VT_UI4 to a VT_UI2.
 *
 * PARAMS
 *  ulIn   [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromUI4(ULONG ulIn, USHORT* pusOut)
{
  return _VarUI2FromUI4(ulIn, pusOut);
}

/************************************************************************
 * VarUI2FromDec (OLEAUT32.269)
 *
 * Convert a VT_DECIMAL to a VT_UI2.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pusOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromDec(DECIMAL *pdecIn, USHORT* pusOut)
{
  LONG64 i64;
  HRESULT hRet;

  hRet = _VarI8FromDec(pdecIn, &i64);

  if (SUCCEEDED(hRet))
    hRet = _VarUI2FromI8(i64, pusOut);
  return hRet;
}

/************************************************************************
 * VarUI2FromI8 (OLEAUT32.378)
 *
 * Convert a VT_I8 to a VT_UI2.
 *
 * PARAMS
 *  llIn   [I] Source
 *  pusOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromI8(LONG64 llIn, USHORT* pusOut)
{
  return _VarUI2FromI8(llIn, pusOut);
}

/************************************************************************
 * VarUI2FromUI8 (OLEAUT32.379)
 *
 * Convert a VT_UI8 to a VT_UI2.
 *
 * PARAMS
 *  ullIn    [I] Source
 *  pusOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI2FromUI8(ULONG64 ullIn, USHORT* pusOut)
{
  return _VarUI2FromUI8(ullIn, pusOut);
}

/* I4
 */

/************************************************************************
 * VarI4FromUI1 (OLEAUT32.58)
 *
 * Convert a VT_UI1 to a VT_I4.
 *
 * PARAMS
 *  bIn     [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI4FromUI1(BYTE bIn, LONG *piOut)
{
  return _VarI4FromUI1(bIn, piOut);
}

/************************************************************************
 * VarI4FromI2 (OLEAUT32.59)
 *
 * Convert a VT_I2 to a VT_I4.
 *
 * PARAMS
 *  sIn     [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromI2(SHORT sIn, LONG *piOut)
{
  return _VarI4FromI2(sIn, piOut);
}

/************************************************************************
 * VarI4FromR4 (OLEAUT32.60)
 *
 * Convert a VT_R4 to a VT_I4.
 *
 * PARAMS
 *  fltIn   [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromR4(FLOAT fltIn, LONG *piOut)
{
  return _VarI4FromR4(fltIn, piOut);
}

/************************************************************************
 * VarI4FromR8 (OLEAUT32.61)
 *
 * Convert a VT_R8 to a VT_I4.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarI4FromR8(double dblIn, LONG *piOut)
{
  if (dblIn < (double)I4_MIN || dblIn > (double)I4_MAX)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(LONG, dblIn, *piOut);
  return S_OK;
}

/************************************************************************
 * VarI4FromCy (OLEAUT32.62)
 *
 * Convert a VT_CY to a VT_I4.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromCy(CY cyIn, LONG *piOut)
{
  double d = cyIn.int64 / CY_MULTIPLIER_F;
  return _VarI4FromR8(d, piOut);
}

/************************************************************************
 * VarI4FromDate (OLEAUT32.63)
 *
 * Convert a VT_DATE to a VT_I4.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromDate(DATE dateIn, LONG *piOut)
{
  return _VarI4FromDate(dateIn, piOut);
}

/************************************************************************
 * VarI4FromStr (OLEAUT32.64)
 *
 * Convert a VT_BSTR to a VT_I4.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if any parameter is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if strIn cannot be converted
 */
HRESULT WINAPI VarI4FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, LONG *piOut)
{
  return _VarI4FromStr(strIn, lcid, dwFlags, piOut);
}

/************************************************************************
 * VarI4FromDisp (OLEAUT32.65)
 *
 * Convert a VT_DISPATCH to a VT_I4.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  piOut    [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI4FromDisp(IDispatch* pdispIn, LCID lcid, LONG *piOut)
{
  return _VarI4FromDisp(pdispIn, lcid, piOut);
}

/************************************************************************
 * VarI4FromBool (OLEAUT32.66)
 *
 * Convert a VT_BOOL to a VT_I4.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI4FromBool(VARIANT_BOOL boolIn, LONG *piOut)
{
  return _VarI4FromBool(boolIn, piOut);
}

/************************************************************************
 * VarI4FromI1 (OLEAUT32.209)
 *
 * Convert a VT_I4 to a VT_I4.
 *
 * PARAMS
 *  cIn     [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI4FromI1(signed char cIn, LONG *piOut)
{
  return _VarI4FromI1(cIn, piOut);
}

/************************************************************************
 * VarI4FromUI2 (OLEAUT32.210)
 *
 * Convert a VT_UI2 to a VT_I4.
 *
 * PARAMS
 *  usIn    [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI4FromUI2(USHORT usIn, LONG *piOut)
{
  return _VarI4FromUI2(usIn, piOut);
}

/************************************************************************
 * VarI4FromUI4 (OLEAUT32.211)
 *
 * Convert a VT_UI4 to a VT_I4.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromUI4(ULONG ulIn, LONG *piOut)
{
  return _VarI4FromUI4(ulIn, piOut);
}

/************************************************************************
 * VarI4FromDec (OLEAUT32.212)
 *
 * Convert a VT_DECIMAL to a VT_I4.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  piOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pdecIn is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromDec(DECIMAL *pdecIn, LONG *piOut)
{
  LONG64 i64;
  HRESULT hRet;

  hRet = _VarI8FromDec(pdecIn, &i64);

  if (SUCCEEDED(hRet))
    hRet = _VarI4FromI8(i64, piOut);
  return hRet;
}

/************************************************************************
 * VarI4FromI8 (OLEAUT32.348)
 *
 * Convert a VT_I8 to a VT_I4.
 *
 * PARAMS
 *  llIn  [I] Source
 *  piOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromI8(LONG64 llIn, LONG *piOut)
{
  return _VarI4FromI8(llIn, piOut);
}

/************************************************************************
 * VarI4FromUI8 (OLEAUT32.349)
 *
 * Convert a VT_UI8 to a VT_I4.
 *
 * PARAMS
 *  ullIn [I] Source
 *  piOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI4FromUI8(ULONG64 ullIn, LONG *piOut)
{
  return _VarI4FromUI8(ullIn, piOut);
}

/* UI4
 */

/************************************************************************
 * VarUI4FromUI1 (OLEAUT32.270)
 *
 * Convert a VT_UI1 to a VT_UI4.
 *
 * PARAMS
 *  bIn    [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI4FromUI1(BYTE bIn, ULONG *pulOut)
{
  return _VarUI4FromUI1(bIn, pulOut);
}

/************************************************************************
 * VarUI4FromI2 (OLEAUT32.271)
 *
 * Convert a VT_I2 to a VT_UI4.
 *
 * PARAMS
 *  sIn    [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromI2(SHORT sIn, ULONG *pulOut)
{
  return _VarUI4FromI2(sIn, pulOut);
}

/************************************************************************
 * VarUI4FromI4 (OLEAUT32.272)
 *
 * Convert a VT_I4 to a VT_UI4.
 *
 * PARAMS
 *  iIn    [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromI4(LONG iIn, ULONG *pulOut)
{
  return _VarUI4FromI4(iIn, pulOut);
}

/************************************************************************
 * VarUI4FromR4 (OLEAUT32.273)
 *
 * Convert a VT_R4 to a VT_UI4.
 *
 * PARAMS
 *  fltIn  [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromR4(FLOAT fltIn, ULONG *pulOut)
{
  return _VarUI4FromR4(fltIn, pulOut);
}

/************************************************************************
 * VarUI4FromR8 (OLEAUT32.274)
 *
 * Convert a VT_R8 to a VT_UI4.
 *
 * PARAMS
 *  dblIn  [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarUI4FromR8(double dblIn, ULONG *pulOut)
{
  if (dblIn < -0.5 || dblIn > (double)UI4_MAX)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(ULONG, dblIn, *pulOut);
  return S_OK;
}

/************************************************************************
 * VarUI4FromDate (OLEAUT32.275)
 *
 * Convert a VT_DATE to a VT_UI4.
 *
 * PARAMS
 *  dateIn [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromDate(DATE dateIn, ULONG *pulOut)
{
  return _VarUI4FromDate(dateIn, pulOut);
}

/************************************************************************
 * VarUI4FromCy (OLEAUT32.276)
 *
 * Convert a VT_CY to a VT_UI4.
 *
 * PARAMS
 *  cyIn   [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromCy(CY cyIn, ULONG *pulOut)
{
  double d = cyIn.int64 / CY_MULTIPLIER_F;
  return _VarUI4FromR8(d, pulOut);
}

/************************************************************************
 * VarUI4FromStr (OLEAUT32.277)
 *
 * Convert a VT_BSTR to a VT_UI4.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pulOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if any parameter is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if strIn cannot be converted
 */
HRESULT WINAPI VarUI4FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, ULONG *pulOut)
{
  return _VarUI4FromStr(strIn, lcid, dwFlags, pulOut);
}

/************************************************************************
 * VarUI4FromDisp (OLEAUT32.278)
 *
 * Convert a VT_DISPATCH to a VT_UI4.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pulOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI4FromDisp(IDispatch* pdispIn, LCID lcid, ULONG *pulOut)
{
  return _VarUI4FromDisp(pdispIn, lcid, pulOut);
}

/************************************************************************
 * VarUI4FromBool (OLEAUT32.279)
 *
 * Convert a VT_BOOL to a VT_UI4.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  pulOut  [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI4FromBool(VARIANT_BOOL boolIn, ULONG *pulOut)
{
  return _VarUI4FromBool(boolIn, pulOut);
}

/************************************************************************
 * VarUI4FromI1 (OLEAUT32.280)
 *
 * Convert a VT_I1 to a VT_UI4.
 *
 * PARAMS
 *  cIn    [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromI1(signed char cIn, ULONG *pulOut)
{
  return _VarUI4FromI1(cIn, pulOut);
}

/************************************************************************
 * VarUI4FromUI2 (OLEAUT32.281)
 *
 * Convert a VT_UI2 to a VT_UI4.
 *
 * PARAMS
 *  usIn   [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI4FromUI2(USHORT usIn, ULONG *pulOut)
{
  return _VarUI4FromUI2(usIn, pulOut);
}

/************************************************************************
 * VarUI4FromDec (OLEAUT32.282)
 *
 * Convert a VT_DECIMAL to a VT_UI4.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pulOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pdecIn is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromDec(DECIMAL *pdecIn, ULONG *pulOut)
{
  LONG64 i64;
  HRESULT hRet;

  hRet = _VarI8FromDec(pdecIn, &i64);

  if (SUCCEEDED(hRet))
    hRet = _VarUI4FromI8(i64, pulOut);
  return hRet;
}

/************************************************************************
 * VarUI4FromI8 (OLEAUT32.425)
 *
 * Convert a VT_I8 to a VT_UI4.
 *
 * PARAMS
 *  llIn   [I] Source
 *  pulOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromI8(LONG64 llIn, ULONG *pulOut)
{
  return _VarUI4FromI8(llIn, pulOut);
}

/************************************************************************
 * VarUI4FromUI8 (OLEAUT32.426)
 *
 * Convert a VT_UI8 to a VT_UI4.
 *
 * PARAMS
 *  ullIn    [I] Source
 *  pulOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI4FromUI8(ULONG64 ullIn, ULONG *pulOut)
{
  return _VarUI4FromUI8(ullIn, pulOut);
}

/* I8
 */

/************************************************************************
 * VarI8FromUI1 (OLEAUT32.333)
 *
 * Convert a VT_UI1 to a VT_I8.
 *
 * PARAMS
 *  bIn     [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI8FromUI1(BYTE bIn, LONG64* pi64Out)
{
  return _VarI8FromUI1(bIn, pi64Out);
}


/************************************************************************
 * VarI8FromI2 (OLEAUT32.334)
 *
 * Convert a VT_I2 to a VT_I8.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI8FromI2(SHORT sIn, LONG64* pi64Out)
{
  return _VarI8FromI2(sIn, pi64Out);
}

/************************************************************************
 * VarI8FromR4 (OLEAUT32.335)
 *
 * Convert a VT_R4 to a VT_I8.
 *
 * PARAMS
 *  fltIn   [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI8FromR4(FLOAT fltIn, LONG64* pi64Out)
{
  return _VarI8FromR4(fltIn, pi64Out);
}

/************************************************************************
 * VarI8FromR8 (OLEAUT32.336)
 *
 * Convert a VT_R8 to a VT_I8.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  Only values that fit into 63 bits are accepted. Due to rounding issues,
 *  very high or low values will not be accurately converted.
 *
 *  Numbers are rounded using Dutch rounding, as follows:
 *
 *|  Fractional Part   Sign  Direction  Example
 *|  ---------------   ----  ---------  -------
 *|  < 0.5              +    Down        0.4 ->  0.0
 *|  < 0.5              -    Up         -0.4 ->  0.0
 *|  > 0.5              +    Up          0.6 ->  1.0
 *|  < 0.5              -    Up         -0.6 -> -1.0
 *|  = 0.5              +    Up/Down    Down if even, Up if odd
 *|  = 0.5              -    Up/Down    Up if even, Down if odd
 *
 *  This system is often used in supermarkets.
 */
HRESULT WINAPI VarI8FromR8(double dblIn, LONG64* pi64Out)
{
  if ( dblIn < -4611686018427387904.0 || dblIn >= 4611686018427387904.0)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(LONG64, dblIn, *pi64Out);
  return S_OK;
}

/************************************************************************
 * VarI8FromCy (OLEAUT32.337)
 *
 * Convert a VT_CY to a VT_I8.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 *
 * NOTES
 *  All negative numbers are rounded down by 1, including those that are
 *  evenly divisible by 10000 (this is a Win32 bug that Wine mimics).
 *  Positive numbers are rounded using Dutch rounding: See VarI8FromR8()
 *  for details.
 */
HRESULT WINAPI VarI8FromCy(CY cyIn, LONG64* pi64Out)
{
  *pi64Out = cyIn.int64 / CY_MULTIPLIER;

  if (cyIn.int64 < 0)
    (*pi64Out)--; /* Mimic Win32 bug */
  else
  {
    cyIn.int64 -= *pi64Out * CY_MULTIPLIER; /* cyIn.s.Lo now holds fractional remainder */

    if (cyIn.s.Lo > CY_HALF || (cyIn.s.Lo == CY_HALF && (*pi64Out & 0x1)))
      (*pi64Out)++;
  }
  return S_OK;
}

/************************************************************************
 * VarI8FromDate (OLEAUT32.338)
 *
 * Convert a VT_DATE to a VT_I8.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI8FromDate(DATE dateIn, LONG64* pi64Out)
{
  return _VarI8FromDate(dateIn, pi64Out);
}

/************************************************************************
 * VarI8FromStr (OLEAUT32.339)
 *
 * Convert a VT_BSTR to a VT_I8.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI8FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, LONG64* pi64Out)
{
  return _VarI8FromStr(strIn, lcid, dwFlags, pi64Out);
}

/************************************************************************
 * VarI8FromDisp (OLEAUT32.340)
 *
 * Convert a VT_DISPATCH to a VT_I8.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pi64Out  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarI8FromDisp(IDispatch* pdispIn, LCID lcid, LONG64* pi64Out)
{
  return _VarI8FromDisp(pdispIn, lcid, pi64Out);
}

/************************************************************************
 * VarI8FromBool (OLEAUT32.341)
 *
 * Convert a VT_BOOL to a VT_I8.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI8FromBool(VARIANT_BOOL boolIn, LONG64* pi64Out)
{
  return _VarI8FromBool(boolIn, pi64Out);
}

/************************************************************************
 * VarI8FromI1 (OLEAUT32.342)
 *
 * Convert a VT_I1 to a VT_I8.
 *
 * PARAMS
 *  cIn     [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI8FromI1(signed char cIn, LONG64* pi64Out)
{
  return _VarI8FromI1(cIn, pi64Out);
}

/************************************************************************
 * VarI8FromUI2 (OLEAUT32.343)
 *
 * Convert a VT_UI2 to a VT_I8.
 *
 * PARAMS
 *  usIn    [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI8FromUI2(USHORT usIn, LONG64* pi64Out)
{
  return _VarI8FromUI2(usIn, pi64Out);
}

/************************************************************************
 * VarI8FromUI4 (OLEAUT32.344)
 *
 * Convert a VT_UI4 to a VT_I8.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarI8FromUI4(ULONG ulIn, LONG64* pi64Out)
{
  return _VarI8FromUI4(ulIn, pi64Out);
}

/************************************************************************
 * VarI8FromDec (OLEAUT32.345)
 *
 * Convert a VT_DECIMAL to a VT_I8.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI8FromDec(DECIMAL *pdecIn, LONG64* pi64Out)
{
  if (!DEC_SCALE(pdecIn))
  {
    /* This decimal is just a 96 bit integer */
    if (DEC_SIGN(pdecIn) & ~DECIMAL_NEG)
      return E_INVALIDARG;

    if (DEC_HI32(pdecIn) || DEC_MID32(pdecIn) & 0x80000000)
      return DISP_E_OVERFLOW;

    if (DEC_SIGN(pdecIn))
      *pi64Out = -DEC_LO64(pdecIn);
    else
      *pi64Out = DEC_LO64(pdecIn);
    return S_OK;
  }
  else
  {
    /* Decimal contains a floating point number */
    HRESULT hRet;
    double dbl;

    hRet = _VarR8FromDec(pdecIn, &dbl);
    if (SUCCEEDED(hRet))
      hRet = VarI8FromR8(dbl, pi64Out);
    return hRet;
  }
}

/************************************************************************
 * VarI8FromUI8 (OLEAUT32.427)
 *
 * Convert a VT_UI8 to a VT_I8.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pi64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarI8FromUI8(ULONG64 ullIn, LONG64* pi64Out)
{
  return _VarI8FromUI8(ullIn, pi64Out);
}

/* UI8
 */

/************************************************************************
 * VarUI8FromI8 (OLEAUT32.428)
 *
 * Convert a VT_I8 to a VT_UI8.
 *
 * PARAMS
 *  ulIn     [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI8FromI8(LONG64 llIn, ULONG64* pui64Out)
{
  return _VarUI8FromI8(llIn, pui64Out);
}

/************************************************************************
 * VarUI8FromUI1 (OLEAUT32.429)
 *
 * Convert a VT_UI1 to a VT_UI8.
 *
 * PARAMS
 *  bIn      [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI8FromUI1(BYTE bIn, ULONG64* pui64Out)
{
  return _VarUI8FromUI1(bIn, pui64Out);
}

/************************************************************************
 * VarUI8FromI2 (OLEAUT32.430)
 *
 * Convert a VT_I2 to a VT_UI8.
 *
 * PARAMS
 *  sIn      [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI8FromI2(SHORT sIn, ULONG64* pui64Out)
{
  return _VarUI8FromI2(sIn, pui64Out);
}

/************************************************************************
 * VarUI8FromR4 (OLEAUT32.431)
 *
 * Convert a VT_R4 to a VT_UI8.
 *
 * PARAMS
 *  fltIn    [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI8FromR4(FLOAT fltIn, ULONG64* pui64Out)
{
  return _VarUI8FromR4(fltIn, pui64Out);
}

/************************************************************************
 * VarUI8FromR8 (OLEAUT32.432)
 *
 * Convert a VT_R8 to a VT_UI8.
 *
 * PARAMS
 *  dblIn    [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  See VarI8FromR8() for details concerning rounding.
 */
HRESULT WINAPI VarUI8FromR8(double dblIn, ULONG64* pui64Out)
{
  if (dblIn < -0.5 || dblIn > 1.844674407370955e19)
    return DISP_E_OVERFLOW;
  OLEAUT32_DutchRound(ULONG64, dblIn, *pui64Out);
  return S_OK;
}

/************************************************************************
 * VarUI8FromCy (OLEAUT32.433)
 *
 * Convert a VT_CY to a VT_UI8.
 *
 * PARAMS
 *  cyIn     [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  Negative values >= -5000 will be converted to 0.
 */
HRESULT WINAPI VarUI8FromCy(CY cyIn, ULONG64* pui64Out)
{
  if (cyIn.int64 < 0)
  {
    if (cyIn.int64 < -CY_HALF)
      return DISP_E_OVERFLOW;
    *pui64Out = 0;
  }
  else
  {
    *pui64Out = cyIn.int64 / CY_MULTIPLIER;

    cyIn.int64 -= *pui64Out * CY_MULTIPLIER; /* cyIn.s.Lo now holds fractional remainder */

    if (cyIn.s.Lo > CY_HALF || (cyIn.s.Lo == CY_HALF && (*pui64Out & 0x1)))
      (*pui64Out)++;
  }
  return S_OK;
}

/************************************************************************
 * VarUI8FromDate (OLEAUT32.434)
 *
 * Convert a VT_DATE to a VT_UI8.
 *
 * PARAMS
 *  dateIn   [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI8FromDate(DATE dateIn, ULONG64* pui64Out)
{
  return _VarUI8FromDate(dateIn, pui64Out);
}

/************************************************************************
 * VarUI8FromStr (OLEAUT32.435)
 *
 * Convert a VT_BSTR to a VT_UI8.
 *
 * PARAMS
 *  strIn    [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI8FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, ULONG64* pui64Out)
{
  return _VarUI8FromStr(strIn, lcid, dwFlags, pui64Out);
}

/************************************************************************
 * VarUI8FromDisp (OLEAUT32.436)
 *
 * Convert a VT_DISPATCH to a VT_UI8.
 *
 * PARAMS
 *  pdispIn   [I] Source
 *  lcid      [I] LCID for conversion
 *  pui64Out  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarUI8FromDisp(IDispatch* pdispIn, LCID lcid, ULONG64* pui64Out)
{
  return _VarUI8FromDisp(pdispIn, lcid, pui64Out);
}

/************************************************************************
 * VarUI8FromBool (OLEAUT32.437)
 *
 * Convert a VT_BOOL to a VT_UI8.
 *
 * PARAMS
 *  boolIn   [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI8FromBool(VARIANT_BOOL boolIn, ULONG64* pui64Out)
{
  return _VarUI8FromBool(boolIn, pui64Out);
}
/************************************************************************
 * VarUI8FromI1 (OLEAUT32.438)
 *
 * Convert a VT_I1 to a VT_UI8.
 *
 * PARAMS
 *  cIn      [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarUI8FromI1(signed char cIn, ULONG64* pui64Out)
{
  return _VarUI8FromI1(cIn, pui64Out);
}

/************************************************************************
 * VarUI8FromUI2 (OLEAUT32.439)
 *
 * Convert a VT_UI2 to a VT_UI8.
 *
 * PARAMS
 *  usIn     [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI8FromUI2(USHORT usIn, ULONG64* pui64Out)
{
  return _VarUI8FromUI2(usIn, pui64Out);
}

/************************************************************************
 * VarUI8FromUI4 (OLEAUT32.440)
 *
 * Convert a VT_UI4 to a VT_UI8.
 *
 * PARAMS
 *  ulIn     [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarUI8FromUI4(ULONG ulIn, ULONG64* pui64Out)
{
  return _VarUI8FromUI4(ulIn, pui64Out);
}

/************************************************************************
 * VarUI8FromDec (OLEAUT32.441)
 *
 * Convert a VT_DECIMAL to a VT_UI8.
 *
 * PARAMS
 *  pDecIn   [I] Source
 *  pui64Out [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  Under native Win32, if the source value has a scale of 0, its sign is
 *  ignored, i.e. this function takes the absolute value rather than fail
 *  with DISP_E_OVERFLOW. This bug has been fixed in Wine's implementation
 *  (use VarAbs() on pDecIn first if you really want this behaviour).
 */
HRESULT WINAPI VarUI8FromDec(DECIMAL *pdecIn, ULONG64* pui64Out)
{
  if (!DEC_SCALE(pdecIn))
  {
    /* This decimal is just a 96 bit integer */
    if (DEC_SIGN(pdecIn) & ~DECIMAL_NEG)
      return E_INVALIDARG;

    if (DEC_HI32(pdecIn))
      return DISP_E_OVERFLOW;

    if (DEC_SIGN(pdecIn))
    {
      WARN("Sign would be ignored under Win32!\n");
      return DISP_E_OVERFLOW;
    }

    *pui64Out = DEC_LO64(pdecIn);
    return S_OK;
  }
  else
  {
    /* Decimal contains a floating point number */
    HRESULT hRet;
    double dbl;

    hRet = _VarR8FromDec(pdecIn, &dbl);
    if (SUCCEEDED(hRet))
      hRet = VarUI8FromR8(dbl, pui64Out);
    return hRet;
  }
}

/* R4
 */

/************************************************************************
 * VarR4FromUI1 (OLEAUT32.68)
 *
 * Convert a VT_UI1 to a VT_R4.
 *
 * PARAMS
 *  bIn     [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromUI1(BYTE bIn, float *pFltOut)
{
  return _VarR4FromUI1(bIn, pFltOut);
}

/************************************************************************
 * VarR4FromI2 (OLEAUT32.69)
 *
 * Convert a VT_I2 to a VT_R4.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromI2(SHORT sIn, float *pFltOut)
{
  return _VarR4FromI2(sIn, pFltOut);
}

/************************************************************************
 * VarR4FromI4 (OLEAUT32.70)
 *
 * Convert a VT_I4 to a VT_R4.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromI4(LONG lIn, float *pFltOut)
{
  return _VarR4FromI4(lIn, pFltOut);
}

/************************************************************************
 * VarR4FromR8 (OLEAUT32.71)
 *
 * Convert a VT_R8 to a VT_R4.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination.
 */
HRESULT WINAPI VarR4FromR8(double dblIn, float *pFltOut)
{
  return _VarR4FromR8(dblIn, pFltOut);
}

/************************************************************************
 * VarR4FromCy (OLEAUT32.72)
 *
 * Convert a VT_CY to a VT_R4.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromCy(CY cyIn, float *pFltOut)
{
  return _VarR4FromCy(cyIn, pFltOut);
}

/************************************************************************
 * VarR4FromDate (OLEAUT32.73)
 *
 * Convert a VT_DATE to a VT_R4.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination.
 */
HRESULT WINAPI VarR4FromDate(DATE dateIn, float *pFltOut)
{
  return _VarR4FromDate(dateIn, pFltOut);
}

/************************************************************************
 * VarR4FromStr (OLEAUT32.74)
 *
 * Convert a VT_BSTR to a VT_R4.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if strIn or pFltOut is invalid.
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR4FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, float *pFltOut)
{
  return _VarR4FromStr(strIn, lcid, dwFlags, pFltOut);
}

/************************************************************************
 * VarR4FromDisp (OLEAUT32.75)
 *
 * Convert a VT_DISPATCH to a VT_R4.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pFltOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR4FromDisp(IDispatch* pdispIn, LCID lcid, float *pFltOut)
{
  return _VarR4FromDisp(pdispIn, lcid, pFltOut);
}

/************************************************************************
 * VarR4FromBool (OLEAUT32.76)
 *
 * Convert a VT_BOOL to a VT_R4.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromBool(VARIANT_BOOL boolIn, float *pFltOut)
{
  return _VarR4FromBool(boolIn, pFltOut);
}

/************************************************************************
 * VarR4FromI1 (OLEAUT32.213)
 *
 * Convert a VT_I1 to a VT_R4.
 *
 * PARAMS
 *  cIn     [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR4FromI1(signed char cIn, float *pFltOut)
{
  return _VarR4FromI1(cIn, pFltOut);
}

/************************************************************************
 * VarR4FromUI2 (OLEAUT32.214)
 *
 * Convert a VT_UI2 to a VT_R4.
 *
 * PARAMS
 *  usIn    [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR4FromUI2(USHORT usIn, float *pFltOut)
{
  return _VarR4FromUI2(usIn, pFltOut);
}

/************************************************************************
 * VarR4FromUI4 (OLEAUT32.215)
 *
 * Convert a VT_UI4 to a VT_R4.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR4FromUI4(ULONG ulIn, float *pFltOut)
{
  return _VarR4FromUI4(ulIn, pFltOut);
}

/************************************************************************
 * VarR4FromDec (OLEAUT32.216)
 *
 * Convert a VT_DECIMAL to a VT_R4.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid.
 */
HRESULT WINAPI VarR4FromDec(DECIMAL* pDecIn, float *pFltOut)
{
  BYTE scale = DEC_SCALE(pDecIn);
  int divisor = 1;
  double highPart;

  if (scale > DEC_MAX_SCALE || DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
    return E_INVALIDARG;

  while (scale--)
    divisor *= 10;

  if (DEC_SIGN(pDecIn))
    divisor = -divisor;

  if (DEC_HI32(pDecIn))
  {
    highPart = (double)DEC_HI32(pDecIn) / (double)divisor;
    highPart *= 1.0e64;
  }
  else
    highPart = 0.0;

  *pFltOut = (double)DEC_LO64(pDecIn) / (double)divisor + highPart;
  return S_OK;
}

/************************************************************************
 * VarR4FromI8 (OLEAUT32.360)
 *
 * Convert a VT_I8 to a VT_R4.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromI8(LONG64 llIn, float *pFltOut)
{
  return _VarR4FromI8(llIn, pFltOut);
}

/************************************************************************
 * VarR4FromUI8 (OLEAUT32.361)
 *
 * Convert a VT_UI8 to a VT_R4.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pFltOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR4FromUI8(ULONG64 ullIn, float *pFltOut)
{
  return _VarR4FromUI8(ullIn, pFltOut);
}

/************************************************************************
 * VarR4CmpR8 (OLEAUT32.316)
 *
 * Compare a VT_R4 to a VT_R8.
 *
 * PARAMS
 *  fltLeft  [I] Source
 *  dblRight [I] Value to compare
 *
 * RETURNS
 *  VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that fltLeft is less than,
 *  equal to or greater than dblRight respectively.
 */
HRESULT WINAPI VarR4CmpR8(float fltLeft, double dblRight)
{
  if (fltLeft < dblRight)
    return VARCMP_LT;
  else if (fltLeft > dblRight)
    return VARCMP_GT;
  return VARCMP_EQ;
}

/* R8
 */

/************************************************************************
 * VarR8FromUI1 (OLEAUT32.78)
 *
 * Convert a VT_UI1 to a VT_R8.
 *
 * PARAMS
 *  bIn     [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromUI1(BYTE bIn, double *pDblOut)
{
  return _VarR8FromUI1(bIn, pDblOut);
}

/************************************************************************
 * VarR8FromI2 (OLEAUT32.79)
 *
 * Convert a VT_I2 to a VT_R8.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromI2(SHORT sIn, double *pDblOut)
{
  return _VarR8FromI2(sIn, pDblOut);
}

/************************************************************************
 * VarR8FromI4 (OLEAUT32.80)
 *
 * Convert a VT_I4 to a VT_R8.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromI4(LONG lIn, double *pDblOut)
{
  return _VarR8FromI4(lIn, pDblOut);
}

/************************************************************************
 * VarR8FromR4 (OLEAUT32.81)
 *
 * Convert a VT_R4 to a VT_R8.
 *
 * PARAMS
 *  fltIn   [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromR4(FLOAT fltIn, double *pDblOut)
{
  return _VarR8FromR4(fltIn, pDblOut);
}

/************************************************************************
 * VarR8FromCy (OLEAUT32.82)
 *
 * Convert a VT_CY to a VT_R8.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromCy(CY cyIn, double *pDblOut)
{
  return _VarR8FromCy(cyIn, pDblOut);
}

/************************************************************************
 * VarR8FromDate (OLEAUT32.83)
 *
 * Convert a VT_DATE to a VT_R8.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromDate(DATE dateIn, double *pDblOut)
{
  return _VarR8FromDate(dateIn, pDblOut);
}

/************************************************************************
 * VarR8FromStr (OLEAUT32.84)
 *
 * Convert a VT_BSTR to a VT_R8.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if strIn or pDblOut is invalid.
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR8FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, double *pDblOut)
{
  return _VarR8FromStr(strIn, lcid, dwFlags, pDblOut);
}

/************************************************************************
 * VarR8FromDisp (OLEAUT32.85)
 *
 * Convert a VT_DISPATCH to a VT_R8.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pDblOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR8FromDisp(IDispatch* pdispIn, LCID lcid, double *pDblOut)
{
  return _VarR8FromDisp(pdispIn, lcid, pDblOut);
}

/************************************************************************
 * VarR8FromBool (OLEAUT32.86)
 *
 * Convert a VT_BOOL to a VT_R8.
 *
 * PARAMS
 *  boolIn  [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromBool(VARIANT_BOOL boolIn, double *pDblOut)
{
  return _VarR8FromBool(boolIn, pDblOut);
}

/************************************************************************
 * VarR8FromI1 (OLEAUT32.217)
 *
 * Convert a VT_I1 to a VT_R8.
 *
 * PARAMS
 *  cIn     [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR8FromI1(signed char cIn, double *pDblOut)
{
  return _VarR8FromI1(cIn, pDblOut);
}

/************************************************************************
 * VarR8FromUI2 (OLEAUT32.218)
 *
 * Convert a VT_UI2 to a VT_R8.
 *
 * PARAMS
 *  usIn    [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR8FromUI2(USHORT usIn, double *pDblOut)
{
  return _VarR8FromUI2(usIn, pDblOut);
}

/************************************************************************
 * VarR8FromUI4 (OLEAUT32.219)
 *
 * Convert a VT_UI4 to a VT_R8.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarR8FromUI4(ULONG ulIn, double *pDblOut)
{
  return _VarR8FromUI4(ulIn, pDblOut);
}

/************************************************************************
 * VarR8FromDec (OLEAUT32.220)
 *
 * Convert a VT_DECIMAL to a VT_R8.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid.
 */
HRESULT WINAPI VarR8FromDec(DECIMAL* pDecIn, double *pDblOut)
{
  BYTE scale = DEC_SCALE(pDecIn);
  double divisor = 1.0, highPart;

  if (scale > DEC_MAX_SCALE || DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
    return E_INVALIDARG;

  while (scale--)
    divisor *= 10;

  if (DEC_SIGN(pDecIn))
    divisor = -divisor;

  if (DEC_HI32(pDecIn))
  {
    highPart = (double)DEC_HI32(pDecIn) / divisor;
    highPart *= 1.0e64;
  }
  else
    highPart = 0.0;

  *pDblOut = (double)DEC_LO64(pDecIn) / divisor + highPart;
  return S_OK;
}

/************************************************************************
 * VarR8FromI8 (OLEAUT32.362)
 *
 * Convert a VT_I8 to a VT_R8.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromI8(LONG64 llIn, double *pDblOut)
{
  return _VarR8FromI8(llIn, pDblOut);
}

/************************************************************************
 * VarR8FromUI8 (OLEAUT32.363)
 *
 * Convert a VT_UI8 to a VT_R8.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarR8FromUI8(ULONG64 ullIn, double *pDblOut)
{
  return _VarR8FromUI8(ullIn, pDblOut);
}

/************************************************************************
 * VarR8Pow (OLEAUT32.315)
 *
 * Raise a VT_R8 to a power.
 *
 * PARAMS
 *  dblLeft [I] Source
 *  dblPow  [I] Power to raise dblLeft by
 *  pDblOut [O] Destination
 *
 * RETURNS
 *  S_OK. pDblOut contains dblLeft to the power of dblRight.
 */
HRESULT WINAPI VarR8Pow(double dblLeft, double dblPow, double *pDblOut)
{
  *pDblOut = pow(dblLeft, dblPow);
  return S_OK;
}

/************************************************************************
 * VarR8Round (OLEAUT32.317)
 *
 * Round a VT_R8 to a given number of decimal points.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  nDig    [I] Number of decimal points to round to
 *  pDblOut [O] Destination for rounded number
 *
 * RETURNS
 *  Success: S_OK. pDblOut is rounded to nDig digits.
 *  Failure: E_INVALIDARG, if cDecimals is less than 0.
 *
 * NOTES
 *  The native version of this function rounds using the internal
 *  binary representation of the number. Wine uses the dutch rounding
 *  convention, so therefore small differences can occur in the value returned.
 *  MSDN says that you should use your own rounding function if you want
 *  rounding to be predictable in your application.
 */
HRESULT WINAPI VarR8Round(double dblIn, int nDig, double *pDblOut)
{
  double scale, whole, fract;

  if (nDig < 0)
    return E_INVALIDARG;

  scale = pow(10.0, nDig);

  dblIn *= scale;
  whole = dblIn < 0 ? ceil(dblIn) : floor(dblIn);
  fract = dblIn - whole;

  if (fract > 0.5)
    dblIn = whole + 1.0;
  else if (fract == 0.5)
    dblIn = whole + fmod(whole, 2.0);
  else if (fract >= 0.0)
    dblIn = whole;
  else if (fract == -0.5)
    dblIn = whole - fmod(whole, 2.0);
  else if (fract > -0.5)
    dblIn = whole;
  else
    dblIn = whole - 1.0;

  *pDblOut = dblIn / scale;
  return S_OK;
}

/* CY
 */

/* Powers of 10 from 0..4 D.P. */
static const int CY_Divisors[5] = { CY_MULTIPLIER/10000, CY_MULTIPLIER/1000,
  CY_MULTIPLIER/100, CY_MULTIPLIER/10, CY_MULTIPLIER };

/************************************************************************
 * VarCyFromUI1 (OLEAUT32.98)
 *
 * Convert a VT_UI1 to a VT_CY.
 *
 * PARAMS
 *  bIn    [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromUI1(BYTE bIn, CY* pCyOut)
{
  return _VarCyFromUI1(bIn, pCyOut);
}

/************************************************************************
 * VarCyFromI2 (OLEAUT32.99)
 *
 * Convert a VT_I2 to a VT_CY.
 *
 * PARAMS
 *  sIn    [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromI2(SHORT sIn, CY* pCyOut)
{
  return _VarCyFromI2(sIn, pCyOut);
}

/************************************************************************
 * VarCyFromI4 (OLEAUT32.100)
 *
 * Convert a VT_I4 to a VT_CY.
 *
 * PARAMS
 *  sIn    [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromI4(LONG lIn, CY* pCyOut)
{
  return _VarCyFromI4(lIn, pCyOut);
}

/************************************************************************
 * VarCyFromR4 (OLEAUT32.101)
 *
 * Convert a VT_R4 to a VT_CY.
 *
 * PARAMS
 *  fltIn  [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromR4(FLOAT fltIn, CY* pCyOut)
{
  return _VarCyFromR4(fltIn, pCyOut);
}

/************************************************************************
 * VarCyFromR8 (OLEAUT32.102)
 *
 * Convert a VT_R8 to a VT_CY.
 *
 * PARAMS
 *  dblIn  [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromR8(double dblIn, CY* pCyOut)
{
#if defined(__GNUC__) && defined(__i386__)
  /* This code gives identical results to Win32 on Intel.
   * Here we use fp exceptions to catch overflows when storing the value.
   */
  static const unsigned short r8_fpcontrol = 0x137f;
  static const double r8_multiplier = CY_MULTIPLIER_F;
  unsigned short old_fpcontrol, result_fpstatus;

  /* Clear exceptions, save the old fp state and load the new state */
  __asm__ __volatile__( "fnclex" );
  __asm__ __volatile__( "fstcw %0"   :   "=m" (old_fpcontrol) : );
  __asm__ __volatile__( "fldcw %0"   : : "m"  (r8_fpcontrol) );
  /* Perform the conversion. */
  __asm__ __volatile__( "fldl  %0"   : : "m"  (dblIn) );
  __asm__ __volatile__( "fmull %0"   : : "m"  (r8_multiplier) );
  __asm__ __volatile__( "fistpll %0" : : "m"  (*pCyOut) );
  /* Save the resulting fp state, load the old state and clear exceptions */
  __asm__ __volatile__( "fstsw %0"   :   "=m" (result_fpstatus) : );
  __asm__ __volatile__( "fnclex" );
  __asm__ __volatile__( "fldcw %0"   : : "m"  (old_fpcontrol) );

  if (result_fpstatus & 0x9) /* Overflow | Invalid */
    return DISP_E_OVERFLOW;
  return S_OK;
#else
  /* This version produces slightly different results for boundary cases */
  if (dblIn < -922337203685477.5807 || dblIn >= 922337203685477.5807)
    return DISP_E_OVERFLOW;
  dblIn *= CY_MULTIPLIER_F;
  OLEAUT32_DutchRound(LONG64, dblIn, pCyOut->int64);
#endif
  return S_OK;
}

/************************************************************************
 * VarCyFromDate (OLEAUT32.103)
 *
 * Convert a VT_DATE to a VT_CY.
 *
 * PARAMS
 *  dateIn [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromDate(DATE dateIn, CY* pCyOut)
{
  return _VarCyFromDate(dateIn, pCyOut);
}

/************************************************************************
 * VarCyFromStr (OLEAUT32.104)
 *
 * Convert a VT_BSTR to a VT_CY.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, CY* pCyOut)
{
  return _VarCyFromStr(strIn, lcid, dwFlags, pCyOut);
}

/************************************************************************
 * VarCyFromDisp (OLEAUT32.105)
 *
 * Convert a VT_DISPATCH to a VT_CY.
 *
 * PARAMS
 *  pdispIn [I] Source
 *  lcid    [I] LCID for conversion
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromDisp(IDispatch* pdispIn, LCID lcid, CY* pCyOut)
{
  return _VarCyFromDisp(pdispIn, lcid, pCyOut);
}

/************************************************************************
 * VarCyFromBool (OLEAUT32.106)
 *
 * Convert a VT_BOOL to a VT_CY.
 *
 * PARAMS
 *  boolIn [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 *
 * NOTES
 *  While the sign of the boolean is stored in the currency, the value is
 *  converted to either 0 or 1.
 */
HRESULT WINAPI VarCyFromBool(VARIANT_BOOL boolIn, CY* pCyOut)
{
  return _VarCyFromBool(boolIn, pCyOut);
}

/************************************************************************
 * VarCyFromI1 (OLEAUT32.225)
 *
 * Convert a VT_I1 to a VT_CY.
 *
 * PARAMS
 *  cIn    [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromI1(signed char cIn, CY* pCyOut)
{
  return _VarCyFromI1(cIn, pCyOut);
}

/************************************************************************
 * VarCyFromUI2 (OLEAUT32.226)
 *
 * Convert a VT_UI2 to a VT_CY.
 *
 * PARAMS
 *  usIn   [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromUI2(USHORT usIn, CY* pCyOut)
{
  return _VarCyFromUI2(usIn, pCyOut);
}

/************************************************************************
 * VarCyFromUI4 (OLEAUT32.227)
 *
 * Convert a VT_UI4 to a VT_CY.
 *
 * PARAMS
 *  ulIn   [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromUI4(ULONG ulIn, CY* pCyOut)
{
  return _VarCyFromUI4(ulIn, pCyOut);
}

/************************************************************************
 * VarCyFromDec (OLEAUT32.228)
 *
 * Convert a VT_DECIMAL to a VT_CY.
 *
 * PARAMS
 *  pdecIn  [I] Source
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromDec(DECIMAL* pdecIn, CY* pCyOut)
{
  DECIMAL rounded;
  HRESULT hRet;

  hRet = VarDecRound(pdecIn, 4, &rounded);

  if (SUCCEEDED(hRet))
  {
    double d;

    if (DEC_HI32(&rounded))
      return DISP_E_OVERFLOW;

    /* Note: Without the casts this promotes to int64 which loses precision */
    d = (double)DEC_LO64(&rounded) / (double)CY_Divisors[DEC_SCALE(&rounded)];
    if (DEC_SIGN(&rounded))
      d = -d;
    return _VarCyFromR8(d, pCyOut);
  }
  return hRet;
}

/************************************************************************
 * VarCyFromI8 (OLEAUT32.366)
 *
 * Convert a VT_I8 to a VT_CY.
 *
 * PARAMS
 *  ullIn  [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromI8(LONG64 llIn, CY* pCyOut)
{
  return _VarCyFromI8(llIn, pCyOut);
}

/************************************************************************
 * VarCyFromUI8 (OLEAUT32.375)
 *
 * Convert a VT_UI8 to a VT_CY.
 *
 * PARAMS
 *  ullIn  [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarCyFromUI8(ULONG64 ullIn, CY* pCyOut)
{
  return _VarCyFromUI8(ullIn, pCyOut);
}

/************************************************************************
 * VarCyAdd (OLEAUT32.299)
 *
 * Add one CY to another.
 *
 * PARAMS
 *  cyLeft  [I] Source
 *  cyRight [I] Value to add
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCyAdd(const CY cyLeft, const CY cyRight, CY* pCyOut)
{
  double l,r;
  _VarR8FromCy(cyLeft, &l);
  _VarR8FromCy(cyRight, &r);
  l = l + r;
  return _VarCyFromR8(l, pCyOut);
}

/************************************************************************
 * VarCyMul (OLEAUT32.303)
 *
 * Multiply one CY by another.
 *
 * PARAMS
 *  cyLeft  [I] Source
 *  cyRight [I] Value to multiply by
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCyMul(const CY cyLeft, const CY cyRight, CY* pCyOut)
{
  double l,r;
  _VarR8FromCy(cyLeft, &l);
  _VarR8FromCy(cyRight, &r);
  l = l * r;
  return _VarCyFromR8(l, pCyOut);
}

/************************************************************************
 * VarCyMulI4 (OLEAUT32.304)
 *
 * Multiply one CY by a VT_I4.
 *
 * PARAMS
 *  cyLeft  [I] Source
 *  lRight  [I] Value to multiply by
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCyMulI4(const CY cyLeft, LONG lRight, CY* pCyOut)
{
  double d;

  _VarR8FromCy(cyLeft, &d);
  d = d * lRight;
  return _VarCyFromR8(d, pCyOut);
}

/************************************************************************
 * VarCySub (OLEAUT32.305)
 *
 * Subtract one CY from another.
 *
 * PARAMS
 *  cyLeft  [I] Source
 *  cyRight [I] Value to subtract
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCySub(const CY cyLeft, const CY cyRight, CY* pCyOut)
{
  double l,r;
  _VarR8FromCy(cyLeft, &l);
  _VarR8FromCy(cyRight, &r);
  l = l - r;
  return _VarCyFromR8(l, pCyOut);
}

/************************************************************************
 * VarCyAbs (OLEAUT32.306)
 *
 * Convert a VT_CY into its absolute value.
 *
 * PARAMS
 *  cyIn   [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK. pCyOut contains the absolute value.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCyAbs(const CY cyIn, CY* pCyOut)
{
  if (cyIn.s.Hi == (int)0x80000000 && !cyIn.s.Lo)
    return DISP_E_OVERFLOW;

  pCyOut->int64 = cyIn.int64 < 0 ? -cyIn.int64 : cyIn.int64;
  return S_OK;
}

/************************************************************************
 * VarCyFix (OLEAUT32.307)
 *
 * Return the integer part of a VT_CY.
 *
 * PARAMS
 *  cyIn   [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  - The difference between this function and VarCyInt() is that VarCyInt() rounds
 *    negative numbers away from 0, while this function rounds them towards zero.
 */
HRESULT WINAPI VarCyFix(const CY cyIn, CY* pCyOut)
{
  pCyOut->int64 = cyIn.int64 / CY_MULTIPLIER;
  pCyOut->int64 *= CY_MULTIPLIER;
  return S_OK;
}

/************************************************************************
 * VarCyInt (OLEAUT32.308)
 *
 * Return the integer part of a VT_CY.
 *
 * PARAMS
 *  cyIn   [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  - The difference between this function and VarCyFix() is that VarCyFix() rounds
 *    negative numbers towards 0, while this function rounds them away from zero.
 */
HRESULT WINAPI VarCyInt(const CY cyIn, CY* pCyOut)
{
  pCyOut->int64 = cyIn.int64 / CY_MULTIPLIER;
  pCyOut->int64 *= CY_MULTIPLIER;

  if (cyIn.int64 < 0 && cyIn.int64 % CY_MULTIPLIER != 0)
  {
    pCyOut->int64 -= CY_MULTIPLIER;
  }
  return S_OK;
}

/************************************************************************
 * VarCyNeg (OLEAUT32.309)
 *
 * Change the sign of a VT_CY.
 *
 * PARAMS
 *  cyIn   [I] Source
 *  pCyOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCyNeg(const CY cyIn, CY* pCyOut)
{
  if (cyIn.s.Hi == (int)0x80000000 && !cyIn.s.Lo)
    return DISP_E_OVERFLOW;

  pCyOut->int64 = -cyIn.int64;
  return S_OK;
}

/************************************************************************
 * VarCyRound (OLEAUT32.310)
 *
 * Change the precision of a VT_CY.
 *
 * PARAMS
 *  cyIn      [I] Source
 *  cDecimals [I] New number of decimals to keep
 *  pCyOut    [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if cDecimals is less than 0.
 */
HRESULT WINAPI VarCyRound(const CY cyIn, int cDecimals, CY* pCyOut)
{
  if (cDecimals < 0)
    return E_INVALIDARG;

  if (cDecimals > 3)
  {
    /* Rounding to more precision than we have */
    *pCyOut = cyIn;
    return S_OK;
  }
  else
  {
    double d, div = CY_Divisors[cDecimals];

    _VarR8FromCy(cyIn, &d);
    d = d * div;
    OLEAUT32_DutchRound(LONGLONG, d, pCyOut->int64)
    d = (double)pCyOut->int64 / div * CY_MULTIPLIER_F;
    OLEAUT32_DutchRound(LONGLONG, d, pCyOut->int64)
    return S_OK;
  }
}

/************************************************************************
 * VarCyCmp (OLEAUT32.311)
 *
 * Compare two VT_CY values.
 *
 * PARAMS
 *  cyLeft  [I] Source
 *  cyRight [I] Value to compare
 *
 * RETURNS
 *  Success: VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that the value to
 *           compare is less, equal or greater than source respectively.
 *  Failure: DISP_E_OVERFLOW, if overflow occurs during the comparison
 */
HRESULT WINAPI VarCyCmp(const CY cyLeft, const CY cyRight)
{
  HRESULT hRet;
  CY result;

  /* Subtract right from left, and compare the result to 0 */
  hRet = VarCySub(cyLeft, cyRight, &result);

  if (SUCCEEDED(hRet))
  {
    if (result.int64 < 0)
      hRet = (HRESULT)VARCMP_LT;
    else if (result.int64 > 0)
      hRet = (HRESULT)VARCMP_GT;
    else
      hRet = (HRESULT)VARCMP_EQ;
  }
  return hRet;
}

/************************************************************************
 * VarCyCmpR8 (OLEAUT32.312)
 *
 * Compare a VT_CY to a double
 *
 * PARAMS
 *  cyLeft   [I] Currency Source
 *  dblRight [I] double to compare to cyLeft
 *
 * RETURNS
 *  Success: VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that dblRight is
 *           less than, equal to or greater than cyLeft respectively.
 *  Failure: DISP_E_OVERFLOW, if overflow occurs during the comparison
 */
HRESULT WINAPI VarCyCmpR8(const CY cyLeft, double dblRight)
{
  HRESULT hRet;
  CY cyRight;

  hRet = _VarCyFromR8(dblRight, &cyRight);

  if (SUCCEEDED(hRet))
    hRet = VarCyCmp(cyLeft, cyRight);

  return hRet;
}

/************************************************************************
 * VarCyMulI8 (OLEAUT32.329)
 *
 * Multiply a VT_CY by a VT_I8.
 *
 * PARAMS
 *  cyLeft  [I] Source
 *  llRight [I] Value to multiply by
 *  pCyOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarCyMulI8(const CY cyLeft, LONG64 llRight, CY* pCyOut)
{
  double d;

  _VarR8FromCy(cyLeft, &d);
  d = d  * (double)llRight;
  return _VarCyFromR8(d, pCyOut);
}

/* DECIMAL
 */

/************************************************************************
 * VarDecFromUI1 (OLEAUT32.190)
 *
 * Convert a VT_UI1 to a DECIMAL.
 *
 * PARAMS
 *  bIn     [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromUI1(BYTE bIn, DECIMAL* pDecOut)
{
  return _VarDecFromUI1(bIn, pDecOut);
}

/************************************************************************
 * VarDecFromI2 (OLEAUT32.191)
 *
 * Convert a VT_I2 to a DECIMAL.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromI2(SHORT sIn, DECIMAL* pDecOut)
{
  return _VarDecFromI2(sIn, pDecOut);
}

/************************************************************************
 * VarDecFromI4 (OLEAUT32.192)
 *
 * Convert a VT_I4 to a DECIMAL.
 *
 * PARAMS
 *  sIn     [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromI4(LONG lIn, DECIMAL* pDecOut)
{
  DEC_HI32(pDecOut) = 0;
  DEC_MID32(pDecOut) = 0;

  if (lIn < 0)
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_NEG,0);
    DEC_LO32(pDecOut) = -lIn;
  }
  else
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_POS,0);
    DEC_LO32(pDecOut) = lIn;
  }
  return S_OK;
}

/************************************************************************
 * VarDecFromR4 (OLEAUT32.193)
 *
 * Convert a VT_R4 to a DECIMAL.
 *
 * PARAMS
 *  fltIn   [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromR4(FLOAT fltIn, DECIMAL* pDecOut)
{
  WCHAR buff[256];

  sprintfW( buff, szFloatFormatW, fltIn );
  return _VarDecFromStr(buff, LOCALE_SYSTEM_DEFAULT, 0, pDecOut);
}

/************************************************************************
 * VarDecFromR8 (OLEAUT32.194)
 *
 * Convert a VT_R8 to a DECIMAL.
 *
 * PARAMS
 *  dblIn   [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromR8(double dblIn, DECIMAL* pDecOut)
{
  WCHAR buff[256];

  sprintfW( buff, szDoubleFormatW, dblIn );
  return _VarDecFromStr(buff, LOCALE_USER_DEFAULT, 0, pDecOut);
}

/************************************************************************
 * VarDecFromDate (OLEAUT32.195)
 *
 * Convert a VT_DATE to a DECIMAL.
 *
 * PARAMS
 *  dateIn  [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromDate(DATE dateIn, DECIMAL* pDecOut)
{
  return _VarDecFromDate(dateIn, pDecOut);
}

/************************************************************************
 * VarDecFromCy (OLEAUT32.196)
 *
 * Convert a VT_CY to a DECIMAL.
 *
 * PARAMS
 *  cyIn    [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromCy(CY cyIn, DECIMAL* pDecOut)
{
  DEC_HI32(pDecOut) = 0;

  /* Note: This assumes 2s complement integer representation */
  if (cyIn.s.Hi & 0x80000000)
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_NEG,4);
    DEC_LO64(pDecOut) = -cyIn.int64;
  }
  else
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_POS,4);
    DEC_MID32(pDecOut) = cyIn.s.Hi;
    DEC_LO32(pDecOut) = cyIn.s.Lo;
  }
  return S_OK;
}

/************************************************************************
 * VarDecFromStr (OLEAUT32.197)
 *
 * Convert a VT_BSTR to a DECIMAL.
 *
 * PARAMS
 *  strIn   [I] Source
 *  lcid    [I] LCID for the conversion
 *  dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDecFromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, DECIMAL* pDecOut)
{
  return _VarDecFromStr(strIn, lcid, dwFlags, pDecOut);
}

/************************************************************************
 * VarDecFromDisp (OLEAUT32.198)
 *
 * Convert a VT_DISPATCH to a DECIMAL.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pDecOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarDecFromDisp(IDispatch* pdispIn, LCID lcid, DECIMAL* pDecOut)
{
  return _VarDecFromDisp(pdispIn, lcid, pDecOut);
}

/************************************************************************
 * VarDecFromBool (OLEAUT32.199)
 *
 * Convert a VT_BOOL to a DECIMAL.
 *
 * PARAMS
 *  bIn     [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 *
 * NOTES
 *  The value is converted to either 0 (if bIn is FALSE) or -1 (TRUE).
 */
HRESULT WINAPI VarDecFromBool(VARIANT_BOOL bIn, DECIMAL* pDecOut)
{
  DEC_HI32(pDecOut) = 0;
  DEC_MID32(pDecOut) = 0;
  if (bIn)
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_NEG,0);
    DEC_LO32(pDecOut) = 1;
  }
  else
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_POS,0);
    DEC_LO32(pDecOut) = 0;
  }
  return S_OK;
}

/************************************************************************
 * VarDecFromI1 (OLEAUT32.241)
 *
 * Convert a VT_I1 to a DECIMAL.
 *
 * PARAMS
 *  cIn     [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromI1(signed char cIn, DECIMAL* pDecOut)
{
  return _VarDecFromI1(cIn, pDecOut);
}

/************************************************************************
 * VarDecFromUI2 (OLEAUT32.242)
 *
 * Convert a VT_UI2 to a DECIMAL.
 *
 * PARAMS
 *  usIn    [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromUI2(USHORT usIn, DECIMAL* pDecOut)
{
  return _VarDecFromUI2(usIn, pDecOut);
}

/************************************************************************
 * VarDecFromUI4 (OLEAUT32.243)
 *
 * Convert a VT_UI4 to a DECIMAL.
 *
 * PARAMS
 *  ulIn    [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromUI4(ULONG ulIn, DECIMAL* pDecOut)
{
  DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_POS,0);
  DEC_HI32(pDecOut) = 0;
  DEC_MID32(pDecOut) = 0;
  DEC_LO32(pDecOut) = ulIn;
  return S_OK;
}

/************************************************************************
 * VarDecFromI8 (OLEAUT32.374)
 *
 * Convert a VT_I8 to a DECIMAL.
 *
 * PARAMS
 *  llIn    [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromI8(LONG64 llIn, DECIMAL* pDecOut)
{
  PULARGE_INTEGER pLi = (PULARGE_INTEGER)&llIn;

  DEC_HI32(pDecOut) = 0;

  /* Note: This assumes 2s complement integer representation */
  if (pLi->u.HighPart & 0x80000000)
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_NEG,0);
    DEC_LO64(pDecOut) = -pLi->QuadPart;
  }
  else
  {
    DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_POS,0);
    DEC_MID32(pDecOut) = pLi->u.HighPart;
    DEC_LO32(pDecOut) = pLi->u.LowPart;
  }
  return S_OK;
}

/************************************************************************
 * VarDecFromUI8 (OLEAUT32.375)
 *
 * Convert a VT_UI8 to a DECIMAL.
 *
 * PARAMS
 *  ullIn   [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDecFromUI8(ULONG64 ullIn, DECIMAL* pDecOut)
{
  DEC_SIGNSCALE(pDecOut) = SIGNSCALE(DECIMAL_POS,0);
  DEC_HI32(pDecOut) = 0;
  DEC_LO64(pDecOut) = ullIn;
  return S_OK;
}

/* Make two DECIMALS the same scale; used by math functions below */
static HRESULT VARIANT_DecScale(const DECIMAL** ppDecLeft,
                                const DECIMAL** ppDecRight,
                                DECIMAL* pDecOut)
{
  static DECIMAL scaleFactor;
  DECIMAL decTemp;
  int scaleAmount, i;
  HRESULT hRet = S_OK;

  if (DEC_SIGN(*ppDecLeft) & ~DECIMAL_NEG || DEC_SIGN(*ppDecRight) & ~DECIMAL_NEG)
    return E_INVALIDARG;

  DEC_LO32(&scaleFactor) = 10;

  i = scaleAmount = DEC_SCALE(*ppDecLeft) - DEC_SCALE(*ppDecRight);

  if (!scaleAmount)
    return S_OK; /* Same scale */

  if (scaleAmount > 0)
  {
    decTemp = *(*ppDecRight); /* Left is bigger - scale the right hand side */
    *ppDecRight = pDecOut;
  }
  else
  {
    decTemp = *(*ppDecLeft); /* Right is bigger - scale the left hand side */
    *ppDecLeft = pDecOut;
    i = scaleAmount = -scaleAmount;
  }

  if (DEC_SCALE(&decTemp) + scaleAmount > DEC_MAX_SCALE)
    return DISP_E_OVERFLOW; /* Can't scale up */

  /* Multiply up the value to be scaled by the correct amount */
  while (SUCCEEDED(hRet) && i--)
  {
    /* Note we are multiplying by a value with a scale of 0, so we don't recurse */
    hRet = VarDecMul(&decTemp, &scaleFactor, pDecOut);
    decTemp = *pDecOut;
  }
  DEC_SCALE(pDecOut) += scaleAmount; /* Set the new scale */
  return hRet;
}

/* Add two unsigned 32 bit values with overflow */
static ULONG VARIANT_Add(ULONG ulLeft, ULONG ulRight, ULONG* pulHigh)
{
  ULARGE_INTEGER ul64;

  ul64.QuadPart = (ULONG64)ulLeft + (ULONG64)ulRight + (ULONG64)*pulHigh;
  *pulHigh = ul64.u.HighPart;
  return ul64.u.LowPart;
}

/* Subtract two unsigned 32 bit values with underflow */
static ULONG VARIANT_Sub(ULONG ulLeft, ULONG ulRight, ULONG* pulHigh)
{
  int invert = 0;
  ULARGE_INTEGER ul64;

  ul64.QuadPart = (LONG64)ulLeft - (ULONG64)ulRight;
  if (ulLeft < ulRight)
    invert = 1;

  if (ul64.QuadPart > (ULONG64)*pulHigh)
    ul64.QuadPart -= (ULONG64)*pulHigh;
  else
  {
    ul64.QuadPart -= (ULONG64)*pulHigh;
    invert = 1;
  }
  if (invert)
    ul64.u.HighPart = -ul64.u.HighPart ;

  *pulHigh = ul64.u.HighPart;
  return ul64.u.LowPart;
}

/* Multiply two unsigned 32 bit values with overflow */
static ULONG VARIANT_Mul(ULONG ulLeft, ULONG ulRight, ULONG* pulHigh)
{
  ULARGE_INTEGER ul64;

  ul64.QuadPart = (ULONG64)ulLeft * (ULONG64)ulRight + (ULONG64)*pulHigh;
  *pulHigh = ul64.u.HighPart;
  return ul64.u.LowPart;
}

/* Compare two decimals that have the same scale */
static inline int VARIANT_DecCmp(const DECIMAL *pDecLeft, const DECIMAL *pDecRight)
{
  if ( DEC_HI32(pDecLeft) < DEC_HI32(pDecRight) ||
      (DEC_HI32(pDecLeft) <= DEC_HI32(pDecRight) && DEC_LO64(pDecLeft) < DEC_LO64(pDecRight)))
    return -1;
  else if (DEC_HI32(pDecLeft) == DEC_HI32(pDecRight) && DEC_LO64(pDecLeft) == DEC_LO64(pDecRight))
    return 0;
  return 1;
}

/************************************************************************
 * VarDecAdd (OLEAUT32.177)
 *
 * Add one DECIMAL to another.
 *
 * PARAMS
 *  pDecLeft  [I] Source
 *  pDecRight [I] Value to add
 *  pDecOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDecAdd(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
{
  HRESULT hRet;
  DECIMAL scaled;

  hRet = VARIANT_DecScale(&pDecLeft, &pDecRight, &scaled);

  if (SUCCEEDED(hRet))
  {
    /* Our decimals now have the same scale, we can add them as 96 bit integers */
    ULONG overflow = 0;
    BYTE sign = DECIMAL_POS;

    /* Correct for the sign of the result */
    if (DEC_SIGN(pDecLeft) && DEC_SIGN(pDecRight))
    {
      /* -x + -y : Negative */
      sign = DECIMAL_NEG;
      goto VarDecAdd_AsPositive;
    }
    else if (DEC_SIGN(pDecLeft) && !DEC_SIGN(pDecRight))
    {
      int cmp = VARIANT_DecCmp(pDecLeft, pDecRight);

      /* -x + y : Negative if x > y */
      if (cmp > 0)
      {
        sign = DECIMAL_NEG;
VarDecAdd_AsNegative:
        DEC_LO32(pDecOut)  = VARIANT_Sub(DEC_LO32(pDecLeft),  DEC_LO32(pDecRight),  &overflow);
        DEC_MID32(pDecOut) = VARIANT_Sub(DEC_MID32(pDecLeft), DEC_MID32(pDecRight), &overflow);
        DEC_HI32(pDecOut)  = VARIANT_Sub(DEC_HI32(pDecLeft),  DEC_HI32(pDecRight),  &overflow);
      }
      else
      {
VarDecAdd_AsInvertedNegative:
        DEC_LO32(pDecOut)  = VARIANT_Sub(DEC_LO32(pDecRight),  DEC_LO32(pDecLeft),  &overflow);
        DEC_MID32(pDecOut) = VARIANT_Sub(DEC_MID32(pDecRight), DEC_MID32(pDecLeft), &overflow);
        DEC_HI32(pDecOut)  = VARIANT_Sub(DEC_HI32(pDecRight),  DEC_HI32(pDecLeft),  &overflow);
      }
    }
    else if (!DEC_SIGN(pDecLeft) && DEC_SIGN(pDecRight))
    {
      int cmp = VARIANT_DecCmp(pDecLeft, pDecRight);

      /* x + -y : Negative if x <= y */
      if (cmp <= 0)
      {
        sign = DECIMAL_NEG;
        goto VarDecAdd_AsInvertedNegative;
      }
      goto VarDecAdd_AsNegative;
    }
    else
    {
      /* x + y : Positive */
VarDecAdd_AsPositive:
      DEC_LO32(pDecOut)  = VARIANT_Add(DEC_LO32(pDecLeft),  DEC_LO32(pDecRight),  &overflow);
      DEC_MID32(pDecOut) = VARIANT_Add(DEC_MID32(pDecLeft), DEC_MID32(pDecRight), &overflow);
      DEC_HI32(pDecOut)  = VARIANT_Add(DEC_HI32(pDecLeft),  DEC_HI32(pDecRight),  &overflow);
    }

    if (overflow)
      return DISP_E_OVERFLOW; /* overflowed */

    DEC_SCALE(pDecOut) = DEC_SCALE(pDecLeft);
    DEC_SIGN(pDecOut) = sign;
  }
  return hRet;
}

/************************************************************************
 * VarDecDiv (OLEAUT32.178)
 *
 * Divide one DECIMAL by another.
 *
 * PARAMS
 *  pDecLeft  [I] Source
 *  pDecRight [I] Value to divide by
 *  pDecOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDecDiv(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
{
  FIXME("(%p,%p,%p)-stub!\n",pDecLeft,pDecRight,pDecOut);
  return DISP_E_OVERFLOW;
}

/************************************************************************
 * VarDecMul (OLEAUT32.179)
 *
 * Multiply one DECIMAL by another.
 *
 * PARAMS
 *  pDecLeft  [I] Source
 *  pDecRight [I] Value to multiply by
 *  pDecOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDecMul(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
{
  /* FIXME: This only allows multiplying by a fixed integer <= 0xffffffff */

  if (!DEC_SCALE(pDecLeft) || !DEC_SCALE(pDecRight))
  {
    /* At least one term is an integer */
    const DECIMAL* pDecInteger = DEC_SCALE(pDecLeft) ? pDecRight : pDecLeft;
    const DECIMAL* pDecOperand = DEC_SCALE(pDecLeft) ? pDecLeft  : pDecRight;
    HRESULT hRet = S_OK;
    unsigned int multiplier = DEC_LO32(pDecInteger);
    ULONG overflow = 0;

    if (DEC_HI32(pDecInteger) || DEC_MID32(pDecInteger))
    {
      FIXME("(%p,%p,%p) semi-stub!\n",pDecLeft,pDecRight,pDecOut);
      return DISP_E_OVERFLOW;
    }

    DEC_LO32(pDecOut)  = VARIANT_Mul(DEC_LO32(pDecOperand),  multiplier, &overflow);
    DEC_MID32(pDecOut) = VARIANT_Mul(DEC_MID32(pDecOperand), multiplier, &overflow);
    DEC_HI32(pDecOut)  = VARIANT_Mul(DEC_HI32(pDecOperand),  multiplier, &overflow);

    if (overflow)
       hRet = DISP_E_OVERFLOW;
    else
    {
      BYTE sign = DECIMAL_POS;

      if (DEC_SIGN(pDecLeft) != DEC_SIGN(pDecRight))
        sign = DECIMAL_NEG; /* pos * neg => negative */
      DEC_SIGN(pDecOut) = sign;
      DEC_SCALE(pDecOut) = DEC_SCALE(pDecOperand);
    }
    return hRet;
  }
  FIXME("(%p,%p,%p) semi-stub!\n",pDecLeft,pDecRight,pDecOut);
  return DISP_E_OVERFLOW;
}

/************************************************************************
 * VarDecSub (OLEAUT32.181)
 *
 * Subtract one DECIMAL from another.
 *
 * PARAMS
 *  pDecLeft  [I] Source
 *  pDecRight [I] DECIMAL to subtract from pDecLeft
 *  pDecOut   [O] Destination
 *
 * RETURNS
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDecSub(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
{
  DECIMAL decRight;

  /* Implement as addition of the negative */
  VarDecNeg(pDecRight, &decRight);
  return VarDecAdd(pDecLeft, &decRight, pDecOut);
}

/************************************************************************
 * VarDecAbs (OLEAUT32.182)
 *
 * Convert a DECIMAL into its absolute value.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK. This function does not fail.
 */
HRESULT WINAPI VarDecAbs(const DECIMAL* pDecIn, DECIMAL* pDecOut)
{
  *pDecOut = *pDecIn;
  DEC_SIGN(pDecOut) &= ~DECIMAL_NEG;
  return S_OK;
}

/************************************************************************
 * VarDecFix (OLEAUT32.187)
 *
 * Return the integer portion of a DECIMAL.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  - The difference between this function and VarDecInt() is that VarDecInt() rounds
 *    negative numbers away from 0, while this function rounds them towards zero.
 */
HRESULT WINAPI VarDecFix(const DECIMAL* pDecIn, DECIMAL* pDecOut)
{
  if (DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
    return E_INVALIDARG;

  if (!DEC_SCALE(pDecIn))
  {
    *pDecOut = *pDecIn; /* Already an integer */
    return S_OK;
  }

  FIXME("semi-stub!\n");
  return DISP_E_OVERFLOW;
}

/************************************************************************
 * VarDecInt (OLEAUT32.188)
 *
 * Return the integer portion of a DECIMAL.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 *
 * NOTES
 *  - The difference between this function and VarDecFix() is that VarDecFix() rounds
 *    negative numbers towards 0, while this function rounds them away from zero.
 */
HRESULT WINAPI VarDecInt(const DECIMAL* pDecIn, DECIMAL* pDecOut)
{
  if (DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
    return E_INVALIDARG;

  if (!(DEC_SIGN(pDecIn) & DECIMAL_NEG) || !DEC_SCALE(pDecIn))
    return VarDecFix(pDecIn, pDecOut); /* The same, if +ve or no fractionals */

  FIXME("semi-stub!\n");
  return DISP_E_OVERFLOW;
}

/************************************************************************
 * VarDecNeg (OLEAUT32.189)
 *
 * Change the sign of a DECIMAL.
 *
 * PARAMS
 *  pDecIn  [I] Source
 *  pDecOut [O] Destination
 *
 * RETURNS
 *  S_OK. This function does not fail.
 */
HRESULT WINAPI VarDecNeg(const DECIMAL* pDecIn, DECIMAL* pDecOut)
{
  *pDecOut = *pDecIn;
  DEC_SIGN(pDecOut) ^= DECIMAL_NEG;
  return S_OK;
}

/************************************************************************
 * VarDecRound (OLEAUT32.203)
 *
 * Change the precision of a DECIMAL.
 *
 * PARAMS
 *  pDecIn    [I] Source
 *  cDecimals [I] New number of decimals to keep
 *  pDecOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK. pDecOut contains the rounded value.
 *  Failure: E_INVALIDARG if any argument is invalid.
 */
HRESULT WINAPI VarDecRound(const DECIMAL* pDecIn, int cDecimals, DECIMAL* pDecOut)
{
  if (cDecimals < 0 || (DEC_SIGN(pDecIn) & ~DECIMAL_NEG) || DEC_SCALE(pDecIn) > DEC_MAX_SCALE)
    return E_INVALIDARG;

  if (cDecimals >= DEC_SCALE(pDecIn))
  {
    *pDecOut = *pDecIn; /* More precision than we have */
    return S_OK;
  }

  FIXME("semi-stub!\n");

  return DISP_E_OVERFLOW;
}

/************************************************************************
 * VarDecCmp (OLEAUT32.204)
 *
 * Compare two DECIMAL values.
 *
 * PARAMS
 *  pDecLeft  [I] Source
 *  pDecRight [I] Value to compare
 *
 * RETURNS
 *  Success: VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that pDecLeft
 *           is less than, equal to or greater than pDecRight respectively.
 *  Failure: DISP_E_OVERFLOW, if overflow occurs during the comparison
 */
HRESULT WINAPI VarDecCmp(const DECIMAL* pDecLeft, const DECIMAL* pDecRight)
{
  HRESULT hRet;
  DECIMAL result;

  /* Subtract right from left, and compare the result to 0 */
  hRet = VarDecSub(pDecLeft, pDecRight, &result);

  if (SUCCEEDED(hRet))
  {
    int non_zero = DEC_HI32(&result) | DEC_MID32(&result) | DEC_LO32(&result);

    if ((DEC_SIGN(&result) & DECIMAL_NEG) && non_zero)
      hRet = (HRESULT)VARCMP_LT;
    else if (non_zero)
      hRet = (HRESULT)VARCMP_GT;
    else
      hRet = (HRESULT)VARCMP_EQ;
  }
  return hRet;
}

/************************************************************************
 * VarDecCmpR8 (OLEAUT32.298)
 *
 * Compare a DECIMAL to a double
 *
 * PARAMS
 *  pDecLeft [I] DECIMAL Source
 *  dblRight [I] double to compare to pDecLeft
 *
 * RETURNS
 *  Success: VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that dblRight
 *           is less than, equal to or greater than pDecLeft respectively.
 *  Failure: DISP_E_OVERFLOW, if overflow occurs during the comparison
 */
HRESULT WINAPI VarDecCmpR8(const DECIMAL* pDecLeft, double dblRight)
{
  HRESULT hRet;
  DECIMAL decRight;

  hRet = VarDecFromR8(dblRight, &decRight);

  if (SUCCEEDED(hRet))
    hRet = VarDecCmp(pDecLeft, &decRight);

  return hRet;
}

/* BOOL
 */

/************************************************************************
 * VarBoolFromUI1 (OLEAUT32.118)
 *
 * Convert a VT_UI1 to a VT_BOOL.
 *
 * PARAMS
 *  bIn      [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromUI1(BYTE bIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromUI1(bIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromI2 (OLEAUT32.119)
 *
 * Convert a VT_I2 to a VT_BOOL.
 *
 * PARAMS
 *  sIn      [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromI2(SHORT sIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromI2(sIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromI4 (OLEAUT32.120)
 *
 * Convert a VT_I4 to a VT_BOOL.
 *
 * PARAMS
 *  sIn      [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromI4(LONG lIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromI4(lIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromR4 (OLEAUT32.121)
 *
 * Convert a VT_R4 to a VT_BOOL.
 *
 * PARAMS
 *  fltIn    [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromR4(FLOAT fltIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromR4(fltIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromR8 (OLEAUT32.122)
 *
 * Convert a VT_R8 to a VT_BOOL.
 *
 * PARAMS
 *  dblIn    [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromR8(double dblIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromR8(dblIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromDate (OLEAUT32.123)
 *
 * Convert a VT_DATE to a VT_BOOL.
 *
 * PARAMS
 *  dateIn   [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromDate(DATE dateIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromDate(dateIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromCy (OLEAUT32.124)
 *
 * Convert a VT_CY to a VT_BOOL.
 *
 * PARAMS
 *  cyIn     [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromCy(CY cyIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromCy(cyIn, pBoolOut);
}

static BOOL VARIANT_GetLocalisedText(LANGID langId, DWORD dwId, WCHAR *lpszDest)
{
  HRSRC hrsrc;

  hrsrc = FindResourceExW( OLEAUT32_hModule, (LPWSTR)RT_STRING,
                           (LPCWSTR)((dwId >> 4) + 1), langId );
  if (hrsrc)
  {
    HGLOBAL hmem = LoadResource( OLEAUT32_hModule, hrsrc );

    if (hmem)
    {
      const WCHAR *p;
      unsigned int i;

      p = LockResource( hmem );
      for (i = 0; i < (dwId & 0x0f); i++) p += *p + 1;

      memcpy( lpszDest, p + 1, *p * sizeof(WCHAR) );
      lpszDest[*p] = '\0';
      TRACE("got %s for LANGID %08x\n", debugstr_w(lpszDest), langId);
      return TRUE;
    }
  }
  return FALSE;
}

/************************************************************************
 * VarBoolFromStr (OLEAUT32.125)
 *
 * Convert a VT_BSTR to a VT_BOOL.
 *
 * PARAMS
 *  strIn    [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pBoolOut is invalid.
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 *
 * NOTES
 *  - strIn will be recognised if it contains "#TRUE#" or "#FALSE#". Additionally,
 *  it may contain (in any case mapping) the text "true" or "false".
 *  - If dwFlags includes VAR_LOCALBOOL, then the text may also match the
 *  localised text of "True" or "False" in the language specified by lcid.
 *  - If none of these matches occur, the string is treated as a numeric string
 *  and the boolean pBoolOut will be set according to whether the number is zero
 *  or not. The dwFlags parameter is passed to VarR8FromStr() for this conversion.
 *  - If the text is not numeric and does not match any of the above, then
 *  DISP_E_TYPEMISMATCH is returned.
 */
HRESULT WINAPI VarBoolFromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, VARIANT_BOOL *pBoolOut)
{
  /* Any VB/VBA programmers out there should recognise these strings... */
  static const WCHAR szFalse[] = { '#','F','A','L','S','E','#','\0' };
  static const WCHAR szTrue[] = { '#','T','R','U','E','#','\0' };
  WCHAR szBuff[64];
  LANGID langId = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
  HRESULT hRes = S_OK;

  if (!strIn || !pBoolOut)
    return DISP_E_TYPEMISMATCH;

  /* Check if we should be comparing against localised text */
  if (dwFlags & VAR_LOCALBOOL)
  {
    /* Convert our LCID into a usable value */
    lcid = ConvertDefaultLocale(lcid);

    langId = LANGIDFROMLCID(lcid);

    if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
      langId = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);

    /* Note: Native oleaut32 always copies strIn and maps halfwidth characters.
     * I don't think this is needed unless any of the localised text strings
     * contain characters that can be so mapped. In the event that this is
     * true for a given language (possibly some Asian languages), then strIn
     * should be mapped here _only_ if langId is an Id for which this can occur.
     */
  }

  /* Note that if we are not comparing against localised strings, langId
   * will have its default value of LANG_ENGLISH. This allows us to mimic
   * the native behaviour of always checking against English strings even
   * after we've checked for localised ones.
   */
VarBoolFromStr_CheckLocalised:
  if (VARIANT_GetLocalisedText(langId, IDS_TRUE, szBuff))
  {
    /* Compare against localised strings, ignoring case */
    if (!strcmpiW(strIn, szBuff))
    {
      *pBoolOut = VARIANT_TRUE; /* Matched localised 'true' text */
      return hRes;
    }
    VARIANT_GetLocalisedText(langId, IDS_FALSE, szBuff);
    if (!strcmpiW(strIn, szBuff))
    {
      *pBoolOut = VARIANT_FALSE; /* Matched localised 'false' text */
      return hRes;
    }
  }

  if (langId != MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT))
  {
    /* We have checked the localised text, now check English */
    langId = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
    goto VarBoolFromStr_CheckLocalised;
  }

  /* All checks against localised text have failed, try #TRUE#/#FALSE# */
  if (!strcmpW(strIn, szFalse))
    *pBoolOut = VARIANT_FALSE;
  else if (!strcmpW(strIn, szTrue))
    *pBoolOut = VARIANT_TRUE;
  else
  {
    double d;

    /* If this string is a number, convert it as one */
    hRes = _VarR8FromStr(strIn, lcid, dwFlags, &d);
    if (SUCCEEDED(hRes))
      hRes = _VarBoolFromR8(d, pBoolOut);
  }
  return hRes;
}

/************************************************************************
 * VarBoolFromDisp (OLEAUT32.126)
 *
 * Convert a VT_DISPATCH to a VT_BOOL.
 *
 * PARAMS
 *  pdispIn   [I] Source
 *  lcid      [I] LCID for conversion
 *  pBoolOut  [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarBoolFromDisp(IDispatch* pdispIn, LCID lcid, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromDisp(pdispIn, lcid, pBoolOut);
}

/************************************************************************
 * VarBoolFromI1 (OLEAUT32.233)
 *
 * Convert a VT_I1 to a VT_BOOL.
 *
 * PARAMS
 *  cIn      [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromI1(signed char cIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromI1(cIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromUI2 (OLEAUT32.234)
 *
 * Convert a VT_UI2 to a VT_BOOL.
 *
 * PARAMS
 *  usIn     [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromUI2(USHORT usIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromUI2(usIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromUI4 (OLEAUT32.235)
 *
 * Convert a VT_UI4 to a VT_BOOL.
 *
 * PARAMS
 *  ulIn     [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromUI4(ULONG ulIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromUI4(ulIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromDec (OLEAUT32.236)
 *
 * Convert a VT_DECIMAL to a VT_BOOL.
 *
 * PARAMS
 *  pDecIn   [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pDecIn is invalid.
 */
HRESULT WINAPI VarBoolFromDec(DECIMAL* pDecIn, VARIANT_BOOL *pBoolOut)
{
  if (DEC_SCALE(pDecIn) > DEC_MAX_SCALE || (DEC_SIGN(pDecIn) & ~DECIMAL_NEG))
    return E_INVALIDARG;

  if (DEC_HI32(pDecIn) || DEC_MID32(pDecIn) || DEC_LO32(pDecIn))
    *pBoolOut = VARIANT_TRUE;
  else
    *pBoolOut = VARIANT_FALSE;
  return S_OK;
}

/************************************************************************
 * VarBoolFromI8 (OLEAUT32.370)
 *
 * Convert a VT_I8 to a VT_BOOL.
 *
 * PARAMS
 *  ullIn    [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromI8(LONG64 llIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromI8(llIn, pBoolOut);
}

/************************************************************************
 * VarBoolFromUI8 (OLEAUT32.371)
 *
 * Convert a VT_UI8 to a VT_BOOL.
 *
 * PARAMS
 *  ullIn    [I] Source
 *  pBoolOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarBoolFromUI8(ULONG64 ullIn, VARIANT_BOOL *pBoolOut)
{
  return _VarBoolFromUI8(ullIn, pBoolOut);
}

/* BSTR
 */

/* Write a number from a UI8 and sign */
static WCHAR *VARIANT_WriteNumber(ULONG64 ulVal, WCHAR* szOut)
{
  do
  {
    WCHAR ulNextDigit = ulVal % 10;

    *szOut-- = '0' + ulNextDigit;
    ulVal = (ulVal - ulNextDigit) / 10;
  } while (ulVal);

  szOut++;
  return szOut;
}

/* Create a (possibly localised) BSTR from a UI8 and sign */
static BSTR VARIANT_MakeBstr(LCID lcid, DWORD dwFlags, WCHAR *szOut)
{
  WCHAR szConverted[256];

  if (dwFlags & VAR_NEGATIVE)
    *--szOut = '-';

  if (dwFlags & LOCALE_USE_NLS)
  {
    /* Format the number for the locale */
    szConverted[0] = '\0';
    GetNumberFormatW(lcid,
                     dwFlags & LOCALE_NOUSEROVERRIDE,
                     szOut, NULL, szConverted, sizeof(szConverted)/sizeof(WCHAR));
    szOut = szConverted;
  }
  return SysAllocStringByteLen((LPCSTR)szOut, strlenW(szOut) * sizeof(WCHAR));
}

/* Create a (possibly localised) BSTR from a UI8 and sign */
static HRESULT VARIANT_BstrFromUInt(ULONG64 ulVal, LCID lcid, DWORD dwFlags, BSTR *pbstrOut)
{
  WCHAR szBuff[64], *szOut = szBuff + sizeof(szBuff)/sizeof(WCHAR) - 1;

  if (!pbstrOut)
    return E_INVALIDARG;

  /* Create the basic number string */
  *szOut-- = '\0';
  szOut = VARIANT_WriteNumber(ulVal, szOut);

  *pbstrOut = VARIANT_MakeBstr(lcid, dwFlags, szOut);
  TRACE("returning %s\n", debugstr_w(*pbstrOut));
  return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}

/******************************************************************************
 * VarBstrFromUI1 (OLEAUT32.108)
 *
 * Convert a VT_UI1 to a VT_BSTR.
 *
 * PARAMS
 *  bIn      [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromUI1(BYTE bIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  return VARIANT_BstrFromUInt(bIn, lcid, dwFlags, pbstrOut);
}

/******************************************************************************
 * VarBstrFromI2 (OLEAUT32.109)
 *
 * Convert a VT_I2 to a VT_BSTR.
 *
 * PARAMS
 *  sIn      [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromI2(short sIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  ULONG64 ul64 = sIn;

  if (sIn < 0)
  {
    ul64 = -sIn;
    dwFlags |= VAR_NEGATIVE;
  }
  return VARIANT_BstrFromUInt(ul64, lcid, dwFlags, pbstrOut);
}

/******************************************************************************
 * VarBstrFromI4 (OLEAUT32.110)
 *
 * Convert a VT_I4 to a VT_BSTR.
 *
 * PARAMS
 *  lIn      [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromI4(LONG lIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  ULONG64 ul64 = lIn;

  if (lIn < 0)
  {
    ul64 = (ULONG)-lIn;
    dwFlags |= VAR_NEGATIVE;
  }
  return VARIANT_BstrFromUInt(ul64, lcid, dwFlags, pbstrOut);
}

static HRESULT VARIANT_BstrFromReal(DOUBLE dblIn, LCID lcid, ULONG dwFlags,
                                    BSTR* pbstrOut, LPCWSTR lpszFormat)
{
  WCHAR buff[256];

  if (!pbstrOut)
    return E_INVALIDARG;

  sprintfW( buff, lpszFormat, dblIn );
  TRACE("created string %s\n", debugstr_w(buff));
  if (dwFlags & LOCALE_USE_NLS)
  {
    WCHAR numbuff[256];

    /* Format the number for the locale */
    numbuff[0] = '\0';
    GetNumberFormatW(lcid, dwFlags & LOCALE_NOUSEROVERRIDE,
                     buff, NULL, numbuff, sizeof(numbuff) / sizeof(WCHAR));
    TRACE("created NLS string %s\n", debugstr_w(numbuff));
    *pbstrOut = SysAllocString(numbuff);
  }
  else
    *pbstrOut = SysAllocString(buff);
  return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}

/******************************************************************************
 * VarBstrFromR4 (OLEAUT32.111)
 *
 * Convert a VT_R4 to a VT_BSTR.
 *
 * PARAMS
 *  fltIn    [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromR4(FLOAT fltIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  return VARIANT_BstrFromReal(fltIn, lcid, dwFlags, pbstrOut, szFloatFormatW);
}

/******************************************************************************
 * VarBstrFromR8 (OLEAUT32.112)
 *
 * Convert a VT_R8 to a VT_BSTR.
 *
 * PARAMS
 *  dblIn    [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromR8(double dblIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  return VARIANT_BstrFromReal(dblIn, lcid, dwFlags, pbstrOut, szDoubleFormatW);
}

/******************************************************************************
 *    VarBstrFromCy   [OLEAUT32.113]
 *
 * Convert a VT_CY to a VT_BSTR.
 *
 * PARAMS
 *  cyIn     [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromCy(CY cyIn, LCID lcid, ULONG dwFlags, BSTR *pbstrOut)
{
  WCHAR buff[256];
  double dblVal;

  if (!pbstrOut)
    return E_INVALIDARG;

  VarR8FromCy(cyIn, &dblVal);
  sprintfW(buff, szDoubleFormatW, dblVal);

  if (dwFlags & LOCALE_USE_NLS)
  {
    WCHAR cybuff[256];

    /* Format the currency for the locale */
    cybuff[0] = '\0';
    GetCurrencyFormatW(lcid, dwFlags & LOCALE_NOUSEROVERRIDE,
                       buff, NULL, cybuff, sizeof(cybuff) / sizeof(WCHAR));
    *pbstrOut = SysAllocString(cybuff);
  }
  else
    *pbstrOut = SysAllocString(buff);

  return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}

/******************************************************************************
 *    VarBstrFromDate    [OLEAUT32.114]
 *
 * Convert a VT_DATE to a VT_BSTR.
 *
 * PARAMS
 *  dateIn   [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut or dateIn is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromDate(DATE dateIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  SYSTEMTIME st;
  DWORD dwFormatFlags = dwFlags & LOCALE_NOUSEROVERRIDE;
  WCHAR date[128], *time;

  TRACE("(%g,0x%08lx,0x%08lx,%p)\n", dateIn, lcid, dwFlags, pbstrOut);

  if (!pbstrOut || !VariantTimeToSystemTime(dateIn, &st))
    return E_INVALIDARG;

  *pbstrOut = NULL;

  if (dwFlags & VAR_CALENDAR_THAI)
      st.wYear += 553; /* Use the Thai buddhist calendar year */
  else if (dwFlags & (VAR_CALENDAR_HIJRI|VAR_CALENDAR_GREGORIAN))
      FIXME("VAR_CALENDAR_HIJRI/VAR_CALENDAR_GREGORIAN not handled\n");

  if (dwFlags & LOCALE_USE_NLS)
    dwFlags &= ~(VAR_TIMEVALUEONLY|VAR_DATEVALUEONLY);
  else
  {
    double whole = dateIn < 0 ? ceil(dateIn) : floor(dateIn);
    double partial = dateIn - whole;

    if (whole == 0.0)
      dwFlags |= VAR_TIMEVALUEONLY;
    else if (partial < 1e-12)
      dwFlags |= VAR_DATEVALUEONLY;
  }

  if (dwFlags & VAR_TIMEVALUEONLY)
    date[0] = '\0';
  else
    if (!GetDateFormatW(lcid, dwFormatFlags|DATE_SHORTDATE, &st, NULL, date,
                        sizeof(date)/sizeof(WCHAR)))
      return E_INVALIDARG;

  if (!(dwFlags & VAR_DATEVALUEONLY))
  {
    time = date + strlenW(date);
    if (time != date)
      *time++ = ' ';
    if (!GetTimeFormatW(lcid, dwFormatFlags, &st, NULL, time,
                        sizeof(date)/sizeof(WCHAR)-(time-date)))
      return E_INVALIDARG;
  }

  *pbstrOut = SysAllocString(date);
  if (*pbstrOut)
    TRACE("returning %s\n", debugstr_w(*pbstrOut));
  return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}

/******************************************************************************
 * VarBstrFromBool (OLEAUT32.116)
 *
 * Convert a VT_BOOL to a VT_BSTR.
 *
 * PARAMS
 *  boolIn   [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 *
 * NOTES
 *  If dwFlags includes VARIANT_LOCALBOOL, this function converts to the
 *  localised text of "True" or "False". To convert a bool into a
 *  numeric string of "0" or "-1", use VariantChangeTypeTypeEx().
 */
HRESULT WINAPI VarBstrFromBool(VARIANT_BOOL boolIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  WCHAR szBuff[64];
  DWORD dwResId = IDS_TRUE;
  LANGID langId;

  TRACE("%d,0x%08lx,0x%08lx,%p\n", boolIn, lcid, dwFlags, pbstrOut);

  if (!pbstrOut)
    return E_INVALIDARG;

  /* VAR_BOOLONOFF and VAR_BOOLYESNO are internal flags used
   * for variant formatting */
  switch (dwFlags & (VAR_LOCALBOOL|VAR_BOOLONOFF|VAR_BOOLYESNO))
  {
  case VAR_BOOLONOFF:
      dwResId = IDS_ON;
      break;
  case VAR_BOOLYESNO:
      dwResId = IDS_YES;
      break;
  case VAR_LOCALBOOL:
      break;
  default:
    lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),SORT_DEFAULT);
  }

  lcid = ConvertDefaultLocale(lcid);
  langId = LANGIDFROMLCID(lcid);
  if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
    langId = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);

  if (boolIn == VARIANT_FALSE)
    dwResId++; /* Use negative form */

VarBstrFromBool_GetLocalised:
  if (VARIANT_GetLocalisedText(langId, dwResId, szBuff))
  {
    *pbstrOut = SysAllocString(szBuff);
    return *pbstrOut ? S_OK : E_OUTOFMEMORY;
  }

  if (langId != MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT))
  {
    langId = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
    goto VarBstrFromBool_GetLocalised;
  }

  /* Should never get here */
  WARN("Failed to load bool text!\n");
  return E_OUTOFMEMORY;
}

/******************************************************************************
 * VarBstrFromI1 (OLEAUT32.229)
 *
 * Convert a VT_I1 to a VT_BSTR.
 *
 * PARAMS
 *  cIn      [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromI1(signed char cIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  ULONG64 ul64 = cIn;

  if (cIn < 0)
  {
    ul64 = -cIn;
    dwFlags |= VAR_NEGATIVE;
  }
  return VARIANT_BstrFromUInt(ul64, lcid, dwFlags, pbstrOut);
}

/******************************************************************************
 * VarBstrFromUI2 (OLEAUT32.230)
 *
 * Convert a VT_UI2 to a VT_BSTR.
 *
 * PARAMS
 *  usIn     [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromUI2(USHORT usIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  return VARIANT_BstrFromUInt(usIn, lcid, dwFlags, pbstrOut);
}

/******************************************************************************
 * VarBstrFromUI4 (OLEAUT32.231)
 *
 * Convert a VT_UI4 to a VT_BSTR.
 *
 * PARAMS
 *  ulIn     [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromUI4(ULONG ulIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  return VARIANT_BstrFromUInt(ulIn, lcid, dwFlags, pbstrOut);
}

/******************************************************************************
 * VarBstrFromDec (OLEAUT32.232)
 *
 * Convert a VT_DECIMAL to a VT_BSTR.
 *
 * PARAMS
 *  pDecIn   [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromDec(DECIMAL* pDecIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  if (!pbstrOut)
    return E_INVALIDARG;

  if (!DEC_SCALE(pDecIn) && !DEC_HI32(pDecIn))
  {
    WCHAR szBuff[256], *szOut = szBuff + sizeof(szBuff)/sizeof(WCHAR) - 1;

    /* Create the basic number string */
    *szOut-- = '\0';
    szOut = VARIANT_WriteNumber(DEC_LO64(pDecIn), szOut);
    if (DEC_SIGN(pDecIn))
      dwFlags |= VAR_NEGATIVE;

    *pbstrOut = VARIANT_MakeBstr(lcid, dwFlags, szOut);
    TRACE("returning %s\n", debugstr_w(*pbstrOut));
    return *pbstrOut ? S_OK : E_OUTOFMEMORY;
  }
  FIXME("semi-stub\n");
  return E_INVALIDARG;
}

/************************************************************************
 * VarBstrFromI8 (OLEAUT32.370)
 *
 * Convert a VT_I8 to a VT_BSTR.
 *
 * PARAMS
 *  llIn     [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromI8(LONG64 llIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  ULONG64 ul64 = llIn;

  if (llIn < 0)
  {
    ul64 = -llIn;
    dwFlags |= VAR_NEGATIVE;
  }
  return VARIANT_BstrFromUInt(ul64, lcid, dwFlags, pbstrOut);
}

/************************************************************************
 * VarBstrFromUI8 (OLEAUT32.371)
 *
 * Convert a VT_UI8 to a VT_BSTR.
 *
 * PARAMS
 *  ullIn    [I] Source
 *  lcid     [I] LCID for the conversion
 *  dwFlags  [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
 *  pbstrOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrFromUI8(ULONG64 ullIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
  return VARIANT_BstrFromUInt(ullIn, lcid, dwFlags, pbstrOut);
}

/**********************************************************************
 * VarBstrCat (OLEAUT32.313)
 *
 * Concatenate two BSTR values.
 *
 * PARAMS
 *  pbstrLeft  [I] Source
 *  pbstrRight [I] Value to concatenate
 *  pbstrOut   [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if pbstrOut is invalid.
 *           E_OUTOFMEMORY, if memory allocation fails.
 */
HRESULT WINAPI VarBstrCat(BSTR pbstrLeft, BSTR pbstrRight, BSTR *pbstrOut)
{
  unsigned int len;

  if (!pbstrOut)
    return E_INVALIDARG;

  len = pbstrLeft ? strlenW(pbstrLeft) : 0;
  if (pbstrRight)
    len += strlenW(pbstrRight);

  *pbstrOut = SysAllocStringLen(NULL, len);
  if (!*pbstrOut)
    return E_OUTOFMEMORY;

  (*pbstrOut)[0] = '\0';

  if (pbstrLeft)
    strcpyW(*pbstrOut, pbstrLeft);

  if (pbstrRight)
    strcatW(*pbstrOut, pbstrRight);

  return S_OK;
}

/**********************************************************************
 * VarBstrCmp (OLEAUT32.314)
 *
 * Compare two BSTR values.
 *
 * PARAMS
 *  pbstrLeft  [I] Source
 *  pbstrRight [I] Value to compare
 *  lcid       [I] LCID for the comparison
 *  dwFlags    [I] Flags to pass directly to CompareStringW().
 *
 * RETURNS
 *  VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that pbstrLeft is less
 *  than, equal to or greater than pbstrRight respectively.
 *  VARCMP_NULL is returned if either string is NULL, unless both are NULL
 *  in which case VARCMP_EQ is returned.
 */
HRESULT WINAPI VarBstrCmp(BSTR pbstrLeft, BSTR pbstrRight, LCID lcid, DWORD dwFlags)
{
    if (!pbstrLeft)
    {
      if (!pbstrRight || !*pbstrRight)
        return VARCMP_EQ;
      return VARCMP_NULL;
    }
    else if (!pbstrRight)
    {
      if (!*pbstrLeft)
        return VARCMP_EQ;
      return VARCMP_NULL;
    }

    return CompareStringW(lcid, dwFlags, pbstrLeft, -1, pbstrRight, -1) - 1;
}

/*
 * DATE
 */

/******************************************************************************
 * VarDateFromUI1 (OLEAUT32.88)
 *
 * Convert a VT_UI1 to a VT_DATE.
 *
 * PARAMS
 *  bIn      [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromUI1(BYTE bIn, DATE* pdateOut)
{
  return _VarDateFromUI1(bIn, pdateOut);
}

/******************************************************************************
 * VarDateFromI2 (OLEAUT32.89)
 *
 * Convert a VT_I2 to a VT_DATE.
 *
 * PARAMS
 *  sIn      [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromI2(short sIn, DATE* pdateOut)
{
  return _VarDateFromI2(sIn, pdateOut);
}

/******************************************************************************
 * VarDateFromI4 (OLEAUT32.90)
 *
 * Convert a VT_I4 to a VT_DATE.
 *
 * PARAMS
 *  lIn      [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromI4(LONG lIn, DATE* pdateOut)
{
  return _VarDateFromI4(lIn, pdateOut);
}

/******************************************************************************
 * VarDateFromR4 (OLEAUT32.91)
 *
 * Convert a VT_R4 to a VT_DATE.
 *
 * PARAMS
 *  fltIn    [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromR4(FLOAT fltIn, DATE* pdateOut)
{
  return _VarDateFromR4(fltIn, pdateOut);
}

/******************************************************************************
 * VarDateFromR8 (OLEAUT32.92)
 *
 * Convert a VT_R8 to a VT_DATE.
 *
 * PARAMS
 *  dblIn    [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromR8(double dblIn, DATE* pdateOut)
{
  return _VarDateFromR8(dblIn, pdateOut);
}

/**********************************************************************
 * VarDateFromDisp (OLEAUT32.95)
 *
 * Convert a VT_DISPATCH to a VT_DATE.
 *
 * PARAMS
 *  pdispIn  [I] Source
 *  lcid     [I] LCID for conversion
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: E_INVALIDARG, if the source value is invalid
 *           DISP_E_OVERFLOW, if the value will not fit in the destination
 *           DISP_E_TYPEMISMATCH, if the type cannot be converted
 */
HRESULT WINAPI VarDateFromDisp(IDispatch* pdispIn, LCID lcid, DATE* pdateOut)
{
  return _VarDateFromDisp(pdispIn, lcid, pdateOut);
}

/******************************************************************************
 * VarDateFromBool (OLEAUT32.96)
 *
 * Convert a VT_BOOL to a VT_DATE.
 *
 * PARAMS
 *  boolIn   [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromBool(VARIANT_BOOL boolIn, DATE* pdateOut)
{
  return _VarDateFromBool(boolIn, pdateOut);
}

/**********************************************************************
 * VarDateFromCy (OLEAUT32.93)
 *
 * Convert a VT_CY to a VT_DATE.
 *
 * PARAMS
 *  lIn      [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromCy(CY cyIn, DATE* pdateOut)
{
  return _VarDateFromCy(cyIn, pdateOut);
}

/* Date string parsing */
#define DP_TIMESEP 0x01 /* Time separator ( _must_ remain 0x1, used as a bitmask) */
#define DP_DATESEP 0x02 /* Date separator */
#define DP_MONTH   0x04 /* Month name */
#define DP_AM      0x08 /* AM */
#define DP_PM      0x10 /* PM */

typedef struct tagDATEPARSE
{
    DWORD dwCount;      /* Number of fields found so far (maximum 6) */
    DWORD dwParseFlags; /* Global parse flags (DP_ Flags above) */
    DWORD dwFlags[6];   /* Flags for each field */
    DWORD dwValues[6];  /* Value of each field */
} DATEPARSE;

#define TIMEFLAG(i) ((dp.dwFlags[i] & DP_TIMESEP) << i)

#define IsLeapYear(y) (((y % 4) == 0) && (((y % 100) != 0) || ((y % 400) == 0)))

/* Determine if a day is valid in a given month of a given year */
static BOOL VARIANT_IsValidMonthDay(DWORD day, DWORD month, DWORD year)
{
  static const BYTE days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

  if (day && month && month < 13)
  {
    if (day <= days[month] || (month == 2 && day == 29 && IsLeapYear(year)))
      return TRUE;
  }
  return FALSE;
}

/* Possible orders for 3 numbers making up a date */
#define ORDER_MDY 0x01
#define ORDER_YMD 0x02
#define ORDER_YDM 0x04
#define ORDER_DMY 0x08
#define ORDER_MYD 0x10 /* Synthetic order, used only for funky 2 digit dates */

/* Determine a date for a particular locale, from 3 numbers */
static inline HRESULT VARIANT_MakeDate(DATEPARSE *dp, DWORD iDate,
                                       DWORD offset, SYSTEMTIME *st)
{
  DWORD dwAllOrders, dwTry, dwCount = 0, v1, v2, v3;

  if (!dp->dwCount)
  {
    v1 = 30; /* Default to (Variant) 0 date part */
    v2 = 12;
    v3 = 1899;
    goto VARIANT_MakeDate_OK;
  }

  v1 = dp->dwValues[offset + 0];
  v2 = dp->dwValues[offset + 1];
  if (dp->dwCount == 2)
  {
    SYSTEMTIME current;
    GetSystemTime(&current);
    v3 = current.wYear;
  }
  else
    v3 = dp->dwValues[offset + 2];

  TRACE("(%ld,%ld,%ld,%ld,%ld)\n", v1, v2, v3, iDate, offset);

  /* If one number must be a month (Because a month name was given), then only
   * consider orders with the month in that position.
   * If we took the current year as 'v3', then only allow a year in that position.
   */
  if (dp->dwFlags[offset + 0] & DP_MONTH)
  {
    dwAllOrders = ORDER_MDY;
  }
  else if (dp->dwFlags[offset + 1] & DP_MONTH)
  {
    dwAllOrders = ORDER_DMY;
    if (dp->dwCount > 2)
      dwAllOrders |= ORDER_YMD;
  }
  else if (dp->dwCount > 2 && dp->dwFlags[offset + 2] & DP_MONTH)
  {
    dwAllOrders = ORDER_YDM;
  }
  else
  {
    dwAllOrders = ORDER_MDY|ORDER_DMY;
    if (dp->dwCount > 2)
      dwAllOrders |= (ORDER_YMD|ORDER_YDM);
  }

VARIANT_MakeDate_Start:
  TRACE("dwAllOrders is 0x%08lx\n", dwAllOrders);

  while (dwAllOrders)
  {
    DWORD dwTemp;

    if (dwCount == 0)
    {
      /* First: Try the order given by iDate */
      switch (iDate)
      {
      case 0:  dwTry = dwAllOrders & ORDER_MDY; break;
      case 1:  dwTry = dwAllOrders & ORDER_DMY; break;
      default: dwTry = dwAllOrders & ORDER_YMD; break;
      }
    }
    else if (dwCount == 1)
    {
      /* Second: Try all the orders compatible with iDate */
      switch (iDate)
      {
      case 0:  dwTry = dwAllOrders & ~(ORDER_DMY|ORDER_YDM); break;
      case 1:  dwTry = dwAllOrders & ~(ORDER_MDY|ORDER_YMD|ORDER_MYD); break;
      default: dwTry = dwAllOrders & ~(ORDER_DMY|ORDER_YDM); break;
      }
    }
    else
    {
      /* Finally: Try any remaining orders */
      dwTry = dwAllOrders;
    }

    TRACE("Attempt %ld, dwTry is 0x%08lx\n", dwCount, dwTry);

    dwCount++;
    if (!dwTry)
      continue;

#define DATE_SWAP(x,y) do { dwTemp = x; x = y; y = dwTemp; } while (0)

    if (dwTry & ORDER_MDY)
    {
      if (VARIANT_IsValidMonthDay(v2,v1,v3))
      {
        DATE_SWAP(v1,v2);
        goto VARIANT_MakeDate_OK;
      }
      dwAllOrders &= ~ORDER_MDY;
    }
    if (dwTry & ORDER_YMD)
    {
      if (VARIANT_IsValidMonthDay(v3,v2,v1))
      {
        DATE_SWAP(v1,v3);
        goto VARIANT_MakeDate_OK;
      }
      dwAllOrders &= ~ORDER_YMD;
    }
    if (dwTry & ORDER_YDM)
    {
      if (VARIANT_IsValidMonthDay(v2,v3,v1))
      {
        DATE_SWAP(v1,v2);
        DATE_SWAP(v2,v3);
        goto VARIANT_MakeDate_OK;
      }
      dwAllOrders &= ~ORDER_YDM;
    }
    if (dwTry & ORDER_DMY)
    {
      if (VARIANT_IsValidMonthDay(v1,v2,v3))
        goto VARIANT_MakeDate_OK;
      dwAllOrders &= ~ORDER_DMY;
    }
    if (dwTry & ORDER_MYD)
    {
      /* Only occurs if we are trying a 2 year date as M/Y not D/M */
      if (VARIANT_IsValidMonthDay(v3,v1,v2))
      {
        DATE_SWAP(v1,v3);
        DATE_SWAP(v2,v3);
        goto VARIANT_MakeDate_OK;
      }
      dwAllOrders &= ~ORDER_MYD;
    }
  }

  if (dp->dwCount == 2)
  {
    /* We couldn't make a date as D/M or M/D, so try M/Y or Y/M */
    v3 = 1; /* 1st of the month */
    dwAllOrders = ORDER_YMD|ORDER_MYD;
    dp->dwCount = 0; /* Don't return to this code path again */
    dwCount = 0;
    goto VARIANT_MakeDate_Start;
  }

  /* No valid dates were able to be constructed */
  return DISP_E_TYPEMISMATCH;

VARIANT_MakeDate_OK:

  /* Check that the time part is ok */
  if (st->wHour > 23 || st->wMinute > 59 || st->wSecond > 59)
    return DISP_E_TYPEMISMATCH;

  TRACE("Time %d %d %d\n", st->wHour, st->wMinute, st->wSecond);
  if (st->wHour < 12 && (dp->dwParseFlags & DP_PM))
    st->wHour += 12;
  else if (st->wHour == 12 && (dp->dwParseFlags & DP_AM))
    st->wHour = 0;
  TRACE("Time %d %d %d\n", st->wHour, st->wMinute, st->wSecond);

  st->wDay = v1;
  st->wMonth = v2;
  /* FIXME: For 2 digit dates, I'm not sure if 30 is hard coded or not. It may
   * be retrieved from:
   * HKCU\Control Panel\International\Calendars\TwoDigitYearMax
   * But Wine doesn't have/use that key as at the time of writing.
   */
  st->wYear = v3 < 30 ? 2000 + v3 : v3 < 100 ? 1900 + v3 : v3;
  TRACE("Returning date %ld/%ld/%d\n", v1, v2, st->wYear);
  return S_OK;
}

/******************************************************************************
 * VarDateFromStr [OLEAUT32.94]
 *
 * Convert a VT_BSTR to at VT_DATE.
 *
 * PARAMS
 *  strIn    [I] String to convert
 *  lcid     [I] Locale identifier for the conversion
 *  dwFlags  [I] Flags affecting the conversion (VAR_ flags from "oleauto.h")
 *  pdateOut [O] Destination for the converted value
 *
 * RETURNS
 *  Success: S_OK. pdateOut contains the converted value.
 *  FAILURE: An HRESULT error code indicating the prolem.
 *
 * NOTES
 *  Any date format that can be created using the date formats from lcid
 *  (Either from kernel Nls functions, variant conversion or formatting) is a
 *  valid input to this function. In addition, a few more esoteric formats are
 *  also supported for compatibility with the native version. The date is
 *  interpreted according to the date settings in the control panel, unless
 *  the date is invalid in that format, in which the most compatible format
 *  that produces a valid date will be used.
 */
HRESULT WINAPI VarDateFromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, DATE* pdateOut)
{
  static const USHORT ParseDateTokens[] =
  {
    LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4,
    LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8,
    LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
    LOCALE_SMONTHNAME13,
    LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
    LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
    LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
    LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
    LOCALE_SABBREVMONTHNAME13,
    LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3, LOCALE_SDAYNAME4,
    LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
    LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
    LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
    LOCALE_SABBREVDAYNAME7,
    LOCALE_S1159, LOCALE_S2359
  };
  static const BYTE ParseDateMonths[] =
  {
    1,2,3,4,5,6,7,8,9,10,11,12,13,
    1,2,3,4,5,6,7,8,9,10,11,12,13
  };
  size_t i;
  BSTR tokens[sizeof(ParseDateTokens)/sizeof(ParseDateTokens[0])];
  DATEPARSE dp;
  DWORD dwDateSeps = 0, iDate = 0;
  HRESULT hRet = S_OK;

  if ((dwFlags & (VAR_TIMEVALUEONLY|VAR_DATEVALUEONLY)) ==
      (VAR_TIMEVALUEONLY|VAR_DATEVALUEONLY))
    return E_INVALIDARG;

  if (!strIn)
    return DISP_E_TYPEMISMATCH;

  *pdateOut = 0.0;

  TRACE("(%s,0x%08lx,0x%08lx,%p)\n", debugstr_w(strIn), lcid, dwFlags, pdateOut);

  memset(&dp, 0, sizeof(dp));

  GetLocaleInfoW(lcid, LOCALE_IDATE|LOCALE_RETURN_NUMBER|(dwFlags & LOCALE_NOUSEROVERRIDE),
                 (LPWSTR)&iDate, sizeof(iDate)/sizeof(WCHAR));
  TRACE("iDate is %ld\n", iDate);

  /* Get the month/day/am/pm tokens for this locale */
  for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
  {
    WCHAR buff[128];
    LCTYPE lctype =  ParseDateTokens[i] | (dwFlags & LOCALE_NOUSEROVERRIDE);

    /* FIXME: Alternate calendars - should use GetCalendarInfo() and/or
     *        GetAltMonthNames(). We should really cache these strings too.
     */
    buff[0] = '\0';
    GetLocaleInfoW(lcid, lctype, buff, sizeof(buff)/sizeof(WCHAR));
    tokens[i] = SysAllocString(buff);
    TRACE("token %d is %s\n", i, debugstr_w(tokens[i]));
  }

  /* Parse the string into our structure */
  while (*strIn)
  {
    if (dp.dwCount > 6)
      break;

    if (isdigitW(*strIn))
    {
      dp.dwValues[dp.dwCount] = strtoulW(strIn, &strIn, 10);
      dp.dwCount++;
      strIn--;
    }
    else if (isalpha(*strIn))
    {
      BOOL bFound = FALSE;

      for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
      {
        DWORD dwLen = strlenW(tokens[i]);
        if (dwLen && !strncmpiW(strIn, tokens[i], dwLen))
        {
          if (i <= 25)
          {
            dp.dwValues[dp.dwCount] = ParseDateMonths[i];
            dp.dwFlags[dp.dwCount] |= (DP_MONTH|DP_DATESEP);
            dp.dwCount++;
          }
          else if (i > 39)
          {
            if (!dp.dwCount || dp.dwParseFlags & (DP_AM|DP_PM))
              hRet = DISP_E_TYPEMISMATCH;
            else
            {
              dp.dwFlags[dp.dwCount - 1] |= (i == 40 ? DP_AM : DP_PM);
              dp.dwParseFlags |= (i == 40 ? DP_AM : DP_PM);
            }
          }
          strIn += (dwLen - 1);
          bFound = TRUE;
          break;
        }
      }

      if (!bFound)
      {
        if ((*strIn == 'a' || *strIn == 'A' || *strIn == 'p' || *strIn == 'P') &&
            (dp.dwCount && !(dp.dwParseFlags & (DP_AM|DP_PM))))
        {
          /* Special case - 'a' and 'p' are recognised as short for am/pm */
          if (*strIn == 'a' || *strIn == 'A')
          {
            dp.dwFlags[dp.dwCount - 1] |= DP_AM;
            dp.dwParseFlags |=  DP_AM;
          }
          else
          {
            dp.dwFlags[dp.dwCount - 1] |= DP_PM;
            dp.dwParseFlags |=  DP_PM;
          }
          strIn++;
        }
        else
        {
          TRACE("No matching token for %s\n", debugstr_w(strIn));
          hRet = DISP_E_TYPEMISMATCH;
          break;
        }
      }
    }
    else if (*strIn == ':' ||  *strIn == '.')
    {
      if (!dp.dwCount || !strIn[1])
        hRet = DISP_E_TYPEMISMATCH;
      else
        dp.dwFlags[dp.dwCount - 1] |= DP_TIMESEP;
    }
    else if (*strIn == '-' || *strIn == '/')
    {
      dwDateSeps++;
      if (dwDateSeps > 2 || !dp.dwCount || !strIn[1])
        hRet = DISP_E_TYPEMISMATCH;
      else
        dp.dwFlags[dp.dwCount - 1] |= DP_DATESEP;
    }
    else if (*strIn == ',' || isspaceW(*strIn))
    {
      if (*strIn == ',' && !strIn[1])
        hRet = DISP_E_TYPEMISMATCH;
    }
    else
    {
      hRet = DISP_E_TYPEMISMATCH;
    }
    strIn++;
  }

  if (!dp.dwCount || dp.dwCount > 6 ||
      (dp.dwCount == 1 && !(dp.dwParseFlags & (DP_AM|DP_PM))))
    hRet = DISP_E_TYPEMISMATCH;

  if (SUCCEEDED(hRet))
  {
    SYSTEMTIME st;
    DWORD dwOffset = 0; /* Start of date fields in dp.dwValues */

    st.wDayOfWeek = st.wHour = st.wMinute = st.wSecond = st.wMilliseconds = 0;

    /* Figure out which numbers correspond to which fields.
     *
     * This switch statement works based on the fact that native interprets any
     * fields that are not joined with a time separator ('.' or ':') as date
     * fields. Thus we construct a value from 0-32 where each set bit indicates
     * a time field. This encapsulates the hundreds of permutations of 2-6 fields.
     * For valid permutations, we set dwOffset to point to the first date field
     * and shorten dp.dwCount by the number of time fields found. The real
     * magic here occurs in VARIANT_MakeDate() above, where we determine what
     * each date number must represent in the context of iDate.
     */
    TRACE("0x%08lx\n", TIMEFLAG(0)|TIMEFLAG(1)|TIMEFLAG(2)|TIMEFLAG(3)|TIMEFLAG(4));

    switch (TIMEFLAG(0)|TIMEFLAG(1)|TIMEFLAG(2)|TIMEFLAG(3)|TIMEFLAG(4))
    {
    case 0x1: /* TT TTDD TTDDD */
      if (dp.dwCount > 3 &&
          ((dp.dwFlags[2] & (DP_AM|DP_PM)) || (dp.dwFlags[3] & (DP_AM|DP_PM)) ||
          (dp.dwFlags[4] & (DP_AM|DP_PM))))
        hRet = DISP_E_TYPEMISMATCH;
      else if (dp.dwCount != 2 && dp.dwCount != 4 && dp.dwCount != 5)
        hRet = DISP_E_TYPEMISMATCH;
      st.wHour = dp.dwValues[0];
      st.wMinute  = dp.dwValues[1];
      dp.dwCount -= 2;
      dwOffset = 2;
      break;

    case 0x3: /* TTT TTTDD TTTDDD */
      if (dp.dwCount > 4 &&
          ((dp.dwFlags[3] & (DP_AM|DP_PM)) || (dp.dwFlags[4] & (DP_AM|DP_PM)) ||
          (dp.dwFlags[5] & (DP_AM|DP_PM))))
        hRet = DISP_E_TYPEMISMATCH;
      else if (dp.dwCount != 3 && dp.dwCount != 5 && dp.dwCount != 6)
        hRet = DISP_E_TYPEMISMATCH;
      st.wHour   = dp.dwValues[0];
      st.wMinute = dp.dwValues[1];
      st.wSecond = dp.dwValues[2];
      dwOffset = 3;
      dp.dwCount -= 3;
      break;

    case 0x4: /* DDTT */
      if (dp.dwCount != 4 ||
          (dp.dwFlags[0] & (DP_AM|DP_PM)) || (dp.dwFlags[1] & (DP_AM|DP_PM)))
        hRet = DISP_E_TYPEMISMATCH;

      st.wHour = dp.dwValues[2];
      st.wMinute  = dp.dwValues[3];
      dp.dwCount -= 2;
      break;

   case 0x0: /* T DD DDD TDDD TDDD */
      if (dp.dwCount == 1 && (dp.dwParseFlags & (DP_AM|DP_PM)))
      {
        st.wHour = dp.dwValues[0]; /* T */
        dp.dwCount = 0;
        break;
      }
      else if (dp.dwCount > 4 || (dp.dwCount < 3 && dp.dwParseFlags & (DP_AM|DP_PM)))
      {
        hRet = DISP_E_TYPEMISMATCH;
      }
      else if (dp.dwCount == 3)
      {
        if (dp.dwFlags[0] & (DP_AM|DP_PM)) /* TDD */
        {
          dp.dwCount = 2;
          st.wHour = dp.dwValues[0];
          dwOffset = 1;
          break;
        }
        if (dp.dwFlags[2] & (DP_AM|DP_PM)) /* DDT */
        {
          dp.dwCount = 2;
          st.wHour = dp.dwValues[2];
          break;
        }
        else if (dp.dwParseFlags & (DP_AM|DP_PM))
          hRet = DISP_E_TYPEMISMATCH;
      }
      else if (dp.dwCount == 4)
      {
        dp.dwCount = 3;
        if (dp.dwFlags[0] & (DP_AM|DP_PM)) /* TDDD */
        {
          st.wHour = dp.dwValues[0];
          dwOffset = 1;
        }
        else if (dp.dwFlags[3] & (DP_AM|DP_PM)) /* DDDT */
        {
          st.wHour = dp.dwValues[3];
        }
        else
          hRet = DISP_E_TYPEMISMATCH;
        break;
      }
      /* .. fall through .. */

    case 0x8: /* DDDTT */
      if ((dp.dwCount == 2 && (dp.dwParseFlags & (DP_AM|DP_PM))) ||
          (dp.dwCount == 5 && ((dp.dwFlags[0] & (DP_AM|DP_PM)) ||
           (dp.dwFlags[1] & (DP_AM|DP_PM)) || (dp.dwFlags[2] & (DP_AM|DP_PM)))) ||
           dp.dwCount == 4 || dp.dwCount == 6)
        hRet = DISP_E_TYPEMISMATCH;
      st.wHour   = dp.dwValues[3];
      st.wMinute = dp.dwValues[4];
      if (dp.dwCount == 5)
        dp.dwCount -= 2;
      break;

    case 0xC: /* DDTTT */
      if (dp.dwCount != 5 ||
          (dp.dwFlags[0] & (DP_AM|DP_PM)) || (dp.dwFlags[1] & (DP_AM|DP_PM)))
        hRet = DISP_E_TYPEMISMATCH;
      st.wHour   = dp.dwValues[2];
      st.wMinute = dp.dwValues[3];
      st.wSecond = dp.dwValues[4];
      dp.dwCount -= 3;
      break;

    case 0x18: /* DDDTTT */
      if ((dp.dwFlags[0] & (DP_AM|DP_PM)) || (dp.dwFlags[1] & (DP_AM|DP_PM)) ||
          (dp.dwFlags[2] & (DP_AM|DP_PM)))
        hRet = DISP_E_TYPEMISMATCH;
      st.wHour   = dp.dwValues[3];
      st.wMinute = dp.dwValues[4];
      st.wSecond = dp.dwValues[5];
      dp.dwCount -= 3;
      break;

    default:
      hRet = DISP_E_TYPEMISMATCH;
      break;
    }

    if (SUCCEEDED(hRet))
    {
      hRet = VARIANT_MakeDate(&dp, iDate, dwOffset, &st);

      if (dwFlags & VAR_TIMEVALUEONLY)
      {
        st.wYear = 1899;
        st.wMonth = 12;
        st.wDay = 30;
      }
      else if (dwFlags & VAR_DATEVALUEONLY)
       st.wHour = st.wMinute = st.wSecond = 0;

      /* Finally, convert the value to a VT_DATE */
      if (SUCCEEDED(hRet))
        hRet = SystemTimeToVariantTime(&st, pdateOut) ? S_OK : DISP_E_TYPEMISMATCH;
    }
  }

  for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
    SysFreeString(tokens[i]);
  return hRet;
}

/******************************************************************************
 * VarDateFromI1 (OLEAUT32.221)
 *
 * Convert a VT_I1 to a VT_DATE.
 *
 * PARAMS
 *  cIn      [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromI1(signed char cIn, DATE* pdateOut)
{
  return _VarDateFromI1(cIn, pdateOut);
}

/******************************************************************************
 * VarDateFromUI2 (OLEAUT32.222)
 *
 * Convert a VT_UI2 to a VT_DATE.
 *
 * PARAMS
 *  uiIn     [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromUI2(USHORT uiIn, DATE* pdateOut)
{
  return _VarDateFromUI2(uiIn, pdateOut);
}

/******************************************************************************
 * VarDateFromUI4 (OLEAUT32.223)
 *
 * Convert a VT_UI4 to a VT_DATE.
 *
 * PARAMS
 *  ulIn     [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromUI4(ULONG ulIn, DATE* pdateOut)
{
  return _VarDateFromUI4(ulIn, pdateOut);
}

/**********************************************************************
 * VarDateFromDec (OLEAUT32.224)
 *
 * Convert a VT_DECIMAL to a VT_DATE.
 *
 * PARAMS
 *  pdecIn   [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  S_OK.
 */
HRESULT WINAPI VarDateFromDec(DECIMAL *pdecIn, DATE* pdateOut)
{
  return _VarDateFromDec(pdecIn, pdateOut);
}

/******************************************************************************
 * VarDateFromI8 (OLEAUT32.364)
 *
 * Convert a VT_I8 to a VT_DATE.
 *
 * PARAMS
 *  llIn     [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDateFromI8(LONG64 llIn, DATE* pdateOut)
{
  return _VarDateFromI8(llIn, pdateOut);
}

/******************************************************************************
 * VarDateFromUI8 (OLEAUT32.365)
 *
 * Convert a VT_UI8 to a VT_DATE.
 *
 * PARAMS
 *  ullIn    [I] Source
 *  pdateOut [O] Destination
 *
 * RETURNS
 *  Success: S_OK.
 *  Failure: DISP_E_OVERFLOW, if the value will not fit in the destination
 */
HRESULT WINAPI VarDateFromUI8(ULONG64 ullIn, DATE* pdateOut)
{
  return _VarDateFromUI8(ullIn, pdateOut);
}

Generated by  Doxygen 1.6.0   Back to index