//*********************************************************************
//*                  Microsoft Windows                               **
//*            Copyright(c) Microsoft Corp., 1999                    **
//*********************************************************************
//
//  PID.CPP - Header for the implementation of CSystemClock
//
//  HISTORY:
//
//  1/27/99 a-jaswed Created.
//

#include "sysclock.h"
#include "appdefs.h"
#include "dispids.h"
#include "msobmain.h"
#include "resource.h"
#include <stdlib.h>

int WINAPI StrToWideStr(LPWSTR pwsz, LPCSTR psz)
{
    return MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, MAX_PATH);
}

DISPATCHLIST SystemClockExternalInterface[] =
{
    { L"set_TimeZone",          DISPID_SYSTEMCLOCK_SETTIMEZONE          },
    { L"set_Time",              DISPID_SYSTEMCLOCK_SETTIME              },
    { L"set_Date",              DISPID_SYSTEMCLOCK_SETDATE              },

    // new oobe2 methods below, others are currently unused

    { L"Init",                  DISPID_SYSTEMCLOCK_INIT                 },
    { L"get_AllTimeZones",      DISPID_SYSTEMCLOCK_GETALLTIMEZONES      },
    { L"get_TimeZoneIdx",       DISPID_SYSTEMCLOCK_GETTIMEZONEIDX       },
    { L"set_TimeZoneIdx",       DISPID_SYSTEMCLOCK_SETTIMEZONEIDX       },
    { L"set_AutoDaylight",      DISPID_SYSTEMCLOCK_SETAUTODAYLIGHT      },
    { L"get_AutoDaylight",      DISPID_SYSTEMCLOCK_GETAUTODAYLIGHT      },
    { L"get_DaylightEnabled",   DISPID_SYSTEMCLOCK_GETDAYLIGHT_ENABLED  },
    { L"get_TimeZonewasPreset", DISPID_SYSTEMCLOCK_GETTIMEZONEWASPRESET }
};

/////////////////////////////////////////////////////////////
// CSystemClock::CSystemClock
CSystemClock::CSystemClock(HINSTANCE hInstance)
{

    // Init member vars
    m_cRef                  = 0;
    m_cNumTimeZones         = 0;
    m_pTimeZoneArr          = NULL;
    m_szTimeZoneOptionStrs  = NULL;
    m_uCurTimeZoneIdx       = 0;
    m_bSetAutoDaylightMode  = TRUE;  // on by default
    m_bTimeZonePreset       = FALSE;
    m_hInstance=hInstance;
}

/////////////////////////////////////////////////////////////
// CSystemClock::~CSystemClock
CSystemClock::~CSystemClock()
{
   MYASSERT(m_cRef == 0);

   if ( m_pTimeZoneArr )
       HeapFree(GetProcessHeap(), 0x0, (LPVOID)  m_pTimeZoneArr );

   if(m_szTimeZoneOptionStrs)
        HeapFree(GetProcessHeap(), 0x0,(LPVOID) m_szTimeZoneOptionStrs);

   m_cNumTimeZones = 0;
   m_pTimeZoneArr = NULL;
   m_szTimeZoneOptionStrs = NULL;
}

int CSystemClock::GetTimeZoneValStr() {
    LPCWSTR szINIFileName   = INI_SETTINGS_FILENAME;
    UINT    uiSectionName   = IDS_SECTION_OPTIONS;
    UINT    uiKeyName       = IDS_KEY_TIMEZONEVAL;
    int     Result          = -1;

    WCHAR szSectionName[1024], szKeyName[1024];

    if(GetString(m_hInstance, uiSectionName, szSectionName) && GetString(m_hInstance, uiKeyName, szKeyName))
    {
        WCHAR szINIPath[MAX_PATH];

        if(GetCanonicalizedPath(szINIPath, szINIFileName))
            Result = GetPrivateProfileInt(szSectionName, szKeyName, -1, szINIPath);
    }

    return Result;
}


int
__cdecl
TimeZoneCompare(
    const void *arg1,
    const void *arg2
    )
{
    int     BiasDiff = ((PTZINFO)arg2)->Bias - ((PTZINFO)arg1)->Bias;


    if (BiasDiff) {
        return BiasDiff;
    } else {
        return lstrcmp(
            ((PTZINFO)arg1)->szDisplayName,
            ((PTZINFO)arg2)->szDisplayName
            );
    }
}


HRESULT CSystemClock::InitSystemClock()
{
    // constructor cant return failure, so make separate init fn

    DWORD       cDefltZoneNameLen, cTotalDispNameSize;
    HRESULT     hr;
    HKEY        hRootZoneKey     = NULL;
    HKEY        hTimeZoneInfoKey = NULL;


    hr = RegOpenKey(HKEY_LOCAL_MACHINE, TIME_ZONE_REGKEY, &hRootZoneKey);
    if(hr != ERROR_SUCCESS)
      return hr;

    // find number of keys, length of default keystr
    hr= RegQueryInfoKey(hRootZoneKey, NULL,NULL,NULL,
                        &m_cNumTimeZones,
                        NULL,  // longest subkey name length
                        NULL,  // longest class string length
                        NULL,  // number of value entries
                        &cDefltZoneNameLen,  // longest value name length
                        NULL,  // longest value data length
                        NULL,  // security descriptor length
                        NULL); // last write time

    if(hr != ERROR_SUCCESS)
      return hr;

    MYASSERT(cDefltZoneNameLen<TZNAME_SIZE);

    MYASSERT(m_cNumTimeZones>0  && m_cNumTimeZones<1000);  // ensure reasonable value

    cTotalDispNameSize=0;

    if(m_pTimeZoneArr!=NULL)
        HeapFree(GetProcessHeap(), 0x0,m_pTimeZoneArr);

    m_pTimeZoneArr = (PTZINFO) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  (m_cNumTimeZones+2) * sizeof(TZINFO) );

    if( m_pTimeZoneArr == NULL)
      return ERROR_OUTOFMEMORY;

    DWORD   i;
    WCHAR   CurZoneKeyName[MAXKEYNAMELEN];
    DWORD   CurZoneKeyNameLen;
    HKEY    hCurZoneKey = NULL;
    HRESULT hrEnumRes   = ERROR_SUCCESS;

    for(i=0;hrEnumRes==ERROR_SUCCESS; i++) {
       CurZoneKeyNameLen=sizeof(CurZoneKeyName);

       hr = hrEnumRes = RegEnumKeyEx(hRootZoneKey, i,CurZoneKeyName,&CurZoneKeyNameLen,NULL,NULL,NULL,NULL);
       if(!((hr == ERROR_NO_MORE_ITEMS) || (hr ==ERROR_SUCCESS)))
          return hr;

#ifdef DBG
       if(hr!=ERROR_NO_MORE_ITEMS)
          MYASSERT(CurZoneKeyNameLen<MAXKEYNAMELEN);  // for some reason CurZoneKeyNameLen is reset to the orig value at the end of the enumeration
#endif

       // call ReadZoneData for each KeyName

       hr=RegOpenKey(hRootZoneKey, CurZoneKeyName, &hCurZoneKey);
       if(hr != ERROR_SUCCESS)
         return hr;

       hr = ReadZoneData(&m_pTimeZoneArr[i], hCurZoneKey, CurZoneKeyName);

       if(hr != S_OK)
         return hr;

       cTotalDispNameSize+= BYTES_REQUIRED_BY_SZ(m_pTimeZoneArr[i].szDisplayName) + sizeof(WCHAR);  //+1 for safety

       RegCloseKey(hCurZoneKey);
    }

    MYASSERT((i-1)==m_cNumTimeZones);

    DWORD uType, uLen = sizeof(DefltZoneKeyValue);

    MYASSERT(uLen>cDefltZoneNameLen);

    //
    // Get the current timezone name.
    //
    hr = RegOpenKey( HKEY_LOCAL_MACHINE,
                     TIME_ZONE_INFO_REGKEY,
                     &hTimeZoneInfoKey
                     );

    if ( hr != ERROR_SUCCESS )
        return hr;

    hr = RegQueryValueEx( hTimeZoneInfoKey,
                          TIMEZONE_STANDARD_NAME,
                          NULL,
                          &uType,
                          (LPBYTE)DefltZoneKeyValue,
                          &uLen
                          );

    if(hr != ERROR_SUCCESS)
        return hr;

    RegCloseKey( hTimeZoneInfoKey );
    hTimeZoneInfoKey = NULL;

    //
    // Sort our array of timezones.
    //
    qsort(
        m_pTimeZoneArr,
        m_cNumTimeZones,
        sizeof(TZINFO),
        TimeZoneCompare
        );

    // Set the timezone by the value in oobeinfo.ini, if specified
    int iINIidx=GetTimeZoneValStr();

    if ( iINIidx != -1 ) {

        // Search for the specified Index
        for(i=0;i<m_cNumTimeZones; i++) {
            if ( m_pTimeZoneArr[i].Index == iINIidx ) {
                m_uCurTimeZoneIdx = i;
                break;
            }
        }

        // need to tell script to skip tz page if we do the preset
        // set timezone to preset value

        if(i<m_cNumTimeZones) {
            m_bTimeZonePreset=TRUE;
        }
    }

    // find index of default std name
    if ( !m_bTimeZonePreset ) {
        for(i=0;i<m_cNumTimeZones; i++) {
           if(CSTR_EQUAL==CompareString(LOCALE_USER_DEFAULT, 0,
                                        DefltZoneKeyValue, -1,
                                        m_pTimeZoneArr[i].szStandardName, -1))
               break;
        }

        if(i>=m_cNumTimeZones) {
            // search failed, the default stdname value of the timezone root key does not
            // exist in the subkeys's stdnames, use default of 0
            i = 0;
        }

        m_uCurTimeZoneIdx = i;
    }

    // Create the SELECT tag OPTIONS for the html so it can get all the country names in one shot.
    if(m_szTimeZoneOptionStrs)
        HeapFree(GetProcessHeap(), 0x0,m_szTimeZoneOptionStrs);

    cTotalDispNameSize += m_cNumTimeZones * sizeof(szOptionTag) + 1;

    m_szTimeZoneOptionStrs = (WCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cTotalDispNameSize);
    if(m_szTimeZoneOptionStrs == NULL)
      return ERROR_OUTOFMEMORY;

    WCHAR szTempBuf[MAX_PATH];

    for (i=0; i < m_cNumTimeZones; i++)
    {
        wsprintf(szTempBuf, szOptionTag, m_pTimeZoneArr[i].szDisplayName);
        lstrcat(m_szTimeZoneOptionStrs, szTempBuf);
    }

    return hr;
}

////////////////////////////////////////////////
////////////////////////////////////////////////
//// GET / SET :: System clock stuff
////

HRESULT CSystemClock::set_Time(WORD wHour, WORD wMinute, WORD wSec)
{
    SYSTEMTIME SystemTime;

    GetSystemTime(&SystemTime);

    SystemTime.wHour   = wHour;
    SystemTime.wMinute = wMinute;
    SystemTime.wSecond = wSec;

    SystemTime.wMilliseconds = 0;

    SetLocalTime (&SystemTime);

    SendMessage((HWND)-1, WM_TIMECHANGE, 0, 0);

    return S_OK;
}

HRESULT CSystemClock::set_Date(WORD wMonth, WORD wDay, WORD wYear)
{
    SYSTEMTIME SystemTime;

    GetSystemTime(&SystemTime);

    SystemTime.wMonth  = wMonth;
    SystemTime.wDay    = wDay;
    SystemTime.wYear   = wYear;

    SetLocalTime (&SystemTime);

    SendMessage((HWND)-1, WM_TIMECHANGE, 0, 0);

    return S_OK;
}

HRESULT CSystemClock::set_TimeZone(BSTR bstrTimeZone)
{

    TZINFO tZone;

    ZeroMemory((void*)&tZone, sizeof(TZINFO));

    BOOL   bRet    = FALSE;
    HKEY   hKey    = NULL;
    HKEY   hSubKey = NULL;

    if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TIME_ZONE_REGKEY, &hKey))
    {
        // user can pass key name
        if(RegOpenKey(hKey, bstrTimeZone, &hSubKey) == ERROR_SUCCESS)
        {
            if(ReadZoneData(&tZone, hSubKey, bstrTimeZone))
                bRet = TRUE;
        }
        RegCloseKey(hKey);

        if(bRet)
            SetTheTimezone(m_bSetAutoDaylightMode, &tZone);
    }

    SendMessage((HWND)-1, WM_TIMECHANGE, 0, 0);

    return S_OK;
}

HRESULT CSystemClock::ReadZoneData(PTZINFO ptZone, HKEY hKey, LPCWSTR szKeyName)
{
    DWORD dwLen = 0;
    HRESULT hr;

    dwLen = sizeof(ptZone->szDisplayName);

    if(ERROR_SUCCESS != (hr = RegQueryValueEx(hKey,
                                        TIME_ZONE_DISPLAYNAME_REGVAL,
                                        0,
                                        NULL,
                                        (LPBYTE)ptZone->szDisplayName,
                                        &dwLen)))
    {
        if(hr == ERROR_MORE_DATA) {
            // registry strings from timezone.inf are too long (timezone.inf author error)
            // truncate them
            ptZone->szDisplayName[sizeof(ptZone->szDisplayName)-1]=L'\0';
        } else
           return hr;
    }

    dwLen = sizeof(ptZone->szStandardName);

    if(ERROR_SUCCESS != (hr = RegQueryValueEx(hKey,
                                        TIME_ZONE_STANDARDNAME_REGVAL,
                                        0,
                                        NULL,
                                        (LPBYTE)ptZone->szStandardName,
                                        &dwLen)))
    {
        if(hr == ERROR_MORE_DATA) {
            // registry strings from timezone.inf are too long (timezone.inf author error)
            // truncate them
            ptZone->szStandardName[sizeof(ptZone->szStandardName)-1]=L'\0';
        } else {
          // use keyname if cant get StandardName value
          lstrcpyn(ptZone->szStandardName, szKeyName, MAX_CHARS_IN_BUFFER(ptZone->szStandardName));
        }
    }

    dwLen = sizeof(ptZone->szDaylightName);

    if(ERROR_SUCCESS != (hr = RegQueryValueEx(hKey,
                                        TIME_ZONE_DAYLIGHTNAME_REGVAL,
                                        0,
                                        NULL,
                                        (LPBYTE)ptZone->szDaylightName,
                                        &dwLen)))
    {
        if(hr == ERROR_MORE_DATA) {
            // registry strings from timezone.inf are too long (timezone.inf author error)
            // truncate them
            ptZone->szDaylightName[sizeof(ptZone->szDaylightName)-1]=L'\0';
        } else
           return hr;
    }

    // get the Index
    dwLen = sizeof(ptZone->Index);

    if(ERROR_SUCCESS != (hr = RegQueryValueEx(hKey,
                                        TIME_ZONE_INDEX_REGVAL,
                                        NULL,
                                        NULL,
                                        (LPBYTE) &(ptZone->Index),
                                        &dwLen)))
    {
        return hr;
    }

    // read all these fields in at once, the way they are stored in registry
    dwLen = sizeof(ptZone->Bias)         +
            sizeof(ptZone->StandardBias) +
            sizeof(ptZone->DaylightBias) +
            sizeof(ptZone->StandardDate) +
            sizeof(ptZone->DaylightDate);

    if(ERROR_SUCCESS != (hr = RegQueryValueEx(hKey,
                                        TIME_ZONE_TZI_REGVAL,
                                        NULL,
                                        NULL,
                                        (LPBYTE) &(ptZone->Bias),
                                        &dwLen)))
    {
        // registry data from timezone.inf is too long (timezone.inf author error)
        // no good fallback behavior for binary data, so fail it so people notice the problem
        return hr;
    }

    return S_OK;
}

BOOL CSystemClock::SetTheTimezone(BOOL fAutoDaylightSavings, PTZINFO ptZone)
{
    TIME_ZONE_INFORMATION   tzi;
    WCHAR                   szIniFile[MAX_PATH] = L"";
    BOOL                    KeepCurrentTime = FALSE;
    SYSTEMTIME              SysTime;
    BOOL                    bRet;


    ZeroMemory((void*)&tzi, sizeof(TIME_ZONE_INFORMATION));

    if (ptZone==NULL)
        return FALSE;

    lstrcpyn(tzi.StandardName, ptZone->szStandardName,
            MAX_CHARS_IN_BUFFER(tzi.StandardName));
    lstrcpyn(tzi.DaylightName, ptZone->szStandardName,
            MAX_CHARS_IN_BUFFER(tzi.DaylightName));
    tzi.Bias = ptZone->Bias;
    tzi.StandardBias = ptZone->StandardBias;
    tzi.DaylightBias = ptZone->DaylightBias;
    tzi.StandardDate = ptZone->StandardDate;
    tzi.DaylightDate = ptZone->DaylightDate;

    SetAllowLocalTimeChange(fAutoDaylightSavings);

    //
    // KeepCurrentTime means we don't want the time shift that normally occurs
    // when the timezone is changed.
    //
    if (GetCanonicalizedPath(szIniFile, INI_SETTINGS_FILENAME))
    {
        KeepCurrentTime = (GetPrivateProfileInt(OPTIONS_SECTION,
                                    OOBE_KEEPCURRENTTIME,
                                    -1,
                                    szIniFile) == 1);
    }

    if (KeepCurrentTime)
    {
        GetLocalTime( &SysTime );
    }
    bRet = SetTimeZoneInformation(&tzi);
    if (KeepCurrentTime)
    {
        SetLocalTime( &SysTime );
    }
    return bRet;
}

void CSystemClock::GetTimeZoneInfo(BOOL fAutoDaylightSavings, PTZINFO ptZone)
{
    TIME_ZONE_INFORMATION tzi;

    ZeroMemory((void*)&tzi, sizeof(TIME_ZONE_INFORMATION));

    if (!ptZone)
        return;

    lstrcpyn(tzi.StandardName, ptZone->szStandardName,
            MAX_CHARS_IN_BUFFER(tzi.StandardName));
    lstrcpyn(tzi.DaylightName, ptZone->szStandardName,
            MAX_CHARS_IN_BUFFER(tzi.DaylightName));
    tzi.Bias = ptZone->Bias;
    tzi.StandardBias = ptZone->StandardBias;
    tzi.DaylightBias = ptZone->DaylightBias;
    tzi.StandardDate = ptZone->StandardDate;
    tzi.DaylightDate = ptZone->DaylightDate;

    SetAllowLocalTimeChange(fAutoDaylightSavings);
    SetTimeZoneInformation(&tzi);

}

void CSystemClock::SetAllowLocalTimeChange(BOOL fAutoDaylightSavings)
{
    HKEY  hKey  = NULL;
    DWORD dwVal = 1;

    if(fAutoDaylightSavings)
    {
        // remove the disallow flag from the registry if it exists
        if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE,
                                       REGSTR_PATH_TIMEZONE,
                                       &hKey))
        {
            RegDeleteValue(hKey, REGSTR_VAL_TZNOAUTOTIME);
        }
    }
    else
    {
        // add/set the nonzero disallow flag
        if(ERROR_SUCCESS == RegCreateKey(HKEY_LOCAL_MACHINE,
                                         REGSTR_PATH_TIMEZONE,
                                         &hKey))
        {
            RegSetValueEx(hKey,
                         (LPCWSTR)REGSTR_VAL_TZNOAUTOTIME,
                         0UL,
                         REG_DWORD,
                         (LPBYTE)&dwVal,
                         sizeof(dwVal));
        }
    }

    if(hKey)
        RegCloseKey(hKey);
}

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
/////// IUnknown implementation
///////
///////

/////////////////////////////////////////////////////////////
// CSystemClock::QueryInterface
STDMETHODIMP CSystemClock::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
    // must set out pointer parameters to NULL
    *ppvObj = NULL;

    if ( riid == IID_IUnknown)
    {
        AddRef();
        *ppvObj = (IUnknown*)this;
        return ResultFromScode(S_OK);
    }

    if (riid == IID_IDispatch)
    {
        AddRef();
        *ppvObj = (IDispatch*)this;
        return ResultFromScode(S_OK);
    }

    // Not a supported interface
    return ResultFromScode(E_NOINTERFACE);
}

/////////////////////////////////////////////////////////////
// CSystemClock::AddRef
STDMETHODIMP_(ULONG) CSystemClock::AddRef()
{
    return ++m_cRef;
}

/////////////////////////////////////////////////////////////
// CSystemClock::Release
STDMETHODIMP_(ULONG) CSystemClock::Release()
{
    return --m_cRef;
}

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
/////// IDispatch implementation
///////
///////

/////////////////////////////////////////////////////////////
// CSystemClock::GetTypeInfo
STDMETHODIMP CSystemClock::GetTypeInfo(UINT, LCID, ITypeInfo**)
{
    return E_NOTIMPL;
}

/////////////////////////////////////////////////////////////
// CSystemClock::GetTypeInfoCount
STDMETHODIMP CSystemClock::GetTypeInfoCount(UINT* pcInfo)
{
    return E_NOTIMPL;
}


/////////////////////////////////////////////////////////////
// CSystemClock::GetIDsOfNames
STDMETHODIMP CSystemClock::GetIDsOfNames(REFIID    riid,
                                       OLECHAR** rgszNames,
                                       UINT      cNames,
                                       LCID      lcid,
                                       DISPID*   rgDispId)
{

    HRESULT hr  = DISP_E_UNKNOWNNAME;
    rgDispId[0] = DISPID_UNKNOWN;

    for (int iX = 0; iX < sizeof(SystemClockExternalInterface)/sizeof(DISPATCHLIST); iX ++)
    {
        if(lstrcmp(SystemClockExternalInterface[iX].szName, rgszNames[0]) == 0)
        {
            rgDispId[0] = SystemClockExternalInterface[iX].dwDispID;
            hr = NOERROR;
            break;
        }
    }

    // Set the disid's for the parameters
    if (cNames > 1)
    {
        // Set a DISPID for function parameters
        for (UINT i = 1; i < cNames ; i++)
            rgDispId[i] = DISPID_UNKNOWN;
    }

    return hr;
}

/////////////////////////////////////////////////////////////
// CSystemClock::Invoke
HRESULT CSystemClock::Invoke
(
    DISPID      dispidMember,
    REFIID      riid,
    LCID        lcid,
    WORD        wFlags,
    DISPPARAMS* pdispparams,
    VARIANT*    pvarResult,
    EXCEPINFO*  pexcepinfo,
    UINT*       puArgErr
)
{
    HRESULT hr = S_OK;

    switch(dispidMember)
    {
        case DISPID_SYSTEMCLOCK_INIT:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_INIT\n");

            InitSystemClock();
            break;
        }

        case DISPID_SYSTEMCLOCK_GETALLTIMEZONES:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_GETALLTIMEZONES\n");

            if (m_cNumTimeZones && m_szTimeZoneOptionStrs && pvarResult) {
                VariantInit(pvarResult);
                V_VT(pvarResult) = VT_BSTR;
                pvarResult->bstrVal = SysAllocString(m_szTimeZoneOptionStrs);
            }
            break;
        }

        case DISPID_SYSTEMCLOCK_GETTIMEZONEIDX:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_GETTIMEZONEIDX\n");

            if(pvarResult==NULL)
              break;

            VariantInit(pvarResult);
            V_VT(pvarResult) = VT_I4;
            V_I4(pvarResult) = m_uCurTimeZoneIdx;
            break;
        }

        case DISPID_SYSTEMCLOCK_SETTIMEZONEIDX:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_SETTIMEZONEIDX\n");

            if(pdispparams && (&pdispparams[0].rgvarg[0]))
            {
                BOOL bReboot;
                m_uCurTimeZoneIdx = pdispparams[0].rgvarg[0].iVal;

                SetTheTimezone(m_bSetAutoDaylightMode,
                               &m_pTimeZoneArr[m_uCurTimeZoneIdx]
                               );
                if (pvarResult != NULL)
                {
                    WCHAR szWindowsRoot[MAX_PATH];
                    BOOL  bCheckTimezone = TRUE;
                    VariantInit(pvarResult);
                    V_VT(pvarResult) = VT_BOOL;
                    V_BOOL(pvarResult) = Bool2VarBool(FALSE);

                    if (GetWindowsDirectory(szWindowsRoot, MAX_PATH))
                    {
                        // If Windows is installed on an NTFS volume,
                        // no need to check the timezones and evtl reboot
                        // The problem we are working around only exists on FAT
                        bCheckTimezone = !(IsDriveNTFS(szWindowsRoot[0]));
                    }
                    if (bCheckTimezone)
                    {
                        // If the name of the default time zone the now selected one is different, we need a reboot.
                        // Problem with fonts, if the time zone changes.
                        V_BOOL(pvarResult) = Bool2VarBool(CSTR_EQUAL!=CompareString(LOCALE_USER_DEFAULT, 0,
                                            DefltZoneKeyValue, -1,
                                            m_pTimeZoneArr[m_uCurTimeZoneIdx].szStandardName, -1));
                    }

                }
            }
            break;
        }

        case DISPID_SYSTEMCLOCK_GETAUTODAYLIGHT:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_GETAUTODAYLIGHT\n");

            if(pvarResult==NULL)
              break;

            VariantInit(pvarResult);
            V_VT(pvarResult) = VT_BOOL;
            V_BOOL(pvarResult) = Bool2VarBool(m_bSetAutoDaylightMode);
            break;
        }

        case DISPID_SYSTEMCLOCK_GETDAYLIGHT_ENABLED:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_GETDAYLIGHT_ENABLED\n");

            if(pvarResult==NULL)
                break;

            if(!(pdispparams && (&pdispparams[0].rgvarg[0])))
            {
                break;
            }

            DWORD iTzIdx = pdispparams[0].rgvarg[0].iVal;

            if(iTzIdx >= m_cNumTimeZones)
            {
                break;
            }

            // if either daylight change date is invalid (0), no daylight savings time for that zone
            BOOL bEnabled = !((m_pTimeZoneArr[iTzIdx].StandardDate.wMonth == 0) ||
                              (m_pTimeZoneArr[iTzIdx].DaylightDate.wMonth == 0));

            VariantInit(pvarResult);
            V_VT(pvarResult) = VT_BOOL;
            V_BOOL(pvarResult) = Bool2VarBool(bEnabled);
            break;
        }

        case DISPID_SYSTEMCLOCK_GETTIMEZONEWASPRESET:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_GETTIMEZONEWASPRESET\n");

            if(pvarResult==NULL)
              break;

            VariantInit(pvarResult);
            V_VT(pvarResult) = VT_BOOL;
            V_BOOL(pvarResult) = Bool2VarBool(m_bTimeZonePreset);
            break;
        }

        case DISPID_SYSTEMCLOCK_SETAUTODAYLIGHT:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_SETAUTODAYLIGHT\n");

            if(!(pdispparams && (&pdispparams[0].rgvarg[0])))
            {
              break;
            }

            m_bSetAutoDaylightMode = pdispparams[0].rgvarg[0].boolVal;
            break;
        }

        case DISPID_SYSTEMCLOCK_SETTIMEZONE:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_SETTIMEZONE\n");

            if(pdispparams && &pdispparams[0].rgvarg[0])
                set_TimeZone(pdispparams[0].rgvarg[0].bstrVal);
            break;
        }

        case DISPID_SYSTEMCLOCK_SETTIME:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_SETTIME\n");

            if(pdispparams               &&
               &pdispparams[0].rgvarg[0] &&
               &pdispparams[0].rgvarg[1] &&
               &pdispparams[0].rgvarg[2]
              )
            {
                set_Time(pdispparams[0].rgvarg[2].iVal,
                         pdispparams[0].rgvarg[1].iVal,
                         pdispparams[0].rgvarg[0].iVal);
            }
            break;
        }

        case DISPID_SYSTEMCLOCK_SETDATE:
        {

            TRACE(L"DISPID_SYSTEMCLOCK_SETDATE\n");

            if(pdispparams               &&
               &pdispparams[0].rgvarg[0] &&
               &pdispparams[0].rgvarg[1] &&
               &pdispparams[0].rgvarg[2]
              )
            {
                set_Date(pdispparams[0].rgvarg[2].iVal,
                         pdispparams[0].rgvarg[1].iVal,
                         pdispparams[0].rgvarg[0].iVal);
            }
            break;
        }

        default:
        {
            hr = DISP_E_MEMBERNOTFOUND;
            break;
        }
    }

    return hr;
}