📄 dst.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*---------------------------------------------------------------------------*\
* module: dst.cpp
* purpose: DST support. Need to handle the following 3 scenarios
* 1) DST turns on/off when system is running
* 2) DST turns on/off when system is suspended
* 3) DST turns on/off when system is powered off
*
\*---------------------------------------------------------------------------*/
#include <windows.h>
#include <notify.h>
#include <service.h>
#include "../inc/timesvc.h"
#include "dst.h"
#include "dstrc.h"
//keep these consistant with names in public\wceshellfe\oak\ctlpnl\cplmain\regcpl.h
#define RK_CLOCK TEXT("Software\\Microsoft\\Clock")
#define RV_INDST TEXT("HomeDST") //are we in DST?
#define RV_AUTODST TEXT("AutoDST") //do we auto-adjust DST?
#define RV_DSTUI TEXT("ShowDSTUI") //do we show a message at dst change?
#define DSTEVENT _T("ShellDSTEvent")
#define DSTTZEVENT _T("DSTTzChange")
#define DSTTIMEEVENT _T("DSTTimeChange")
#define DSTNOTIFICATION _T("\\\\.\\Notifications\\NamedEvents\\ShellDSTEvent")
#define DSTTIMEZONENOTIFICATION _T("\\\\.\\Notifications\\NamedEvents\\DSTTzChange")
#define DSTTIMENOTIFICATION _T("\\\\.\\Notifications\\NamedEvents\\DSTTimeChange")
#define NUMDSTEVENTS 4
#define SHUTDOWNEVENT 0
#define TZCHANGEEVENT 1
#define TIMECHANGEEVENT 2
#define DSTCHANGEEVENT 3 // This event must be last so WaitForMultipleObjects algorithm works (see DST_WaitForEvents)
#define MAX_DSTMSGLEN 256
#define MAX_DSTTITLELEN 64
// FILETIME (100-ns intervals) to minutes (10 x 1000 x 1000 x 60)
#define FILETIME_TO_MINUTES (10 * 1000 * 1000 * 60)
typedef int (*tMessageBoxW) (HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);
typedef HWND (*tGetForegroundWindow) (VOID);
static DWORD g_dwTime = TIME_ZONE_ID_STANDARD;
static HINSTANCE g_hInst;
static HANDLE g_hDSTThread;
static HANDLE g_hDSTShutdownEvent;
static DWORD g_dwDSTState;
// If the DSTCHANGEEVENT is fired within 1 minute on one of the DST boundaries
// (DST->STD or STD->DST) then always flip the system DST value on the system
// with a call to SetDaylightTime(). Otherwise assume that the event has fired
// because of a clock change (manual or SNTP server related) in which case we
// may or may not need to do this calculation.
#define DST_TIME_THRESHHOLD (1 * FILETIME_TO_MINUTES)
//
// Private DST code
//
/*++
Routine Description:
Determines whether the current date and time is in daylight
or standard time, and notifies the kernel.
Kernel doesn't currently update this in all cases
Manually take care of it to be safe and ensure that subsequent calls
to GetTimeZoneInformation return the correct value
pNewSystemTime may be NULL to retrieve current system time, or may
indicate time we're about to change to on clock update
Arguments:
none
Return Value:
current timezone value (same as return values for GetTimeZoneInformation
--*/
void SetDaylightOrStandardTimeDST(SYSTEMTIME *pNewSystemTime)
{
TIME_ZONE_INFORMATION tzi;
DWORD dwTZ = GetTimeZoneInformation(&tzi);
// If the system supports DST verify that the kernel returned the correct
// value, otherwise make sure the kerel has us in SDT
if (LocaleSupportsDST(&tzi))
{
// All times are in UTC time, not local time. We need to convert to UTC
// to avoid the case where after we roll-back the clock from 2AM->1AM, then
// setting a local time of 1:05AM we couldn't tell whether we're in DST or STD.
// We can compute this exactly by comparing against the UTC.
SYSTEMTIME curTime;
LONGLONG llStandard = 0, llDaylight = 0, llNow = 0;
if (pNewSystemTime)
memcpy(&curTime,pNewSystemTime,sizeof(SYSTEMTIME));
else
GetSystemTime(&curTime);
//fix up the date structs if necessary
if (0 == tzi.StandardDate.wYear)
DST_DetermineChangeDate(&tzi.StandardDate, TRUE);
if (0 == tzi.DaylightDate.wYear)
DST_DetermineChangeDate(&tzi.DaylightDate, TRUE);
//convert so we can do the math
VERIFY(SystemTimeToFileTime(&tzi.StandardDate, (FILETIME *)&llStandard));
VERIFY(SystemTimeToFileTime(&tzi.DaylightDate, (FILETIME *)&llDaylight));
VERIFY(SystemTimeToFileTime(&curTime, (FILETIME *)&llNow));
// TIME_ZONE_INFORMATION values need to be converted to UTC. We convert
// llStandard using the DaylightBias because llStandard is the time in
// DST local clock where the DST->STD conversion is performed.
llStandard = llStandard + (((ULONGLONG)(tzi.Bias+tzi.DaylightBias))*FILETIME_TO_MINUTES);
llDaylight = llDaylight + (((ULONGLONG)(tzi.Bias))*FILETIME_TO_MINUTES);
//the greater difference determines which zone we are in
if (DST_Auto() && ((llDaylight <= llStandard && llDaylight <= llNow && llNow <= llStandard)
|| (llDaylight > llStandard && (llDaylight <= llNow || llNow <= llStandard))))
{
DEBUGMSG(ZONE_DST, (_T("[TIMESVC DST] Notifying kernel that we are in Daylight time. GetTimeZoneInformation currently thinks we are in %s time.\r\n"),
TIME_ZONE_ID_DAYLIGHT == dwTZ ? _T("Daylight") : _T("Standard")));
SetDaylightTime(TRUE);
g_dwTime = TIME_ZONE_ID_DAYLIGHT;
}
else
{
DEBUGMSG(ZONE_DST, (_T("[TIMESVC DST] Notifying kernel that we are in Standard time. GetTimeZoneInformation currently thinks we are in %s time.\r\n"),
TIME_ZONE_ID_DAYLIGHT == dwTZ ? _T("Daylight") : _T("Standard")));
SetDaylightTime(FALSE);
g_dwTime = TIME_ZONE_ID_STANDARD;
}
}
else
{
ASSERT(TIME_ZONE_ID_STANDARD == dwTZ);
if (TIME_ZONE_ID_STANDARD != dwTZ) {
SetDaylightTime(FALSE);
}
g_dwTime = TIME_ZONE_ID_STANDARD;
}
}
/*++
Routine Description:
'main' function for DST. Called from shell init, and re-entered from DST_WaitForEvent.
Arguments:
none
Return Value:
none
--*/
static DWORD WINAPI DST_Init(LPVOID lpUnused)
{
DEBUGMSG(ZONE_INIT, (_T("[TIMESVC DST] System Started...\r\n")));
// Clean up any existing events in the notification database
CeRunAppAtEvent(DSTTIMEZONENOTIFICATION, NOTIFICATION_EVENT_NONE);
CeRunAppAtEvent(DSTTIMENOTIFICATION, NOTIFICATION_EVENT_NONE);
CeRunAppAtTime(DSTNOTIFICATION, 0);
//need to know from the start if we are in standard or daylight time
SetDaylightOrStandardTimeDST(NULL);
for (;;)
{
HANDLE hDSTEvents[NUMDSTEVENTS];
hDSTEvents[SHUTDOWNEVENT] = g_hDSTShutdownEvent;
hDSTEvents[TZCHANGEEVENT] = DST_SetTimezoneChangeEvent();
hDSTEvents[TIMECHANGEEVENT] = DST_SetTimeChangeEvent();
hDSTEvents[DSTCHANGEEVENT] = DST_SetDSTEvent(); // This can be NULL if we are in a non-DST timezone
if (NULL == hDSTEvents[TZCHANGEEVENT])
{
DEBUGCHK( _T("DST: Failed to set TZ change Event"));
break;
}
if (NULL == hDSTEvents[TIMECHANGEEVENT])
{
DEBUGCHK( _T("DST: Failed to set Time change Event"));
break;
}
if (! DST_WaitForEvents(hDSTEvents))
break;
}
DEBUGMSG(ZONE_INIT, (_T("[TIMESVC DST] Thread exited...\r\n")));
return 0;
}
/*++
Routine Description:
Determines if we should auto-adjust for DST
Arguments:
none
Return Value:
TRUE if we want to auto adjust
FALSE if we don't want to auto adjust
--*/
static BOOL DST_Auto(void)
{
DWORD ret = 0;
HKEY hKey = NULL;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, RK_CLOCK, 0, 0, &hKey))
{
DWORD dwSize = sizeof(DWORD);
RegQueryValueEx(hKey, RV_AUTODST, 0, NULL, (LPBYTE)&ret, &dwSize);
RegCloseKey(hKey);
}
#ifdef DEBUG
if (ret)
{
DEBUGMSG(ZONE_DST, ( _T("[TIMESVC DST] Auto change for DST is on\r\n")));
}
else
{
DEBUGMSG(ZONE_DST, ( _T("[TIMESVC DST] Auto change for DST is off\r\n")));
}
#endif //DEBUG
return (BOOL)ret;
}
/*++
Routine Description:
Sets Event to be notified when timezone changes
Arguments:
none
Return Value:
HANDLE to timezone event
--*/
static HANDLE DST_SetTimezoneChangeEvent(void)
{
HANDLE hTZEvent = CreateEvent(NULL, FALSE, FALSE, DSTTZEVENT);
if (hTZEvent)
{
if (!CeRunAppAtEvent(DSTTIMEZONENOTIFICATION,
NOTIFICATION_EVENT_TZ_CHANGE))
{
CloseHandle(hTZEvent);
hTZEvent = NULL;
}
}
return hTZEvent;
}
/*++
Routine Description:
Sets Event to be notified when time changes
Arguments:
none
Return Value:
HANDLE to timezone event
--*/
static HANDLE DST_SetTimeChangeEvent(void)
{
HANDLE hTimeEvent = CreateEvent(NULL, FALSE, FALSE, DSTTIMEEVENT);
if (hTimeEvent)
{
if (!CeRunAppAtEvent(DSTTIMENOTIFICATION,
NOTIFICATION_EVENT_TIME_CHANGE))
{
CloseHandle(hTimeEvent);
hTimeEvent = NULL;
}
}
return hTimeEvent;
}
/*++
Routine Description:
Sets the DST Event
Arguments:
none
Return Value:
Handle to event to be triggered when daylight time turns on (or off)
--*/
static HANDLE DST_SetDSTEvent (void)
{
HANDLE hEvent = NULL;
BOOL bEvent = FALSE;
TIME_ZONE_INFORMATION tzi = {0};
SYSTEMTIME curTime, notificationTime;
LONGLONG llNow = 0, llNotification = 0;
DWORD dwTZ = GetTimeZoneInformation(&tzi);
// Only create the event if the locale supports DST
if (!LocaleSupportsDST(&tzi))
return NULL;
switch (g_dwTime)
{
case TIME_ZONE_ID_DAYLIGHT :
DEBUGMSG(ZONE_DST, ( _T("[TIMESVC DST] Currently in Daylight Time\r\n")));
notificationTime = tzi.StandardDate;
break;
case TIME_ZONE_ID_STANDARD :
DEBUGMSG(ZONE_DST, ( _T("[TIMESVC DST] Currently in Standard Time\r\n")));
notificationTime = tzi.DaylightDate;
break;
default :
DEBUGMSG(ZONE_DST, (_T("[TIMESVC DST] Unknown TimeZone\r\n")));
ASSERT(TIME_ZONE_ID_UNKNOWN == dwTZ); //otherwise GetTZInfo is busted
return NULL;
}
if (0 == notificationTime.wYear) //no specific date set - do the math
DST_DetermineChangeDate(¬ificationTime);
//only set events that are in the future
VERIFY(SystemTimeToFileTime(¬ificationTime, (FILETIME *)&llNotification));
GetLocalTime(&curTime);
VERIFY(SystemTimeToFileTime(&curTime, (FILETIME *)&llNow));
if (llNotification > llNow)
{
hEvent = CreateEvent(NULL, FALSE, FALSE, DSTEVENT);
if (hEvent != INVALID_HANDLE_VALUE)
{
if (!CeRunAppAtTime(DSTNOTIFICATION, ¬ificationTime))
{
CloseHandle(hEvent);
hEvent = NULL;
}
}
DEBUGMSG(ZONE_DST, (_T("[TIMESVC DST] Set TimeChange Event for %d/%d/%02d at %d:%02d\r\n"),
notificationTime.wMonth, notificationTime.wDay, notificationTime.wYear,
notificationTime.wHour, notificationTime.wMinute));
}
return hEvent;
}
/*++
Routine Description:
waits for DST event to be signaled, and optionally re-sets the
time as appropriate.
Arguments:
hEvent - handle to DST event
Return Value:
none
--*/
static int DST_WaitForEvents(LPHANDLE DST_handles)
{
// The last event can be NULL if we so check before we give the number of handles
DWORD dwEvent = WaitForMultipleObjects(DST_handles[NUMDSTEVENTS-1] ? NUMDSTEVENTS : NUMDSTEVENTS-1,
DST_handles, FALSE, INFINITE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -