|
|
/*****************************************************************************\
FILE: inettime.cpp
DESCRIPTION: This file contains the code used to display UI allowing the user to control the feature that updates the computer's clock from an internet NTP time server.
BryanSt 3/22/2000 Copyright (C) Microsoft Corp 2000-2000. All rights reserved. \*****************************************************************************/
#include "timedate.h"
#include "inettime.h"
#include "rc.h"
#include <wininet.h>
#include <shlwapi.h>
#include <DSGetDC.h> // For DsGetDcName
#include <help.h> // For IDH_DATETIME_*
#include <Lm.h> // For NetGetJoinInformation() and NETSETUP_JOIN_STATUS
#include <Lmjoin.h> // For NetGetJoinInformation() and NETSETUP_JOIN_STATUS
#include <shlobj.h>
#include <shlobjp.h>
#include <w32timep.h> // For Time APIs
#include <shellp.h>
#include <ccstock.h>
#include <shpriv.h>
#define DECL_CRTFREE
#include <crtfree.h>
#define DISALLOW_Assert // Force to use ASSERT instead of Assert
#define DISALLOW_DebugMsg // Force to use TraceMsg instead of DebugMsg
#include <debug.h>
// Reg Keys and Values
#define SZ_REGKEY_DATETIME TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\DateTime")
#define SZ_REGKEY_DATETIME_SERVERS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers")
#define SZ_REGKEY_W32TIME_SERVICE TEXT("System\\CurrentControlSet\\Services\\W32Time")
#define SZ_REGKEY_W32TIME_SERVICE_NTPCLIENT SZ_REGKEY_W32TIME_SERVICE TEXT("\\TimeProviders\\NtpClient")
#define SZ_REGKEY_W32TIME_SERVICE_PARAMETERS SZ_REGKEY_W32TIME_SERVICE TEXT("\\Parameters")
#define SZ_REGVALUE_INTERNET_FEATURE_AVAILABLE TEXT("Support Internet Time")
#define SZ_REGVALUE_TEST_SIMULATENODC TEXT("TestSimulateNoDC") // Used to simulate home scenarios while we have a domain controller
#define SZ_REGVALUE_W32TIME_SYNCFROMFLAGS TEXT("Type")
#define SZ_REGVALUE_W32TIME_STARTSERVICE TEXT("Start")
#define SZ_REGVALUE_NTPSERVERLIST TEXT("NtpServer") // Was "ManualPeerList" in Whistler for a little while.
#define SZ_DEFAULT_NTP_SERVER TEXT("time.windows.gov")
#define SZ_INDEXTO_CUSTOMHOST TEXT("0")
#define SZ_SERVICE_W32TIME TEXT("w32time")
#define SZ_DIFFERENT_SYNCFREQUENCY TEXT(",0x1")
#define SZ_SYNC_BOTH TEXT("AllSync")
#define SZ_SYNC_DS TEXT("NT5DS")
#define SZ_SYNC_NTP TEXT("NTP")
#define SZ_SYNC_NONE TEXT("NoSync")
#define SYNC_ONCE_PER_WEEK 0x93A80 // This is once a week. (Sync every this many seconds)
// Flags for "System\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient","SyncFromFlags"
#define NCSF_ManualPeerList 0x01 // This means use internet SNTP servers.
#define NCSF_DomainHierarchy 0x02 // This means get the time
// this feature is turned off temporarily until we can get the perf hit reduced.
// The problem is that the call to DsGetDcName() can take up to 15 seconds if
// a domain controller can not be found.
#define FEATURE_INTERNET_TIME TRUE
#define MAX_URL_STRING INTERNET_MAX_URL_LENGTH
#define WMUSER_UPDATED_STATUS_TEXT (WM_USER + 11)
// If we aren't using the new w32timep.h, then define it our selves.
// TODO: Nuke this after \nt\ds\ RIs into main.
#ifndef TimeSyncFlag_SoftResync
#define TimeSyncFlag_ReturnResult 0x02
#define TimeSyncFlag_Rediscover 0x04
#define ResyncResult_Success 0x00
#define ResyncResult_ChangeTooBig 0x04
typedef struct _W32TIME_NTP_PEER_INFO { unsigned __int32 ulSize; unsigned __int32 ulResolveAttempts; unsigned __int64 u64TimeRemaining; unsigned __int64 u64LastSuccessfulSync; unsigned __int32 ulLastSyncError; unsigned __int32 ulLastSyncErrorMsgId; unsigned __int32 ulValidDataCounter; unsigned __int32 ulAuthTypeMsgId; #ifdef MIDL_PASS
[string, unique] wchar_t *wszUniqueName; #else // MIDL_PASS
LPWSTR wszUniqueName; #endif // MIDL_PASS
unsigned char ulMode; unsigned char ulStratum; unsigned char ulReachability; unsigned char ulPeerPollInterval; unsigned char ulHostPollInterval; } W32TIME_NTP_PEER_INFO, *PW32TIME_NTP_PEER_INFO;
typedef struct _W32TIME_NTP_PROVIDER_DATA { unsigned __int32 ulSize; unsigned __int32 ulError; unsigned __int32 ulErrorMsgId; unsigned __int32 cPeerInfo; #ifdef MIDL_PASS
[size_is(cPeerInfo)] #endif // MIDL_PASS
W32TIME_NTP_PEER_INFO *pPeerInfo; } W32TIME_NTP_PROVIDER_DATA, *PW32TIME_NTP_PROVIDER_DATA;
#endif // TimeSyncFlag_SoftResync
//============================================================================================================
// *** Globals ***
//============================================================================================================
const static DWORD aInternetTimeHelpIds[] = { DATETIME_AUTOSETFROMINTERNET, IDH_DATETIME_AUTOSETFROMINTERNET, DATETIME_INTERNET_SERVER_LABLE, IDH_DATETIME_SERVER_EDIT, DATETIME_INTERNET_SERVER_EDIT, IDH_DATETIME_SERVER_EDIT, DATETIME_INFOTEXTTOP, IDH_DATETIME_INFOTEXT, DATETIME_INFOTEXTPROXY, IDH_DATETIME_INFOTEXT,
DATETIME_INTERNET_ERRORTEXT, IDH_DATETIME_INFOTEXT, DATETIME_INTERNET_UPDATENOW, IDH_DATETIME_UPDATEFROMINTERNET,
0, 0 };
#define SZ_HELPFILE_INTERNETTIME TEXT("windows.hlp")
#define HINST_THISDLL g_hInst
BOOL g_fCustomServer; BOOL g_fWriteAccess = FALSE; // Does the user have the ACLs set correctly to change the HKLM setting to turn the service on/off or change the server hostname?
HINSTANCE g_hInstW32Time = NULL;
void _LoadW32Time(void) { if (!g_hInstW32Time) { g_hInstW32Time = LoadLibrary(TEXT("w32time.dll")); } }
HRESULT StrReplaceToken(IN LPCTSTR pszToken, IN LPCTSTR pszReplaceValue, IN LPTSTR pszString, IN DWORD cchSize) { HRESULT hr = S_OK; LPTSTR pszTempLastHalf = NULL; LPTSTR pszNextToken = pszString;
while (0 != (pszNextToken = StrStrI(pszNextToken, pszToken))) { // We found one.
LPTSTR pszPastToken = pszNextToken + lstrlen(pszToken);
Str_SetPtr(&pszTempLastHalf, pszPastToken); // Keep a copy because we will overwrite it.
pszNextToken[0] = 0; // Remove the rest of the string.
StrCatBuff(pszString, pszReplaceValue, cchSize); StrCatBuff(pszString, pszTempLastHalf, cchSize);
pszNextToken += lstrlen(pszReplaceValue); }
Str_SetPtr(&pszTempLastHalf, NULL);
return hr; }
typedef DWORD (* PFNW32TimeSyncNow) (IN LPCWSTR pwszServer, IN ULONG ulWaitFlag, IN ULONG ulFlags); typedef DWORD (* PFNW32TimeQueryNTPProviderStatus) (IN LPCWSTR pwszServer, IN DWORD dwFlags, IN LPWSTR pwszProvider, OUT W32TIME_NTP_PROVIDER_DATA **ppNTPProviderData); typedef void (* PFNW32TimeBufferFree) (IN LPVOID pvBuffer); typedef HRESULT (* PFNW32TimeQueryConfig) (IN DWORD dwProperty, OUT DWORD * pdwType, IN OUT BYTE * pbConfig, IN OUT DWORD * pdwSize); typedef HRESULT (* PFNW32TimeSetConfig) (IN DWORD dwProperty, IN DWORD dwType, IN BYTE * pbConfig, IN DWORD dwSize);
DWORD W32TimeSyncNowDDLoad(IN LPCWSTR pwszServer, IN ULONG ulWaitFlag, IN ULONG ulFlags) { DWORD dwReturn = ERROR_UNEXP_NET_ERR;
// Hand delay load until DS RIs.
_LoadW32Time(); if (g_hInstW32Time) { PFNW32TimeSyncNow pfnFunction = (PFNW32TimeSyncNow) GetProcAddress(g_hInstW32Time, "W32TimeSyncNow");
if (pfnFunction) { dwReturn = pfnFunction(pwszServer, ulWaitFlag, ulFlags); } }
return dwReturn; }
DWORD W32TimeQueryNTPProviderStatusDDload(IN LPCWSTR pwszServer, IN DWORD dwFlags, IN LPWSTR pwszProvider, OUT W32TIME_NTP_PROVIDER_DATA **ppNTPProviderData) { DWORD dwReturn = ERROR_UNEXP_NET_ERR;
*ppNTPProviderData = NULL;
// Hand delay load until DS RIs.
_LoadW32Time(); if (g_hInstW32Time) { PFNW32TimeQueryNTPProviderStatus pfnFunction = (PFNW32TimeQueryNTPProviderStatus) GetProcAddress(g_hInstW32Time, "W32TimeQueryNTPProviderStatus");
if (pfnFunction) { dwReturn = pfnFunction(pwszServer, dwFlags, pwszProvider, ppNTPProviderData); } }
return dwReturn; }
void W32TimeBufferFreeDDload(IN LPVOID pvBuffer) { // Hand delay load until DS RIs.
_LoadW32Time(); if (g_hInstW32Time) { PFNW32TimeBufferFree pfnFunction = (PFNW32TimeBufferFree) GetProcAddress(g_hInstW32Time, "W32TimeBufferFree");
if (pfnFunction) { pfnFunction(pvBuffer); } } }
HRESULT W32TimeQueryConfigDDload(IN DWORD dwProperty, OUT DWORD * pdwType, IN OUT BYTE * pbConfig, IN OUT DWORD * pdwSize) { HRESULT hr = ResultFromWin32(ERROR_PROC_NOT_FOUND);
// Hand delay load until DS RIs.
_LoadW32Time(); if (g_hInstW32Time) { PFNW32TimeQueryConfig pfnFunction = (PFNW32TimeQueryConfig) GetProcAddress(g_hInstW32Time, "W32TimeQueryConfig");
if (pfnFunction) { hr = pfnFunction(dwProperty, pdwType, pbConfig, pdwSize); } }
return hr; }
HRESULT W32TimeSetConfigDDload(IN DWORD dwProperty, IN DWORD dwType, IN BYTE * pbConfig, IN DWORD dwSize) { HRESULT hr = ResultFromWin32(ERROR_PROC_NOT_FOUND);
// Hand delay load until DS RIs.
_LoadW32Time(); if (g_hInstW32Time) { PFNW32TimeSetConfig pfnFunction = (PFNW32TimeSetConfig) GetProcAddress(g_hInstW32Time, "W32TimeSetConfig");
if (pfnFunction) { hr = pfnFunction(dwProperty, dwType, pbConfig, dwSize); } }
return hr; }
// Turn this on when vbl1 RIs and w32timep.h contains W32TimeQueryConfig, W32TimeSetConfig
//#define W32TIME_NEWAPIS
HRESULT GetW32TimeServer(BOOL fRemoveJunk, LPWSTR pszServer, DWORD cchSize) { DWORD dwType = REG_SZ; DWORD cbSize = (sizeof(pszServer[0]) * cchSize); HRESULT hr = W32TimeQueryConfigDDload(W32TIME_CONFIG_MANUAL_PEER_LIST, &dwType, (BYTE *) pszServer, &cbSize);
if (ResultFromWin32(ERROR_PROC_NOT_FOUND) == hr) { DWORD dwError = SHGetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_NTPSERVERLIST, NULL, (BYTE *)pszServer, &cbSize);
hr = ResultFromWin32(dwError); }
if (SUCCEEDED(hr) && fRemoveJunk) { LPWSTR pszJunk = StrStr(pszServer, L",0x");
if (pszJunk) { pszJunk[0] = 0; }
pszJunk = StrStr(pszServer, L" "); if (pszJunk) { pszJunk[0] = 0; } }
return hr; }
HRESULT RemoveDuplicateServers(LPWSTR pszServer, DWORD cchSize) { TCHAR szResults[MAX_URL_STRING];
StrCpyN(szResults, pszServer, ARRAYSIZE(szResults));
LPTSTR pszBeginning = szResults; LPTSTR pszEnd; while (NULL != (pszEnd = StrChr(pszBeginning, TEXT(' ')))) { TCHAR szSearchStr[MAX_PATH];
StrCpyN(szSearchStr, pszBeginning, (DWORD)min(ARRAYSIZE(szSearchStr), (pszEnd - pszBeginning + 1)));
pszEnd++; // Skip past the space
StrCatBuff(szSearchStr, L" ", ARRAYSIZE(szSearchStr)); StrReplaceToken(szSearchStr, TEXT(""), pszEnd, (ARRAYSIZE(szResults) - (DWORD)(pszEnd - pszBeginning))); szSearchStr[lstrlen(szSearchStr)-1] = 0; StrReplaceToken(szSearchStr, TEXT(""), pszEnd, (ARRAYSIZE(szResults) - (DWORD)(pszEnd - pszBeginning)));
pszBeginning = pszEnd; }
PathRemoveBlanks(szResults);
StrCpyN(pszServer, szResults, cchSize); return S_OK; }
BOOL ComparePeers(LPTSTR szPeer1, LPTSTR szPeer2) { BOOL fResult; LPTSTR szFlags1; LPTSTR szFlags2; TCHAR szSave1; TCHAR szSave2;
szFlags1 = StrChr(szPeer1, TEXT(',')); if (NULL == szFlags1) { szFlags1 = StrChr(szPeer1, TEXT(' ')); } szFlags2 = StrChr(szPeer2, TEXT(',')); if (NULL == szFlags2) { szFlags2 = StrChr(szPeer2, TEXT(' ')); }
if (NULL != szFlags1) { szSave1 = szFlags1[0]; szFlags1[0] = TEXT('\0'); } if (NULL != szFlags2) { szSave2 = szFlags2[0]; szFlags2[0] = TEXT('\0'); }
fResult = 0 == StrCmpI(szPeer1, szPeer2);
if (NULL != szFlags1) { szFlags1[0] = szSave1; } if (NULL != szFlags2) { szFlags2[0] = szSave2; }
return fResult; }
BOOL ContainsServer(LPWSTR pwszServerList, LPWSTR pwszServer) { DWORD dwNextServerOffset = 0; LPWSTR pwszNext = pwszServerList;
while (NULL != pwszNext) { pwszNext += dwNextServerOffset; if (ComparePeers(pwszNext, pwszServer)) { return TRUE; }
pwszNext = StrChr(pwszNext, TEXT(' ')); dwNextServerOffset = 1; }
return FALSE; }
HRESULT AddTerminators(LPWSTR pszServer, DWORD cchSize) { TCHAR szServer[MAX_URL_STRING]; TCHAR szTemp[MAX_URL_STRING];
szTemp[0] = 0; StrCpyN(szServer, pszServer, ARRAYSIZE(szServer));
LPTSTR pszBeginning = szServer; LPTSTR pszEnd; while (NULL != (pszEnd = StrChr(pszBeginning, TEXT(' ')))) { TCHAR szTemp2[MAX_PATH];
pszEnd[0] = 0;
if (!StrStrI(pszBeginning, L",0x")) { wnsprintf(szTemp2, ARRAYSIZE(szTemp2), TEXT("%s,0x1 "), pszBeginning); StrCatBuff(szTemp, szTemp2, ARRAYSIZE(szTemp)); } else { StrCatBuff(szTemp, pszBeginning, ARRAYSIZE(szTemp)); StrCatBuff(szTemp, L" ", ARRAYSIZE(szTemp)); }
pszBeginning = &pszEnd[1]; }
StrCatBuff(szTemp, pszBeginning, ARRAYSIZE(szTemp)); if (pszBeginning[0] && szTemp[0] && !StrStrI(pszBeginning, L",0x")) { // We need to indicate which kind of sync method
StrCatBuff(szTemp, L",0x1", ARRAYSIZE(szTemp)); }
StrCpyN(pszServer, szTemp, cchSize); return S_OK; }
HRESULT SetW32TimeServer(LPCWSTR pszServer) { TCHAR szServer[MAX_PATH];
StrCpyN(szServer, pszServer, ARRAYSIZE(szServer)); AddTerminators(szServer, ARRAYSIZE(szServer)); RemoveDuplicateServers(szServer, ARRAYSIZE(szServer));
DWORD cbSize = ((lstrlen(szServer) + 1) * sizeof(szServer[0])); HRESULT hr = W32TimeSetConfigDDload(W32TIME_CONFIG_MANUAL_PEER_LIST, REG_SZ, (BYTE *) szServer, cbSize); if (ResultFromWin32(ERROR_PROC_NOT_FOUND) == hr) { DWORD dwError = SHSetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_NTPSERVERLIST, REG_SZ, (BYTE *)szServer, cbSize);
hr = ResultFromWin32(dwError); }
return hr; }
HRESULT SetW32TimeSyncFreq(DWORD dwSyncFreq) { #ifndef W32TIME_NEWAPIS
return S_OK; #else // W32TIME_NEWAPIS
return W32TimeSetConfig(W32TIME_CONFIG_SPECIAL_POLL_INTERVAL, REG_DWORD, (BYTE *) dwSyncFreq, sizeof(dwSyncFreq)); #endif // W32TIME_NEWAPIS
}
enum eBackgroundThreadAction { eBKAWait = 0, // The bkthread should wait for a command
eBKAGetInfo, // The bkthread should get the info on the last sync
eBKAUpdate, // The bkthread should start synching
eBKAUpdating, // The bkthread is synching now
eBKAQuit, // The bkthread should shut down
};
class CInternetTime { public: // Forground methods
BOOL IsInternetTimeAvailable(void); HRESULT AddInternetPage(void);
INT_PTR _AdvancedDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
CInternetTime(HWND hDlg, HWND hwndDate); virtual ~CInternetTime(void);
// Background methods
void AsyncCheck(void);
private:
// Private Member Variables
HWND m_hDlg; // hwnd of parent (property sheet).
HWND m_hwndDate; // Date tab hwnd
HWND m_hwndInternet; // InternetTime tab hwnd
HCURSOR m_hCurOld; // The old cursor while we display the wait cursor
LPTSTR m_pszStatusString; // CROSS-THREAD: This is used to pass between threads.
LPTSTR m_pszNextSyncTime; // CROSS-THREAD: This is used to pass between threads.
eBackgroundThreadAction m_eAction; // CROSS-THREAD: This is used to pass between threads.
// Private Member Functions
// Forground methods
HRESULT _InitAdvancedPage(HWND hDlg); HRESULT _OnUpdateButton(void); HRESULT _OnUpdateStatusString(void); HRESULT _OnSetFeature(HWND hDlg, BOOL fOn); HRESULT _OnToggleFeature(HWND hDlg); HRESULT _GoGetHelp(void); HRESULT _ResetServer(void); HRESULT _PersistInternetTimeSettings(HWND hDlg); HRESULT _StartServiceAndRefresh(BOOL fStartIfOff);
INT_PTR _OnCommandAdvancedPage(HWND hDlg, WPARAM wParam, LPARAM lParam); INT_PTR _OnNotifyAdvancedPage(HWND hDlg, NMHDR * pNMHdr, int idControl);
// Background methods
HRESULT _ProcessBkThreadActions(void); HRESULT _SyncNow(BOOL fOnlyUpdateInfo); HRESULT _CreateW32TimeSuccessErrorString(DWORD dwError, LPTSTR pszString, DWORD cchSize, LPTSTR pszNextSync, DWORD cchNextSyncSize, LPTSTR pszServerToQuery); HRESULT _GetDateTimeString(FILETIME * pftTimeDate, BOOL fDate, LPTSTR pszString, DWORD cchSize); };
CInternetTime * g_pInternetTime = NULL;
// This function is called on the forground thread.
CInternetTime::CInternetTime(HWND hDlg, HWND hwndDate) { m_hDlg = hDlg; m_hwndDate = hwndDate; m_eAction = eBKAGetInfo; m_pszStatusString = NULL; m_hwndInternet = NULL; m_hCurOld = NULL;
m_pszStatusString = NULL; m_pszNextSyncTime = NULL; }
CInternetTime::~CInternetTime() { ENTERCRITICAL; if (m_pszStatusString) { LocalFree(m_pszStatusString); m_pszStatusString = NULL; }
if (m_pszNextSyncTime) { LocalFree(m_pszNextSyncTime); m_pszNextSyncTime = NULL; }
if (m_hCurOld) { SetCursor(m_hCurOld); m_hCurOld = NULL; } LEAVECRITICAL; }
HRESULT FormatMessageWedge(LPCWSTR pwzTemplate, LPWSTR pwzStrOut, DWORD cchSize, ...) { va_list vaParamList; HRESULT hr = S_OK;
va_start(vaParamList, cchSize); if (0 == FormatMessageW(FORMAT_MESSAGE_FROM_STRING, pwzTemplate, 0, 0, pwzStrOut, cchSize, &vaParamList)) { hr = ResultFromLastError(); }
va_end(vaParamList); return hr; }
/////////////////////////////////////////////////////////////////////
// Private Internal Helpers
/////////////////////////////////////////////////////////////////////
// This function is called on the forground thread.
HRESULT CInternetTime::_OnSetFeature(HWND hDlg, BOOL fOn) { HWND hwndOnOffCheckbox = GetDlgItem(m_hwndInternet, DATETIME_AUTOSETFROMINTERNET); HWND hwndServerLable = GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_LABLE); HWND hwndServerEdit = GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_EDIT);
CheckDlgButton(hDlg, DATETIME_AUTOSETFROMINTERNET, (fOn ? BST_CHECKED : BST_UNCHECKED)); EnableWindow(hwndOnOffCheckbox, g_fWriteAccess); EnableWindow(hwndServerLable, (g_fWriteAccess ? fOn : FALSE)); EnableWindow(hwndServerEdit, (g_fWriteAccess ? fOn : FALSE)); EnableWindow(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), (g_fWriteAccess ? fOn : FALSE)); EnableWindow(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_UPDATENOW), (g_fWriteAccess ? fOn : FALSE)); EnableWindow(GetDlgItem(m_hwndInternet, DATETIME_INFOTEXTTOP), (g_fWriteAccess ? fOn : FALSE));
if (fOn) { // If the user just turned on the feature and the editbox is empty,
// replace the text with the default server name.
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
if (!GetWindowText(hwndServerEdit, szServer, ARRAYSIZE(szServer)) || !szServer[0]) { SetWindowText(hwndServerEdit, SZ_DEFAULT_NTP_SERVER); } }
return S_OK; }
// This function is called on the forground thread.
HRESULT CInternetTime::_OnToggleFeature(HWND hDlg) { BOOL fIsFeatureOn = ((BST_CHECKED == IsDlgButtonChecked(hDlg, DATETIME_AUTOSETFROMINTERNET)) ? TRUE : FALSE); PropSheet_Changed(GetParent(hDlg), hDlg); // Say we need to enable the apply button
return _OnSetFeature(hDlg, fIsFeatureOn); }
// This function is called on the forground thread.
HRESULT AddServerList(HWND hwndCombo, HKEY hkey, int nIndex) { TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH]; DWORD cbSizeServer = sizeof(szServer); TCHAR szIndex[MAX_PATH]; DWORD dwType;
wnsprintf(szIndex, ARRAYSIZE(szIndex), TEXT("%d"), nIndex);
DWORD dwError = SHGetValue(hkey, NULL, szIndex, &dwType, (void *)szServer, &cbSizeServer); HRESULT hr = ResultFromWin32(dwError); if (SUCCEEDED(hr)) { LPTSTR pszFlags = StrStr(szServer, SZ_DIFFERENT_SYNCFREQUENCY);
if (pszFlags) { pszFlags[0] = 0; // Remove the flags.
}
dwError = ComboBox_AddString(hwndCombo, szServer); if ((dwError == CB_ERR) || (dwError == CB_ERRSPACE)) { hr = E_FAIL; } }
return hr; }
// This function is called on the forground thread.
HRESULT PopulateComboBox(IN HWND hwndCombo, IN BOOL * pfCustomServer) { HRESULT hr; HKEY hkey;
*pfCustomServer = FALSE;
DWORD dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, 0, KEY_READ, &hkey); hr = ResultFromWin32(dwError); if (SUCCEEDED(hr)) { // Try to add the custom slot.
if (SUCCEEDED(AddServerList(hwndCombo, hkey, 0))) { // We do have an existing custom server, so let the caller know so
// they know to increase the index by 1.
*pfCustomServer = TRUE; }
for (int nIndex = 1; SUCCEEDED(hr); nIndex++) { hr = AddServerList(hwndCombo, hkey, nIndex); } RegCloseKey(hkey); }
return hr; }
// This function is called on the forground thread.
HRESULT CInternetTime::_OnUpdateButton(void) { HRESULT hr = S_OK;
ENTERCRITICAL; // We don't need to do anything if it's already working on another task.
if (eBKAWait == m_eAction) { TCHAR szMessage[2 * MAX_PATH]; TCHAR szTemplate[MAX_PATH]; TCHAR szServer[MAX_PATH];
if (!m_hCurOld) { m_hCurOld = SetCursor(LoadCursor(NULL, IDC_WAIT)); }
LoadString(HINST_THISDLL, IDS_IT_WAITFORSYNC, szTemplate, ARRAYSIZE(szTemplate)); GetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_EDIT), szServer, ARRAYSIZE(szServer)); FormatMessageWedge(szTemplate, szMessage, ARRAYSIZE(szMessage), szServer);
SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), szMessage);
m_eAction = eBKAUpdate; } LEAVECRITICAL;
return hr; }
// This function is called on the forground thread.
BOOL IsManualPeerListOn(void) { BOOL fIsManualPeerListOn = TRUE; TCHAR szSyncType[MAX_PATH]; DWORD cbSize = sizeof(szSyncType); DWORD dwType;
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_W32TIME_SYNCFROMFLAGS, &dwType, (LPBYTE)szSyncType, &cbSize)) { if (!StrCmpI(szSyncType, SZ_SYNC_DS) || !StrCmpI(szSyncType, SZ_SYNC_NONE)) { fIsManualPeerListOn = FALSE; } }
return fIsManualPeerListOn; }
// This function is called on the forground thread.
HRESULT CInternetTime::_InitAdvancedPage(HWND hDlg) { DWORD dwType; TCHAR szIndex[MAX_PATH]; HWND hwndServerEdit = GetDlgItem(hDlg, DATETIME_INTERNET_SERVER_EDIT); HWND hwndOnOffCheckbox = GetDlgItem(hDlg, DATETIME_AUTOSETFROMINTERNET); DWORD cbSize = sizeof(szIndex); BOOL fIsFeatureOn = IsManualPeerListOn();
m_hwndInternet = hDlg; HRESULT hr = PopulateComboBox(hwndServerEdit, &g_fCustomServer);
// Does the user have access to change the setting in the registry?
HKEY hKeyTemp; g_fWriteAccess = FALSE; DWORD dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, 0, KEY_WRITE, &hKeyTemp); if (ERROR_SUCCESS == dwError) { // We have access to read & write so we can enable the UI.
RegCloseKey(hKeyTemp); dwError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyTemp, NULL); if (ERROR_SUCCESS == dwError) { RegCloseKey(hKeyTemp); dwError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyTemp, NULL); if (ERROR_SUCCESS == dwError) { g_fWriteAccess = TRUE; RegCloseKey(hKeyTemp); } } }
dwError = SHGetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, NULL, &dwType, (void *) szIndex, &cbSize); hr = ResultFromWin32(dwError); if (SUCCEEDED(hr)) { int nIndex = StrToInt(szIndex);
if (!g_fCustomServer) { nIndex -= 1; // We don't have slot zero (the custom server), so reduce the index.
}
ComboBox_SetCurSel(hwndServerEdit, nIndex); }
_OnSetFeature(hDlg, fIsFeatureOn);
if (fIsFeatureOn) { // The feature is on, so select the text and set focus
// to the combobox.
ComboBox_SetEditSel(hwndServerEdit, 0, (LPARAM)-1); // Select all the Text
SetFocus(hwndServerEdit); } else { // The feature is off so set focus to the checkbox.
SetFocus(hwndOnOffCheckbox); }
ENTERCRITICAL; if (m_pszStatusString) { SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), m_pszStatusString); }
if (m_pszNextSyncTime) { SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INFOTEXTTOP), m_pszNextSyncTime); } LEAVECRITICAL;
return S_OK; }
// This function is called on the background thread.
HRESULT SetSyncFlags(DWORD dwSyncFromFlags) { LPCTSTR pszFlags = SZ_SYNC_BOTH;
switch (dwSyncFromFlags) { case NCSF_DomainHierarchy: pszFlags = SZ_SYNC_DS; break; case NCSF_ManualPeerList: pszFlags = SZ_SYNC_NTP; break; case 0x00000000: pszFlags = SZ_SYNC_NONE; break; };
DWORD cbSize = ((lstrlen(pszFlags) + 1) * sizeof(pszFlags[0])); DWORD dwError = SHSetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_W32TIME_SYNCFROMFLAGS, REG_SZ, (LPBYTE)pszFlags, cbSize); HRESULT hr = ResultFromWin32(dwError);
return hr; }
/*****************************************************************************\
DESCRIPTION:
The following reg keys determine if the W32Time service is on or off: HKLM,"System\CurrentControlSet\Services\W32Time\" "Start", 0x00000002 (Manual?) 0x0000003 (Automatic?) "Type", 0x00000020 (Nt5Ds?) 0x0000120 (NTP?) HKLM,"System\CurrentControlSet\Services\W32Time\Parameters" "LocalNTP", 0x00000000 (Off) 0x0000001 (On) "Type", "Nt5Ds" (NTP off) "NTP" (NTP?) HKLM,"System\CurrentControlSet\Services\W32Time\TimeProviders\NTPClient" "SyncFromFlags", 0x00000001 (???) 0x0000002 (???) The following reg keys determine which NTP server to use: HKLM,"System\CurrentControlSet\Services\W32Time\TimeProviders\NTPClient" "ManualPeerList", REG_SZ_EXPAND "time.nist.gov" \*****************************************************************************/ // This function is called on the forground thread.
HRESULT CInternetTime::_PersistInternetTimeSettings(HWND hDlg) { BOOL fIsFeatureOn = ((BST_CHECKED == IsDlgButtonChecked(hDlg, DATETIME_AUTOSETFROMINTERNET)) ? TRUE : FALSE); DWORD dwSyncFromFlags = (fIsFeatureOn ? NCSF_ManualPeerList : 0 /*none*/ ); DWORD dwError; DWORD cbSize;
HRESULT hr = SetSyncFlags(dwSyncFromFlags);
HWND hwndServerEdit = GetDlgItem(hDlg, DATETIME_INTERNET_SERVER_EDIT);
// No, so the editbox has a customer server. We need to store the custom server.
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH]; TCHAR szServerReg[INTERNET_MAX_HOST_NAME_LENGTH];
szServer[0] = 0; GetWindowText(hwndServerEdit, szServer, ARRAYSIZE(szServer));
// Here, we want to detect the case where someone typed in the same name as
// in the drop down list. So if "time.nist.gov" is in the list, we want to
// select it instead of saving another "time.nist.gov" in the custom slot.
int nDup = ComboBox_FindString(hwndServerEdit, 0, szServer); if (CB_ERR != nDup) { ComboBox_SetCurSel(hwndServerEdit, nDup); }
// The ",0x1" denotes that we want to sync less frequently and not use the NTP RFC's
// sync frequency.
StrCpyN(szServerReg, szServer, ARRAYSIZE(szServerReg)); StrCatBuff(szServerReg, SZ_DIFFERENT_SYNCFREQUENCY, ARRAYSIZE(szServerReg));
// Write the default server name into "ManualPeerList", which is where the
// service reads it from.
SetW32TimeServer(szServerReg); if (!StrCmpI(szServerReg, L"time.windows.com")) { // We need time.windows.com to scale to a large number of users, so don't let it sync more frequently
// than once a week.
SetW32TimeSyncFreq(SYNC_ONCE_PER_WEEK); }
int nIndex = ComboBox_GetCurSel(hwndServerEdit); if (CB_ERR == nIndex) // Is anything selected?
{ cbSize = ((lstrlenW(szServer) + 1) * sizeof(szServer[0])); dwError = SHSetValueW(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, SZ_INDEXTO_CUSTOMHOST, REG_SZ, (void *)szServer, cbSize);
nIndex = 0; } else { if (!g_fCustomServer) { nIndex += 1; // Push the index down by one because the listbox doesn't have a custom server
} }
TCHAR szIndex[MAX_PATH]; wnsprintf(szIndex, ARRAYSIZE(szIndex), TEXT("%d"), nIndex);
cbSize = ((lstrlenW(szIndex) + 1) * sizeof(szIndex[0])); dwError = SHSetValueW(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, NULL, REG_SZ, (void *)szIndex, cbSize);
_StartServiceAndRefresh(fIsFeatureOn); // Make sure the service is on and make it update it's settings.
return S_OK; }
// This function is called on either the forground or background thread.
HRESULT CInternetTime::_StartServiceAndRefresh(BOOL fStartIfOff) { HRESULT hr = S_OK;
// We need to let the service know that settings may have changed. If the user
// wanted this feature on, then we should make sure the service starts.
SC_HANDLE hServiceMgr = OpenSCManager(NULL, NULL, (SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS)); if (hServiceMgr) { SC_HANDLE hService = OpenService(hServiceMgr, SZ_SERVICE_W32TIME, (/*SERVICE_START |*/ SERVICE_PAUSE_CONTINUE)); DWORD dwServiceStartMode = 0x00000002; // This will cause the service to start Automatically on reboot.
DWORD dwError = SHSetValueW(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE, SZ_REGVALUE_W32TIME_STARTSERVICE, REG_DWORD, &dwServiceStartMode, sizeof(dwServiceStartMode)); if (hService) { SERVICE_STATUS serviceStatus = {0}; BOOL fSucceeded;
if (fStartIfOff) { // We should start up the service in case it's not turned on.
fSucceeded = StartService(hService, 0, NULL); hr = (fSucceeded ? S_OK : ResultFromLastError()); if (ResultFromWin32(ERROR_SERVICE_ALREADY_RUNNING) == hr) { hr = S_OK; // We can ignore this err value because it's benign.
} }
// Tell the service to re-read the registry entry settings.
fSucceeded = ControlService(hService, SERVICE_CONTROL_PARAMCHANGE, &serviceStatus);
CloseServiceHandle(hService); } else { hr = ResultFromLastError(); }
CloseServiceHandle(hServiceMgr); } else { hr = ResultFromLastError(); }
return hr; }
HRESULT CInternetTime::_OnUpdateStatusString(void) { HRESULT hr = S_OK;
ENTERCRITICAL; // Be safe while using m_pszStatusString
SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), (m_pszStatusString ? m_pszStatusString : TEXT(""))); SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INFOTEXTTOP), (m_pszNextSyncTime ? m_pszNextSyncTime : TEXT("")));
if (m_hCurOld) { SetCursor(m_hCurOld); m_hCurOld = NULL; } LEAVECRITICAL;
return S_OK; }
HRESULT HrShellExecute(HWND hwnd, LPCTSTR lpVerb, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd) { HRESULT hr = S_OK; HINSTANCE hReturn = ShellExecute(hwnd, lpVerb, lpFile, lpParameters, lpDirectory, nShowCmd);
if ((HINSTANCE)32 > hReturn) { hr = ResultFromLastError(); }
return hr; }
// This function is called on the forground thread.
HRESULT CInternetTime::_GoGetHelp(void) { HRESULT hr = S_OK; TCHAR szCommand[MAX_PATH];
LoadString(HINST_THISDLL, IDS_TROUBLESHOOT_INTERNETIME, szCommand, ARRAYSIZE(szCommand)); HrShellExecute(m_hwndDate, NULL, szCommand, NULL, NULL, SW_NORMAL);
return S_OK; }
// This function is called on the forground thread.
INT_PTR CInternetTime::_OnCommandAdvancedPage(HWND hDlg, WPARAM wParam, LPARAM lParam) { BOOL fHandled = 1; // Not handled (WM_COMMAND seems to be different)
WORD wMsg = GET_WM_COMMAND_CMD(wParam, lParam); WORD idCtrl = GET_WM_COMMAND_ID(wParam, lParam);
switch (idCtrl) { case DATETIME_AUTOSETFROMINTERNET: switch (wMsg) { case BN_CLICKED: _OnToggleFeature(hDlg); break; } break;
case DATETIME_INTERNET_SERVER_EDIT: switch (wMsg) { case EN_CHANGE: case CBN_EDITCHANGE: case CBN_SELCHANGE: PropSheet_Changed(GetParent(hDlg), hDlg); break; } break;
case DATETIME_INTERNET_UPDATENOW: switch (wMsg) { case BN_CLICKED: _OnUpdateButton(); break; } break; }
return fHandled; }
// This function is called on the forground thread.
INT_PTR CInternetTime::_OnNotifyAdvancedPage(HWND hDlg, NMHDR * pNMHdr, int idControl) { BOOL fHandled = 1; // Not handled (WM_COMMAND seems to be different)
if (pNMHdr) { switch (pNMHdr->idFrom) { case 0: { switch (pNMHdr->code) { case PSN_APPLY: _PersistInternetTimeSettings(hDlg); break; } break; } default: switch (pNMHdr->code) { case NM_RETURN: case NM_CLICK: { PNMLINK pNMLink = (PNMLINK) pNMHdr;
if (!StrCmpW(pNMLink->item.szID, L"HelpMe")) { _GoGetHelp(); } break; } } break; } }
return fHandled; }
// This function is called on the forground thread.
EXTERN_C INT_PTR CALLBACK AdvancedDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (g_pInternetTime) { return g_pInternetTime->_AdvancedDlgProc(hDlg, uMsg, wParam, lParam); }
return (TRUE); }
// This function is called on the forground thread.
INT_PTR CInternetTime::_AdvancedDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: _InitAdvancedPage(hDlg); break;
case WM_DESTROY: break;
case WM_NOTIFY: _OnNotifyAdvancedPage(hDlg, (NMHDR *)lParam, (int) wParam); break;
case WM_COMMAND: _OnCommandAdvancedPage(hDlg, wParam, lParam); break;
case WMUSER_UPDATED_STATUS_TEXT: _OnUpdateStatusString(); break;
case WM_HELP: WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, SZ_HELPFILE_INTERNETTIME, HELP_WM_HELP, (DWORD_PTR) aInternetTimeHelpIds); break;
case WM_CONTEXTMENU: // right mouse click
WinHelp((HWND) wParam, SZ_HELPFILE_INTERNETTIME, HELP_CONTEXTMENU, (DWORD_PTR) aInternetTimeHelpIds); break;
default: return FALSE; }
return (TRUE); }
// This function is called on the forground thread.
HRESULT CInternetTime::AddInternetPage(void) { HRESULT hr = S_OK; PROPSHEETPAGE pspAdvanced; INITCOMMONCONTROLSEX initComctl32;
initComctl32.dwSize = sizeof(initComctl32); initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
InitCommonControlsEx(&initComctl32); // Register the comctl32 LinkWindow
pspAdvanced.dwSize = sizeof(PROPSHEETPAGE); pspAdvanced.dwFlags = PSP_DEFAULT; pspAdvanced.hInstance = HINST_THISDLL; pspAdvanced.pszTemplate = MAKEINTRESOURCE(DLG_ADVANCED); pspAdvanced.pfnDlgProc = AdvancedDlgProc; pspAdvanced.lParam = (LPARAM) this;
if (IsWindow(m_hDlg)) { HPROPSHEETPAGE hPropSheet = CreatePropertySheetPage(&pspAdvanced); if (hPropSheet) { PropSheet_AddPage(m_hDlg, hPropSheet); } }
return hr; }
/////////////////////////////////////////////////////////////////////
// Background Thread Functions
/////////////////////////////////////////////////////////////////////
///////////
// These functions will cause the background thread to sync
///////////
#define SECONDS_FROM_100NS 10000000
// ulResolveAttempts -- the number of times the NTP provider has attempted to
// resolve this peer unsuccessfully. Setting this
// value to 0 indicates that the peer has been successfully
// resolved.
// u64TimeRemaining -- the number of 100ns intervals until the provider will
// poll this peer again
// u64LastSuccessfulSync -- the number of 100ns intervals since (0h 1-Jan 1601)
// ulLastSyncError -- S_OK if the last sync with this peer was successful, otherwise,
// the error that occurred attempting to sync
// ulLastSyncErrorMsgId -- the resource identifier of a string representing the last
// error that occurred syncing from this peer. 0 if there is no
// string associated with this error.
// This function is called on the background thread.
HRESULT W32TimeGetErrorInfoWrap(UINT * pulResolveAttempts, ULONG * pulValidDataCounter, UINT64 * pu64TimeRemaining, UINT64 * pu64LastSuccessfulSync, HRESULT * phrLastSyncError, UINT * pulLastSyncErrorMsgId, LPTSTR pszServer, DWORD cchSize, LPTSTR pszServerToQuery) { HRESULT hr = S_OK;
*pulResolveAttempts = 0; *pulValidDataCounter = 0; *pu64TimeRemaining = 0; *pu64LastSuccessfulSync = 0; *phrLastSyncError = E_FAIL; *pulLastSyncErrorMsgId = 0; pszServer[0] = 0;
// NOTE: Server should return ERROR_TIME_SKEW if time is too far out of date to sync.
W32TIME_NTP_PROVIDER_DATA * pProviderInfo = NULL; DWORD dwError = W32TimeQueryNTPProviderStatusDDload(SZ_COMPUTER_LOCAL, 0, SZ_NTPCLIENT, &pProviderInfo); if ((ERROR_SUCCESS == dwError) && pProviderInfo) { *phrLastSyncError = pProviderInfo->ulError; *pulLastSyncErrorMsgId = pProviderInfo->ulErrorMsgId;
DWORD dwMostRecentlySyncdPeerIndex = 0xFFFFFFFF; UINT64 u64LastSuccessfulSync = 0; for (DWORD dwIndex = 0; dwIndex < pProviderInfo->cPeerInfo; dwIndex++) { // Only want to query those peers which we explictly sync'd from.
// If we haven't explicitly requested a sync, we'll just take the most recently sync'd peer.
if (NULL == pszServerToQuery || ComparePeers(pszServerToQuery, pProviderInfo->pPeerInfo[dwIndex].wszUniqueName)) { if (u64LastSuccessfulSync <= pProviderInfo->pPeerInfo[dwIndex].u64LastSuccessfulSync) { dwMostRecentlySyncdPeerIndex = dwIndex; u64LastSuccessfulSync = pProviderInfo->pPeerInfo[dwIndex].u64LastSuccessfulSync; } } }
if (dwMostRecentlySyncdPeerIndex < pProviderInfo->cPeerInfo && pProviderInfo->pPeerInfo) { *pulResolveAttempts = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulResolveAttempts; *pulValidDataCounter = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulValidDataCounter; *pu64TimeRemaining = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].u64TimeRemaining; *pu64LastSuccessfulSync = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].u64LastSuccessfulSync; *phrLastSyncError = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulLastSyncError; *pulLastSyncErrorMsgId = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulLastSyncErrorMsgId;
if (pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].wszUniqueName) { StrCpyN(pszServer, pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].wszUniqueName, cchSize); // Strip off non-user friendly information which may have been tacked on to the peer name:
LPTSTR pszJunk = StrStrW(pszServer, L" ("); if (pszJunk) { pszJunk[0] = 0; } pszJunk = StrStrW(pszServer, L","); if (pszJunk) { pszJunk[0] = 0; } } } else { *phrLastSyncError = HRESULT_FROM_WIN32(ERROR_TIMEOUT); if (NULL != pszServerToQuery) { StrCpyN(pszServer, pszServerToQuery, cchSize); } }
W32TimeBufferFreeDDload(pProviderInfo); } else { hr = ResultFromWin32(dwError); }
return hr; }
// pftTimeRemaining will be returned with the time/date of the next sync, not time from now.
HRESULT W32TimeGetErrorInfoWrapHelper(UINT * pulResolveAttempts, ULONG * pulValidDataCounter, FILETIME * pftTimeRemaining, FILETIME * pftLastSuccessfulSync, HRESULT * phrLastSyncError, UINT * pulLastSyncErrorMsgId, LPTSTR pszServer, DWORD cchSize, LPTSTR pszServerToQuery) { UINT64 * pu64LastSuccessfulSync = (UINT64 *) pftLastSuccessfulSync; UINT64 u64TimeRemaining; HRESULT hr = W32TimeGetErrorInfoWrap(pulResolveAttempts, pulValidDataCounter, &u64TimeRemaining, pu64LastSuccessfulSync, phrLastSyncError, pulLastSyncErrorMsgId, pszServer, cchSize, pszServerToQuery);
if (SUCCEEDED(hr)) { SYSTEMTIME stCurrent; FILETIME ftCurrent;
GetSystemTime(&stCurrent); SystemTimeToFileTime(&stCurrent, &ftCurrent);
ULONGLONG * pNextSync = (ULONGLONG *) pftTimeRemaining; ULONGLONG * pCurrent = (ULONGLONG *) &ftCurrent; *pNextSync = (*pCurrent + u64TimeRemaining); }
return hr; }
// This function is called on the background thread.
HRESULT CInternetTime::_GetDateTimeString(FILETIME * pftTimeDate, BOOL fDate, LPTSTR pszString, DWORD cchSize) { HRESULT hr = S_OK; TCHAR szFormat[MAX_PATH];
pszString[0] = 0; if (GetLocaleInfo(LOCALE_USER_DEFAULT, (fDate ? LOCALE_SSHORTDATE : LOCALE_STIMEFORMAT), szFormat, ARRAYSIZE(szFormat))) { SYSTEMTIME stTimeDate;
if (FileTimeToSystemTime(pftTimeDate, &stTimeDate)) { if (fDate) { if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, &stTimeDate, szFormat, pszString, cchSize)) { hr = ResultFromLastError(); } } else { if (!GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &stTimeDate, szFormat, pszString, cchSize)) { hr = ResultFromLastError(); } } } else { hr = ResultFromLastError(); } } else { hr = ResultFromLastError(); }
return hr; }
HRESULT _CleanUpErrorString(LPWSTR pszTemp4, DWORD cchSize) { PathRemoveBlanks(pszTemp4); while (TRUE) { DWORD cchSizeTemp = lstrlen(pszTemp4); if (cchSizeTemp && ((TEXT('\n') == pszTemp4[cchSizeTemp-1]) || (13 == pszTemp4[cchSizeTemp-1]))) { pszTemp4[cchSizeTemp-1] = 0; } else { break; } }
return S_OK; }
// This function is called on the background thread.
HRESULT CInternetTime::_CreateW32TimeSuccessErrorString(DWORD dwError, LPTSTR pszString, DWORD cchSize, LPTSTR pszNextSync, DWORD cchNextSyncSize, LPTSTR pszServerToQuery) { HRESULT hr = S_OK; UINT ulResolveAttempts = 0; FILETIME ftTimeRemainingUTC = {0}; FILETIME ftLastSuccessfulSyncUTC = {0}; FILETIME ftTimeRemaining = {0}; FILETIME ftLastSuccessfulSync = {0}; UINT ulLastSyncErrorMsgId = 0; TCHAR szServer[MAX_PATH]; HRESULT hrLastSyncError = 0; ULONG ulValidDataCounter = 0; TCHAR szTemplate[MAX_PATH]; TCHAR szTemp1[MAX_PATH]; TCHAR szTemp2[MAX_PATH]; TCHAR szTemp3[MAX_URL_STRING]; TCHAR szTemp4[MAX_URL_STRING];
pszString[0] = 0; pszNextSync[0] = 0;
hr = W32TimeGetErrorInfoWrapHelper(&ulResolveAttempts, &ulValidDataCounter, &ftTimeRemainingUTC, &ftLastSuccessfulSyncUTC, &hrLastSyncError, &ulLastSyncErrorMsgId, szServer, ARRAYSIZE(szServer), pszServerToQuery); if (SUCCEEDED(hr)) { // ftTimeRemaining and ftLastSuccessfulSync are stored in UTC, so translate to our time zone.
FileTimeToLocalFileTime(&ftTimeRemainingUTC, &ftTimeRemaining); FileTimeToLocalFileTime(&ftLastSuccessfulSyncUTC, &ftLastSuccessfulSync);
// Create the string showing the next time we plan on syncing.
LoadString(HINST_THISDLL, IDS_IT_NEXTSYNC, szTemplate, ARRAYSIZE(szTemplate)); if (SUCCEEDED(_GetDateTimeString(&ftTimeRemaining, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
SUCCEEDED(_GetDateTimeString(&ftTimeRemaining, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
FAILED(FormatMessageWedge(szTemplate, pszNextSync, cchNextSyncSize, szTemp1, szTemp2))) { pszNextSync[0] = 0; }
if (ResyncResult_ChangeTooBig == dwError) { hrLastSyncError = E_FAIL; }
if ((ResyncResult_NoData == dwError || ResyncResult_StaleData == dwError) && SUCCEEDED(hrLastSyncError)) { // We've synchronized from our peer, but it didn't provide good enough samples to update our clock.
hrLastSyncError = HRESULT_FROM_WIN32(ERROR_TIMEOUT); // approximately the right error
}
// We should never hit the following case. But if the operation failed (NOT ResyncResult_Success)
// then we need hrLastSyncError to be a failure value.
if ((ResyncResult_Success != dwError) && SUCCEEDED(hrLastSyncError)) { hrLastSyncError = E_FAIL; }
switch (hrLastSyncError) { case S_OK: if (!ftLastSuccessfulSyncUTC.dwLowDateTime && !ftLastSuccessfulSyncUTC.dwHighDateTime) { // We have never sync from the server.
LoadString(HINST_THISDLL, IDS_NEVER_TRIED_TOSYNC, pszString, cchSize); } else { if (szServer[0]) { // Format: "Successfully synchronized the clock on 12/23/2001 at 11:03:32am from time.windows.com."
LoadString(HINST_THISDLL, IDS_IT_SUCCESS, szTemplate, ARRAYSIZE(szTemplate));
if (SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szTemp1, szTemp2, szServer))) { pszString[0] = 0; } } else { // Format: "Successfully synchronized the clock on 12/23/2001 at 11:03:32am."
LoadString(HINST_THISDLL, IDS_IT_SUCCESS2, szTemplate, ARRAYSIZE(szTemplate));
if (SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szTemp1, szTemp2))) { pszString[0] = 0; } } } break;
case S_FALSE: pszString[0] = 0; break;
default: if (ulValidDataCounter && ((0 != ftLastSuccessfulSyncUTC.dwLowDateTime) || (0 != ftLastSuccessfulSyncUTC.dwHighDateTime))) { // Getting this far means we may have failed this last sync attempt, but we have succeeded
// previously
hr = E_FAIL; szTemp4[0] = 0; // This will be the error message.
szTemp3[0] = 0;
if (ResyncResult_ChangeTooBig == dwError) { // If the date is too far off, we fail the sync for security reasons. That happened
// here.
LoadString(HINST_THISDLL, IDS_ERR_DATETOOWRONG, szTemp4, ARRAYSIZE(szTemp4)); } else if (ulLastSyncErrorMsgId) // We have a bonus error string.
{ _LoadW32Time(); if (g_hInstW32Time) { // Load the specific reason the sync failed.
if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)g_hInstW32Time, ulLastSyncErrorMsgId, 0, szTemp4, ARRAYSIZE(szTemp4), NULL)) { szTemp4[0] = 0; // We will get the value below
hr = S_OK; } else { _CleanUpErrorString(szTemp4, ARRAYSIZE(szTemp4)); } } }
if (!szTemp4[0]) { if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_CODE(hrLastSyncError), 0, szTemp4, ARRAYSIZE(szTemp4), NULL)) { _CleanUpErrorString(szTemp4, ARRAYSIZE(szTemp4)); } else { szTemp4[0] = 0; } }
if (szTemp4[0]) { LoadString(HINST_THISDLL, IDS_IT_FAIL1, szTemplate, ARRAYSIZE(szTemplate)); if (FAILED(FormatMessageWedge(szTemplate, szTemp3, ARRAYSIZE(szTemp3), szServer, szTemp4))) { szTemp3[0] = 0; } } else { // <------------------------------------------------------------->
// Format:
// "An error occurred synchronizing the clock from time.windows.com
// on 2/3/2001 at 11:03:32am. Unable to connect to the server."
LoadString(HINST_THISDLL, IDS_IT_FAIL2, szTemplate, ARRAYSIZE(szTemplate)); if (FAILED(FormatMessageWedge(szTemplate, szTemp3, ARRAYSIZE(szTemp3), szServer))) { szTemp3[0] = 0; }
hr = S_OK; }
LoadString(HINST_THISDLL, IDS_IT_FAILLAST, szTemplate, ARRAYSIZE(szTemplate));
if (SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
FAILED(FormatMessageWedge(szTemplate, szTemp4, ARRAYSIZE(szTemp4), szTemp1, szTemp2))) { szTemp4[0] = 0; }
wnsprintf(pszString, cchSize, TEXT("%s\n\n%s"), szTemp3, szTemp4); } else { // Getting this far means we may have failed to sync this time and we have never succeeded before.
if (ulLastSyncErrorMsgId) // We have a bonus error string.
{ _LoadW32Time();
szTemp3[0] = 0; if (g_hInstW32Time) { // <------------------------------------------------------------->
// Format:
// "An error occurred synchronizing the clock from time.windows.com
// on 2/3/2001 at 11:03:32am. Unable to connect to the server."
if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)g_hInstW32Time, ulLastSyncErrorMsgId, 0, szTemp4, ARRAYSIZE(szTemp4), NULL)) { szTemp4[0] = 0; } else { _CleanUpErrorString(szTemp4, ARRAYSIZE(szTemp4)); }
if (szServer[0]) { LoadString(HINST_THISDLL, IDS_IT_FAIL1, szTemplate, ARRAYSIZE(szTemplate)); if (FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szServer, szTemp4))) { pszString[0] = 0; } } else { LoadString(HINST_THISDLL, IDS_IT_FAIL3, szTemplate, ARRAYSIZE(szTemplate)); if (FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szTemp4))) { pszString[0] = 0; } } } } else { // <------------------------------------------------------------->
// Format:
// "An error occurred synchronizing the clock from time.windows.com
// on 2/3/2001 at 11:03:32am. Unable to connect to the server."
LoadString(HINST_THISDLL, IDS_IT_FAIL2, szTemplate, ARRAYSIZE(szTemplate)); if (FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szServer))) { pszString[0] = 0; } } } break; }; } else { LoadString(HINST_THISDLL, IDS_ERR_GETINFO_FAIL, szTemplate, ARRAYSIZE(szTemplate)); if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_CODE(hr), 0, szTemp1, ARRAYSIZE(szTemp1), NULL)) { szTemp1[0] = 0; } else { _CleanUpErrorString(szTemp1, ARRAYSIZE(szTemp1)); }
wnsprintf(pszString, cchSize, szTemplate, szTemp1); }
return hr; }
///////////
// These functions will cause the background thread to check if we should add the "Internet Time" tab.
///////////
// This function is called on the background thread.
DWORD CALLBACK _AsyncCheckDCThread(void * pv) { if (g_pInternetTime) { g_pInternetTime->AsyncCheck(); }
return 0; }
typedef DWORD (* PFN_NETGETJOININFORMATION) (IN LPCWSTR pszComputerName OPTIONAL, OUT LPWSTR * pszDomainName, IN PNETSETUP_JOIN_STATUS pJoinStatus);
// This function is called on the background thread.
DWORD NetGetJoinInformation_DelayLoad( IN LPCWSTR pszComputerName OPTIONAL, OUT LPWSTR * pszDomainName, IN PNETSETUP_JOIN_STATUS pJoinStatus) { DWORD dwError = ERROR_INVALID_FUNCTION; HMODULE hmod = LoadLibrary(TEXT("NETAPI32.DLL"));
if (hmod) { PFN_NETGETJOININFORMATION pfnDelayLoad = (PFN_NETGETJOININFORMATION)GetProcAddress(hmod, "NetGetJoinInformation"); if (pfnDelayLoad) { dwError = pfnDelayLoad(pszComputerName, pszDomainName, pJoinStatus); }
FreeLibrary(hmod); } else { dwError = GetLastError(); }
return dwError; }
typedef DWORD (* PFN_DSGETDCNAME) (IN LPCTSTR ComputerName OPTIONAL, IN LPCTSTR DomainName OPTIONAL, IN GUID *DomainGuid OPTIONAL, IN LPCTSTR SiteName OPTIONAL, IN ULONG Flags, OUT PDOMAIN_CONTROLLER_INFO * DomainControllerInfo);
// This function is called on the background thread.
DWORD DsGetDcName_DelayLoad( IN LPCWSTR pszComputerName OPTIONAL, IN LPCWSTR pszDomainName OPTIONAL, IN GUID * pDomainGuid OPTIONAL, IN LPCWSTR pszSiteName OPTIONAL, IN ULONG pulFlags, OUT PDOMAIN_CONTROLLER_INFOW * pDomainControllerInfo) { DWORD dwError = ERROR_INVALID_FUNCTION; HMODULE hmod = LoadLibrary(TEXT("NETAPI32.DLL"));
if (hmod) { PFN_DSGETDCNAME pfnDelayLoad = (PFN_DSGETDCNAME)GetProcAddress(hmod, "DsGetDcNameW"); if (pfnDelayLoad) { dwError = pfnDelayLoad(pszComputerName, pszDomainName, pDomainGuid, pszSiteName, pulFlags, pDomainControllerInfo); }
FreeLibrary(hmod); } else { dwError = GetLastError(); }
return dwError; }
typedef DWORD (* PFN_NETAPIBUFFERFREE) (IN LPVOID Buffer);
// This function is called on the background thread.
DWORD NetApiBufferFree_DelayLoad(IN LPVOID Buffer) { DWORD dwError = FALSE; HMODULE hmod = LoadLibrary(TEXT("NETAPI32.DLL"));
if (hmod) { PFN_NETAPIBUFFERFREE pfnDelayLoad = (PFN_NETAPIBUFFERFREE)GetProcAddress(hmod, "NetApiBufferFree"); if (pfnDelayLoad) { dwError = pfnDelayLoad(Buffer); }
FreeLibrary(hmod); } else { dwError = GetLastError(); }
return dwError; }
// This function is called on the background thread.
BOOL CInternetTime::IsInternetTimeAvailable(void) { return SHRegGetBoolUSValue(SZ_REGKEY_DATETIME, SZ_REGVALUE_INTERNET_FEATURE_AVAILABLE, FALSE, FEATURE_INTERNET_TIME); }
// This function is called on the background thread.
EXTERN_C BOOL DoesTimeComeFromDC(void) { BOOL fTimeFromDomain = FALSE; LPWSTR pszDomain = NULL; NETSETUP_JOIN_STATUS joinStatus = NetSetupUnknownStatus; DWORD dwError = NetGetJoinInformation_DelayLoad(NULL, &pszDomain, &joinStatus);
// We will act like there isn't a DC if we have the test registry set.
if (NERR_Success == dwError) { // If we are connected to a domain, we need to do the expensive net search
// to see that is where we will get the time from.
if (NetSetupDomainName == joinStatus) { PDOMAIN_CONTROLLER_INFO pdomainInfo = {0};
dwError = DsGetDcName_DelayLoad(NULL, NULL, NULL, NULL, DS_TIMESERV_REQUIRED, &pdomainInfo); // We will act like there isn't a DC if we have the test registry set.
if (ERROR_SUCCESS == dwError) { if (FALSE == SHRegGetBoolUSValue(SZ_REGKEY_DATETIME, SZ_REGVALUE_TEST_SIMULATENODC, FALSE, FALSE)) { fTimeFromDomain = TRUE; }
NetApiBufferFree_DelayLoad(pdomainInfo); } }
if (pszDomain) { NetApiBufferFree_DelayLoad(pszDomain); } }
return fTimeFromDomain; }
// This function is called on the background thread.
HRESULT CInternetTime::_SyncNow(BOOL fOnlyUpdateInfo) { HRESULT hr = S_OK;
ENTERCRITICAL; BOOL fContinue = ((eBKAUpdate == m_eAction) || (eBKAGetInfo == m_eAction)); if (fContinue) { m_eAction = eBKAUpdating; } LEAVECRITICAL;
if (fContinue) { hr = E_OUTOFMEMORY; DWORD cchSize = 4024; LPTSTR pszString = (LPTSTR) LocalAlloc(LPTR, sizeof(pszString[0]) * cchSize);
if (pszString) { WCHAR szExistingServer[MAX_URL_STRING]; HRESULT hrServer = E_FAIL;
TCHAR szNewServer[MAX_PATH]; TCHAR szNextSync[MAX_PATH]; DWORD dwError = 0; DWORD dwSyncFlags; if (!fOnlyUpdateInfo) { hrServer = GetW32TimeServer(FALSE, szExistingServer, ARRAYSIZE(szExistingServer));
GetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_EDIT), szNextSync, ARRAYSIZE(szNextSync)); // save the new server away for future processing
StrCpyN(szNewServer, szNextSync, ARRAYSIZE(szNewServer)); if (!ContainsServer(szExistingServer, szNextSync)) { // The servers don't match. We want to add the new server to the beginning of the list. This
// will work around a problem in W32time. If we don't do this, then:
// 1. It will cause a second sync with the original server (which is bad for perf and affects statistics)
// 2. The Last Updated sync time will then be from the wrong peer. This is really bad because it's result is basic
// on the user's previously bad time
// 3. It will do an extra DNS resolution causing slowness on our side, increasing server traffic, and dragging down
// the intranet.
TCHAR szTemp[MAX_URL_STRING];
StrCpyN(szTemp, szNextSync, ARRAYSIZE(szTemp)); wnsprintf(szNextSync, ARRAYSIZE(szNextSync), TEXT("%s %s"), szTemp, szExistingServer);
dwSyncFlags = TimeSyncFlag_ReturnResult | TimeSyncFlag_UpdateAndResync; } else { // We've already got this server in our list of servers. Just cause our peers to resync.
dwSyncFlags = TimeSyncFlag_ReturnResult | TimeSyncFlag_HardResync; }
SetW32TimeServer(szNextSync); // I will ignore the error value because I will get the error info in _CreateW32TimeSuccessErrorString.
dwError = W32TimeSyncNowDDLoad(SZ_COMPUTER_LOCAL, TRUE /* Synchronous */, dwSyncFlags); if ((ResyncResult_StaleData == dwError) && (0 == (TimeSyncFlag_HardResync & dwSyncFlags))) { // We've got stale data preventing us from resyncing. Try again with a full resync.
dwSyncFlags = TimeSyncFlag_ReturnResult | TimeSyncFlag_HardResync; dwError = W32TimeSyncNowDDLoad(SZ_COMPUTER_LOCAL, TRUE /* Synchronous */, dwSyncFlags); } }
pszString[0] = 0; szNextSync[0] = 0; hr = _CreateW32TimeSuccessErrorString(dwError, pszString, cchSize, szNextSync, ARRAYSIZE(szNextSync), (SUCCEEDED(hrServer) ? szNewServer : NULL));
if (SUCCEEDED(hrServer)) { SetW32TimeServer(szExistingServer); _StartServiceAndRefresh(TRUE); // Make sure the service is on and make it update it's settings.
}
ENTERCRITICAL; Str_SetPtr(&m_pszNextSyncTime, szNextSync);
if (m_pszStatusString) { LocalFree(m_pszStatusString); } m_pszStatusString = pszString;
PostMessage(m_hwndInternet, WMUSER_UPDATED_STATUS_TEXT, 0, 0); // Tell the forground thread to pick up the new string.
m_eAction = eBKAWait; LEAVECRITICAL; } }
return hr; }
// This function is called on the background thread.
void CInternetTime::AsyncCheck(void) { HRESULT hr = S_OK;
if (m_hDlg && !DoesTimeComeFromDC()) { // Tell the forground thread to add us.
PostMessage(m_hwndDate, WMUSER_ADDINTERNETTAB, 0, 0); _ProcessBkThreadActions(); } }
// This function is called on the background thread.
HRESULT CInternetTime::_ProcessBkThreadActions(void) { HRESULT hr = S_OK;
while (eBKAQuit != m_eAction) // Okay since we are only reading
{ switch (m_eAction) { case eBKAGetInfo: _SyncNow(TRUE); break;
case eBKAUpdate: _SyncNow(FALSE); break;
case eBKAUpdating: case eBKAWait: default: Sleep(300); // We don't care if there is up to 100ms latency between button press and action starting.
break; } }
return hr; }
/////////////////////////////////////////////////////////////////////
// Public Functions
/////////////////////////////////////////////////////////////////////
// This function is called on the forground thread.
EXTERN_C HRESULT AddInternetPageAsync(HWND hDlg, HWND hwndDate) { HRESULT hr = E_OUTOFMEMORY;
if (!g_pInternetTime) { g_pInternetTime = new CInternetTime(hDlg, hwndDate); }
if (g_pInternetTime) { // We only want to add the page that allows the user to get the time from the internet if:
// 1. The feature is turned on, and
// 2. The user doesn't get the time from an intranet Domain Control.
if (g_pInternetTime->IsInternetTimeAvailable()) { // Start the thread to find out if we are in a domain and need the advanced page. We need
// to do this on a background thread because the DsGetDcName() API may take 10-20 seconds.
hr = (SHCreateThread(_AsyncCheckDCThread, hDlg, (CTF_INSIST | CTF_FREELIBANDEXIT), NULL) ? S_OK : E_FAIL); } }
return hr; }
// This function is called on the forground thread.
EXTERN_C HRESULT AddInternetTab(HWND hDlg) { HRESULT hr = E_OUTOFMEMORY;
if (g_pInternetTime) { hr = g_pInternetTime->AddInternetPage(); }
return hr; }
|