windows-nt/Source/XPSP1/NT/base/ntsetup/ntoc/datetime.cpp
2020-09-26 16:20:57 +08:00

952 lines
24 KiB
C++

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
datetime.cpp
Abstract:
This file implements the date & time page.
Environment:
WIN32 User Mode
Author:
Wesley Witt (wesw) 1-Dec-1997
--*/
#include "ntoc.h"
#pragma hdrstop
#define MYDEBUG 0
#define TIMER_ID 1
#define OPEN_TLEN 450 /* < half second */
#define TZNAME_SIZE 128
#define TZDISPLAYZ 128
#define REGKEY_TIMEZONES L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
#define REGVAL_TZ_DISPLAY L"Display"
#define REGVAL_TZ_STD L"Std"
#define REGVAL_TZ_DAYLIGHT L"Dlt"
#define REGVAL_TZ_TZI L"TZI"
#define REGVAL_TZ_INDEX L"Index"
#define REGVAL_TZ_INDEXMAP L"IndexMapping"
#define REGKEY_TIMEZONE_INFO L"System\\CurrentControlSet\\Control\\TimeZoneInformation"
#define REGVAL_TZNOAUTOTIME L"DisableAutoDaylightTimeSet"
typedef struct tagTZINFO
{
LIST_ENTRY ListEntry;
WCHAR szDisplayName[TZDISPLAYZ];
WCHAR szStandardName[TZNAME_SIZE];
WCHAR szDaylightName[TZNAME_SIZE];
int ReferenceIndex;
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} TZINFO, *PTZINFO;
LIST_ENTRY ZoneList;
SYSTEMTIME SelectedTime;
SYSTEMTIME SelectedDate;
BOOL ChangeTime;
BOOL ChangeDate;
PTZINFO CurrZone;
BOOL AllowAutoDST;
INT gUnattenedTimeZone = -1;
BOOL DateTimeBadUnattend;
HWND ghWnd; // global copy of the handle to the wizard page. This
// is required by DateTimeCommitChanges during an
// unattended installation.
BOOL
ReadZoneData(
PTZINFO zone,
HKEY key,
LPCWSTR keyname
)
{
WCHAR mapinfo[16];
DWORD len;
len = sizeof(zone->szDisplayName);
if (RegQueryValueEx( key,
REGVAL_TZ_DISPLAY,
0,
NULL,
(LPBYTE)zone->szDisplayName,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
//
// Under NT, the keyname is the "Standard" name. Values stored
// under the keyname contain the other strings and binary info
// related to the time zone. Every time zone must have a standard
// name, therefore, we save registry space by using the Standard
// name as the subkey name under the "Time Zones" key.
//
len = sizeof(zone->szStandardName);
if (RegQueryValueEx( key,
REGVAL_TZ_STD,
0,
NULL,
(LPBYTE)zone->szStandardName,
&len ) != ERROR_SUCCESS)
{
//
// Use keyname if can't get StandardName value.
//
lstrcpyn( zone->szStandardName,
keyname,
sizeof(zone->szStandardName) );
}
len = sizeof(zone->szDaylightName);
if (RegQueryValueEx( key,
REGVAL_TZ_DAYLIGHT,
0,
NULL,
(LPBYTE)zone->szDaylightName,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
len = sizeof(zone->ReferenceIndex);
if (RegQueryValueEx( key,
REGVAL_TZ_INDEX,
0,
NULL,
(LPBYTE)&zone->ReferenceIndex,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
len = sizeof(zone->Bias) +
sizeof(zone->StandardBias) +
sizeof(zone->DaylightBias) +
sizeof(zone->StandardDate) +
sizeof(zone->DaylightDate);
if (RegQueryValueEx( key,
REGVAL_TZ_TZI,
0,
NULL,
(LPBYTE)&zone->Bias,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
return (TRUE);
}
#if MYDEBUG
void
PrintZones(
void
)
{
PLIST_ENTRY NextZone;
PTZINFO zone;
NextZone = ZoneList.Flink;
if (NextZone) {
DebugPrint(( L"----------------- time zone list -------------------------------------\n" ));
while (NextZone != &ZoneList) {
zone = CONTAINING_RECORD( NextZone, TZINFO, ListEntry );
NextZone = zone->ListEntry.Flink;
DebugPrint(( L"%03d %s", zone->ReferenceIndex, zone->szDisplayName ));
}
}
}
#endif
void
AddZoneToList(
PTZINFO zone
)
{
PLIST_ENTRY NextZone;
PTZINFO ThisZone;
if (IsListEmpty( &ZoneList )) {
InsertHeadList( &ZoneList, &zone->ListEntry );
return;
}
NextZone = ZoneList.Flink;
while (NextZone != &ZoneList)
{
ThisZone = CONTAINING_RECORD( NextZone, TZINFO, ListEntry );
NextZone = ThisZone->ListEntry.Flink;
if (ThisZone->ReferenceIndex > zone->ReferenceIndex) {
InsertTailList( &ThisZone->ListEntry, &zone->ListEntry );
return;
}
}
InsertTailList( &ZoneList, &zone->ListEntry );
}
int
BuildTimeZoneList(
void
)
{
HKEY key = NULL;
int count = -1;
InitializeListHead( &ZoneList );
if (RegOpenKey( HKEY_LOCAL_MACHINE, REGKEY_TIMEZONES, &key ) == ERROR_SUCCESS)
{
WCHAR name[TZNAME_SIZE];
PTZINFO zone = NULL;
int i;
count = 0;
for (i = 0; RegEnumKey( key, i, name, TZNAME_SIZE ) == ERROR_SUCCESS; i++)
{
HKEY subkey = NULL;
if (!zone &&
((zone = (PTZINFO)LocalAlloc(LPTR, sizeof(TZINFO))) == NULL))
{
break;
}
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS)
{
//
// Each sub key name under the Time Zones key is the
// "Standard" name for the Time Zone.
//
lstrcpyn(zone->szStandardName, name, TZNAME_SIZE);
if (ReadZoneData(zone, subkey, name))
{
AddZoneToList(zone);
zone = NULL;
count++;
}
RegCloseKey(subkey);
}
}
RegCloseKey(key);
}
return count;
}
void
DateTimeInit(
void
)
{
DWORD d;
BuildTimeZoneList();
#if MYDEBUG
PrintZones();
#endif
if ((SetupInitComponent.SetupData.OperationFlags & SETUPOP_BATCH) == 0) {
return;
}
HINF InfHandle = SetupInitComponent.HelperRoutines.GetInfHandle(
INFINDEX_UNATTENDED,
SetupInitComponent.HelperRoutines.OcManagerContext
);
if (InfHandle == NULL) {
DateTimeBadUnattend = TRUE;
return;
}
INFCONTEXT InfLine;
if (!SetupFindFirstLine(InfHandle, L"GuiUnattended", L"TimeZone", &InfLine )) {
DateTimeBadUnattend = TRUE;
return;
}
if (SetupGetIntField( &InfLine, 1, (PINT)&d )) {
gUnattenedTimeZone = (INT) d;
} else {
DateTimeBadUnattend = TRUE;
}
}
void
SetAllowLocalTimeChange(
BOOL fAllow
)
{
HKEY key = NULL;
if (fAllow)
{
//
// Remove the disallow flag from the registry if it exists.
//
if (RegOpenKey( HKEY_LOCAL_MACHINE, REGKEY_TIMEZONE_INFO, &key ) == ERROR_SUCCESS)
{
RegDeleteValue(key, REGVAL_TZNOAUTOTIME);
}
}
else
{
//
// Add/set the nonzero disallow flag.
//
if (RegCreateKey( HKEY_LOCAL_MACHINE, REGKEY_TIMEZONE_INFO, &key ) == ERROR_SUCCESS)
{
DWORD value = 1;
RegSetValueEx( key,
REGVAL_TZNOAUTOTIME,
0,
REG_DWORD,
(LPBYTE)&value,
sizeof(value) );
}
}
if (key)
{
RegCloseKey(key);
}
}
BOOL
GetAllowLocalTimeChange(
void
)
{
//
// Assume allowed until we see a disallow flag.
//
BOOL result = TRUE;
HKEY key;
if (RegOpenKey( HKEY_LOCAL_MACHINE, REGKEY_TIMEZONE_INFO, &key ) == ERROR_SUCCESS)
{
//
// Assume no disallow flag until we see one.
//
DWORD value = 0;
DWORD len = sizeof(value);
DWORD type;
if ((RegQueryValueEx( key,
REGVAL_TZNOAUTOTIME,
NULL,
&type,
(LPBYTE)&value,
&len ) == ERROR_SUCCESS) &&
((type == REG_DWORD) || (type == REG_BINARY)) &&
(len == sizeof(value)) && value)
{
//
// Okay, we have a nonzero value, it is either:
//
// 1) 0xFFFFFFFF
// this is set in an inf file for first boot to prevent
// the base from performing any cutovers during setup.
//
// 2) some other value
// this signifies that the user actualy disabled cutovers
// *return that local time changes are disabled
//
if (value != 0xFFFFFFFF)
{
result = FALSE;
}
}
RegCloseKey(key);
}
return (result);
}
void
SetTheTimezone(
BOOL bAutoMagicTimeChange,
PTZINFO ptzi
)
{
TIME_ZONE_INFORMATION tzi;
HCURSOR hCurOld;
if (!ptzi)
{
return;
}
tzi.Bias = ptzi->Bias;
if ((bAutoMagicTimeChange == 0) || (ptzi->StandardDate.wMonth == 0))
{
//
// Standard Only.
//
tzi.StandardBias = ptzi->StandardBias;
tzi.DaylightBias = ptzi->StandardBias;
tzi.StandardDate = ptzi->StandardDate;
tzi.DaylightDate = ptzi->StandardDate;
lstrcpy(tzi.StandardName, ptzi->szStandardName);
lstrcpy(tzi.DaylightName, ptzi->szStandardName);
}
else
{
//
// Automatically adjust for Daylight Saving Time.
//
tzi.StandardBias = ptzi->StandardBias;
tzi.DaylightBias = ptzi->DaylightBias;
tzi.StandardDate = ptzi->StandardDate;
tzi.DaylightDate = ptzi->DaylightDate;
lstrcpy(tzi.StandardName, ptzi->szStandardName);
lstrcpy(tzi.DaylightName, ptzi->szDaylightName);
}
SetAllowLocalTimeChange( bAutoMagicTimeChange );
SetTimeZoneInformation( &tzi );
}
void
DateTimeApplyChanges(
void
)
{
SYSTEMTIME SysTime;
if (SetupInitComponent.SetupData.OperationFlags & SETUPOP_NTUPGRADE) {
return;
}
// Note that in the unattended case we will never have ChangeTime set
// as the page never is used except for the timezone stuff. There is
// no support to set date/time via unattend.
if (ChangeTime) {
SysTime.wHour = SelectedTime.wHour;
SysTime.wMinute = SelectedTime.wMinute;
SysTime.wSecond = SelectedTime.wSecond;
SysTime.wMilliseconds = SelectedTime.wMilliseconds;
} else {
GetLocalTime( &SysTime );
}
// If this is an unattended setup the PSN_WIZNEXT never arrived so it is
// necessary to check the state of ICD_DAYTIME which was set by DateTimeOnInitDialog().
if ((SetupInitComponent.SetupData.OperationFlags & SETUPOP_BATCH) && gUnattenedTimeZone != -1) {
// This is unattended.
AllowAutoDST = IsDlgButtonChecked( ghWnd, IDC_DAYLIGHT ) != 0;
}
else
{
// This is NOT UNATTENDED. SelectedDate was initialized when PSN_WIZNEXT
// was processed.
SysTime.wYear = SelectedDate.wYear;
SysTime.wMonth = SelectedDate.wMonth;
SysTime.wDayOfWeek = SelectedDate.wDayOfWeek;
SysTime.wDay = SelectedDate.wDay;
}
// Function SetLocalTime uses Time Zone information so it is IMPERATIVE that
// SetTheTimezone get called before SetLocalTime.
SetTheTimezone( AllowAutoDST, CurrZone );
SetLocalTime( &SysTime );
}
void
DateTimeCommitChanges(
void
)
{
return;
}
/////////////////////////////////////////////////////////////////////////////
//++
//
// GetTimeZoneReferenceIndexFromRegistry
//
// Routine Description:
// This funecion extracts the Time Zone reference index from information that
// is stored in the registry.
//
// Arguments:
// None
//
// Return Value:
// The Time Zone reference index. If no valid reference index is deduced
// this function will return zero.
//
// Note:
// The logic performed by the following function was originally implemented in
// DateTimeOnInitDialog.
//
//--
/////////////////////////////////////////////////////////////////////////////
int GetTimeZoneReferenceIndexFromRegistry( void )
{
int xReferenceIndex;
HKEY hKey;
// Attempt to open the Time Zones registry key.
if ( RegOpenKey( HKEY_LOCAL_MACHINE, REGKEY_TIMEZONES, &hKey ) == ERROR_SUCCESS )
{
// The following call to RegQueryValueEx retrieves the size, in bytes, of
// the IndexMapping registry value, in parameter "index".
int xIndexMapSize;
if ( RegQueryValueEx( hKey, REGVAL_TZ_INDEXMAP, 0, NULL, NULL,
(LPDWORD) &xIndexMapSize ) == ERROR_SUCCESS )
{
// Allocate memory for the IndexMap registry value.
LPWSTR wszIndexMap;
wszIndexMap = (LPWSTR) LocalAlloc( LPTR, xIndexMapSize );
// Was a buffer allocated successfully?
if ( wszIndexMap != (LPWSTR) NULL )
{
// This call to RegQueryValueEx retrieves the IndexMap value into
// the buffer, wszIndexMap.
if ( RegQueryValueEx( hKey, REGVAL_TZ_INDEXMAP, 0, NULL,
(LPBYTE) wszIndexMap,
(LPDWORD) &xIndexMapSize ) == ERROR_SUCCESS )
{
// Get the language identifier.
WCHAR wszLangStr[32];
if ( GetLocaleInfo( LOCALE_USER_DEFAULT,
LOCALE_ILANGUAGE, wszLangStr,
sizeof( wszLangStr )/sizeof( WCHAR ) ) > 0 )
{
LPWSTR lang = wszLangStr;
LPWSTR map = wszIndexMap;
while ( *lang == L'0' ) lang++;
while ( *map )
{
if ( _wcsicmp( lang, map ) == 0 )
{
while ( *map ) map++;
map++;
xReferenceIndex = _wtol( map );
break;
}
while ( *map ) map++;
map++;
while ( *map ) map++;
map++;
} // end of while loop
} // language identifier obtained?
} // IndexMapping reg value queried?
LocalFree( wszIndexMap );
} // memory allocated for ImageMap reg value retrieval?
else
{
xReferenceIndex = 0;
} // memory allocated for ImageMap reg value retrieval?
} // Size of ImageMap obtained?
else
{
xReferenceIndex = 0;
} // Size of ImageMap obtained?
RegCloseKey( hKey );
} // TimeZones reg key opened?
else
{
xReferenceIndex = 0;
} // TimeZones reg key opened?
return ( xReferenceIndex );
}
BOOL
DateTimeOnInitDialog(
IN HWND hwnd,
IN HWND hwndFocus,
IN LPARAM lParam
)
{
PLIST_ENTRY NextZone;
PTZINFO zone;
HWND combo;
WCHAR LangStr[32];
int DesiredZone = 0;
int index;
HKEY hKey;
LPWSTR IndexMap;
ghWnd = hwnd; // initialize the global copy of the handle to the
// wizard page. ghWnd is used by DateTimeCommitChanges()
// during unattended setup.
SetTimer( hwnd, TIMER_ID, OPEN_TLEN, 0 );
if ( (SetupInitComponent.SetupData.OperationFlags & SETUPOP_BATCH) && gUnattenedTimeZone != -1 )
{
//
// We've got an unattended time zone value
//
// If everything were perfect DesiredZone will exactly match the ReferenceIndex
// member of one of the TZINFO structures in ZoneList. Note that ZoneList was
// built by BuildTimeZoneList.
DesiredZone = gUnattenedTimeZone;
}
else
{
//
// Base the default zone on the locale
//
// Extract the reference index for the desired time zone from the registry.
DesiredZone = GetTimeZoneReferenceIndexFromRegistry();
} // Time zone specified in unattended setup answer file?
#if MYDEBUG
DebugPrint(( L"DesiredZone = %03d", DesiredZone ));
#endif
combo = GetDlgItem( hwnd, IDC_TIMEZONE );
SetWindowRedraw( combo, FALSE );
PTZINFO pTimeZoneInfo = (PTZINFO) NULL;
// Note that ZoneList was built by BuildTimeZoneList.
NextZone = ZoneList.Flink;
if ( NextZone )
{
// Add time zones to the combo box.
while ( NextZone != &ZoneList )
{
zone = CONTAINING_RECORD( NextZone, TZINFO, ListEntry );
NextZone = zone->ListEntry.Flink;
index = ComboBox_AddString( combo, zone->szDisplayName );
#if MYDEBUG
DebugPrint(( L"%03d,%03d %s", index, zone->ReferenceIndex, zone->szDisplayName ));
#endif
if ( index < 0 )
{
break;
}
ComboBox_SetItemData( combo, index, (LPARAM)zone );
if ( DesiredZone == zone->ReferenceIndex )
{
pTimeZoneInfo = zone;
#if MYDEBUG
DebugPrint(( L" Found DesiredZone" ));
#endif
}
} // end of while loop
}
// Was a time zone that matched DesiredZone identified?
if ( pTimeZoneInfo != (PTZINFO) NULL )
{
// Set the GLOBAL Time Zone Info structure pointer.
CurrZone = pTimeZoneInfo;
}
else
{
// The fact that pTimeZoneInfo remained unchanged from its' initialized state
// means that DesiredZone is not meaningfull.
// Was DesiredZone obtained from the unattended setup answer file?
if ( gUnattenedTimeZone != -1 )
{
// DesiredZone was obtained from the answer file. Since it is not meaningfull,
// attempt to deduce it from registry information. Deducing DesiredZone from
// information in the registry is the default action for ATTENDED setup.
DesiredZone = GetTimeZoneReferenceIndexFromRegistry();
} // Was DesiredZone obtained from the answer file?
// Is DesiredZone meaningfull now?
if ( DesiredZone != 0 )
{
// Scan the list of Time Zones for one that matches DesiredZone.
NextZone = ZoneList.Flink;
if ( NextZone )
{
while ( NextZone != &ZoneList )
{
zone = CONTAINING_RECORD( NextZone, TZINFO, ListEntry );
NextZone = zone->ListEntry.Flink;
#if MYDEBUG
DebugPrint(( L"%03d,%03d %s", index, zone->ReferenceIndex, zone->szDisplayName ));
#endif
if ( DesiredZone == zone->ReferenceIndex )
{
pTimeZoneInfo = zone;
}
} // end of while loop
} // Is NextZone legal?
} // Is DesiredZone meaningfull now?
// Was a time zone that matched DesiredZone identified?
Assert( pTimeZoneInfo != (PTZINFO) NULL );
if ( pTimeZoneInfo != (PTZINFO) NULL )
{
// Set the GLOBAL Time Zone Info structure pointer.
CurrZone = pTimeZoneInfo;
}
else
{
// Use the first Time Zone in the list as a default.
CurrZone = CONTAINING_RECORD( ZoneList.Flink, TZINFO, ListEntry );
#if MYDEBUG
DebugPrint(( L"Couldn't find default timzone" ));
#endif
} // Was a time zone that matched DesiredZone identified?
} // Was a time zone that matched DesiredZone identified?
index = ComboBox_FindString( combo, 0, CurrZone->szDisplayName );
if ( index == CB_ERR )
{
index = 0;
}
ComboBox_SetCurSel( combo, index );
EnableWindow( GetDlgItem( hwnd, IDC_DAYLIGHT ), CurrZone->StandardDate.wMonth != 0 );
CheckDlgButton( hwnd, IDC_DAYLIGHT, GetAllowLocalTimeChange() );
SetWindowRedraw(combo, TRUE);
return FALSE;
}
BOOL
DateTimeOnCommand(
IN HWND hwnd,
IN DWORD NotifyCode,
IN DWORD ControlId,
IN HWND hwndControl
)
{
if (NotifyCode == CBN_SELCHANGE && ControlId == IDC_TIMEZONE) {
CurrZone = (PTZINFO) ComboBox_GetItemData( hwndControl, ComboBox_GetCurSel( hwndControl ) );
EnableWindow( GetDlgItem( hwnd, IDC_DAYLIGHT ), CurrZone->StandardDate.wMonth != 0 );
if (CurrZone->StandardDate.wMonth != 0) {
CheckDlgButton( hwnd, IDC_DAYLIGHT, TRUE );
} else {
CheckDlgButton( hwnd, IDC_DAYLIGHT, FALSE );
}
return FALSE;
}
return TRUE;
}
BOOL
DateTimeOnNotify(
IN HWND hwnd,
IN WPARAM ControlId,
IN LPNMHDR pnmh
)
{
switch( pnmh->code ) {
case PSN_SETACTIVE:
if (SetupInitComponent.SetupData.OperationFlags & SETUPOP_NTUPGRADE) {
SetWindowLongPtr( hwnd, DWLP_MSGRESULT, -1 );
return TRUE;
}
if ((SetupInitComponent.SetupData.OperationFlags & SETUPOP_BATCH) && DateTimeBadUnattend) {
// No unattend value for time date in the unattend case.
// make sure the wizard is shown.
// note: When we get out here, only the next button is enabled.
SetupInitComponent.HelperRoutines.ShowHideWizardPage(
SetupInitComponent.HelperRoutines.OcManagerContext,
TRUE);
return FALSE;
}
if ((SetupInitComponent.SetupData.OperationFlags & SETUPOP_BATCH) && gUnattenedTimeZone != -1) {
//
// we're in unattend mode
//
DateTimeApplyChanges();
SetWindowLongPtr( hwnd, DWLP_MSGRESULT, -1 );
return TRUE;
}
// If we get here the user needs has click next or back.
// Make sure the wizard page is showing.
// For Whistler GUI mode we try to hide wizard pages and show a background
// billboard if there is only a progress bar.
//
SetupInitComponent.HelperRoutines.ShowHideWizardPage(
SetupInitComponent.HelperRoutines.OcManagerContext,
TRUE);
PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_BACK | PSWIZB_NEXT);
break;
case DTN_DATETIMECHANGE:
if (ControlId == IDC_TIME_PICKER) {
KillTimer( hwnd, TIMER_ID );
ChangeTime = TRUE;
} else if (ControlId == IDC_DATE_PICKER) {
ChangeDate = TRUE;
}
break;
case PSN_WIZNEXT:
SendDlgItemMessage( hwnd, IDC_TIME_PICKER, DTM_GETSYSTEMTIME, 0, (LPARAM)&SelectedTime );
SendDlgItemMessage( hwnd, IDC_DATE_PICKER, DTM_GETSYSTEMTIME, 0, (LPARAM)&SelectedDate );
AllowAutoDST = IsDlgButtonChecked( hwnd, IDC_DAYLIGHT ) != 0;
DateTimeApplyChanges();
break;
}
return FALSE;
}
BOOL
DateTimeOnTimer(
IN HWND hwnd
)
{
SYSTEMTIME CurrTime;
GetLocalTime( &CurrTime );
SendDlgItemMessage( hwnd, IDC_TIME_PICKER, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&CurrTime );
return FALSE;
}
LRESULT
DateTimeDlgProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
CommonWizardProc( hwnd, message, wParam, lParam, WizPageDateTime );
switch( message ) {
case WM_INITDIALOG:
return DateTimeOnInitDialog( hwnd, (HWND)wParam, lParam );
case WM_COMMAND:
return DateTimeOnCommand( hwnd, HIWORD(wParam), LOWORD(wParam), (HWND)lParam );
case WM_TIMER:
return DateTimeOnTimer( hwnd );
case WM_NOTIFY:
return DateTimeOnNotify( hwnd, wParam, (LPNMHDR) lParam );
}
return FALSE;
}