Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1802 lines
62 KiB

  1. /*****************************************************************************\
  2. FILE: inettime.cpp
  3. DESCRIPTION:
  4. This file contains the code used to display UI allowing the user to
  5. control the feature that updates the computer's clock from an internet
  6. NTP time server.
  7. BryanSt 3/22/2000
  8. Copyright (C) Microsoft Corp 2000-2000. All rights reserved.
  9. \*****************************************************************************/
  10. #include "timedate.h"
  11. #include "inettime.h"
  12. #include "rc.h"
  13. #include <wininet.h>
  14. #include <DSGetDC.h> // For DsGetDcName
  15. #include <help.h> // For IDH_DATETIME_*
  16. #include <Lm.h> // For NetGetJoinInformation() and NETSETUP_JOIN_STATUS
  17. #include <Lmjoin.h> // For NetGetJoinInformation() and NETSETUP_JOIN_STATUS
  18. #include <shlobj.h>
  19. #include <shlobjp.h>
  20. #include <w32timep.h> // For Time APIs
  21. #include <shellp.h>
  22. #include <ccstock.h>
  23. #include <shpriv.h>
  24. #define DECL_CRTFREE
  25. #include <crtfree.h>
  26. #define DISALLOW_Assert // Force to use ASSERT instead of Assert
  27. #define DISALLOW_DebugMsg // Force to use TraceMsg instead of DebugMsg
  28. #include <debug.h>
  29. // Reg Keys and Values
  30. #define SZ_REGKEY_DATETIME TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\DateTime")
  31. #define SZ_REGKEY_DATETIME_SERVERS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers")
  32. #define SZ_REGKEY_W32TIME_SERVICE TEXT("System\\CurrentControlSet\\Services\\W32Time")
  33. #define SZ_REGKEY_W32TIME_SERVICE_NTPCLIENT SZ_REGKEY_W32TIME_SERVICE TEXT("\\TimeProviders\\NtpClient")
  34. #define SZ_REGKEY_W32TIME_SERVICE_PARAMETERS SZ_REGKEY_W32TIME_SERVICE TEXT("\\Parameters")
  35. #define SZ_REGVALUE_INTERNET_FEATURE_AVAILABLE TEXT("Support Internet Time")
  36. #define SZ_REGVALUE_TEST_SIMULATENODC TEXT("TestSimulateNoDC") // Used to simulate home scenarios while we have a domain controller
  37. #define SZ_REGVALUE_W32TIME_SYNCFROMFLAGS TEXT("Type")
  38. #define SZ_REGVALUE_W32TIME_STARTSERVICE TEXT("Start")
  39. #define SZ_REGVALUE_NTPSERVERLIST TEXT("NtpServer") // Was "ManualPeerList" in Whistler for a little while.
  40. #define SZ_DEFAULT_NTP_SERVER TEXT("time.windows.gov")
  41. #define SZ_INDEXTO_CUSTOMHOST TEXT("0")
  42. #define SZ_SERVICE_W32TIME TEXT("w32time")
  43. #define SZ_DIFFERENT_SYNCFREQUENCY TEXT(",0x1")
  44. #define SZ_SYNC_BOTH TEXT("AllSync")
  45. #define SZ_SYNC_DS TEXT("NT5DS")
  46. #define SZ_SYNC_NTP TEXT("NTP")
  47. #define SZ_SYNC_NONE TEXT("NoSync")
  48. #define SYNC_ONCE_PER_WEEK 0x93A80 // This is once a week. (Sync every this many seconds)
  49. // Flags for "System\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient","SyncFromFlags"
  50. #define NCSF_ManualPeerList 0x01 // This means use internet SNTP servers.
  51. #define NCSF_DomainHierarchy 0x02 // This means get the time
  52. // this feature is turned off temporarily until we can get the perf hit reduced.
  53. // The problem is that the call to DsGetDcName() can take up to 15 seconds if
  54. // a domain controller can not be found.
  55. #define FEATURE_INTERNET_TIME TRUE
  56. #define MAX_URL_STRING INTERNET_MAX_URL_LENGTH
  57. #define WMUSER_UPDATED_STATUS_TEXT (WM_USER + 11)
  58. // If we aren't using the new w32timep.h, then define it our selves.
  59. // TODO: Nuke this after \nt\ds\ RIs into main.
  60. #ifndef TimeSyncFlag_SoftResync
  61. #define TimeSyncFlag_ReturnResult 0x02
  62. #define TimeSyncFlag_Rediscover 0x04
  63. #define ResyncResult_Success 0x00
  64. #define ResyncResult_ChangeTooBig 0x04
  65. typedef struct _W32TIME_NTP_PEER_INFO {
  66. unsigned __int32 ulSize;
  67. unsigned __int32 ulResolveAttempts;
  68. unsigned __int64 u64TimeRemaining;
  69. unsigned __int64 u64LastSuccessfulSync;
  70. unsigned __int32 ulLastSyncError;
  71. unsigned __int32 ulLastSyncErrorMsgId;
  72. unsigned __int32 ulValidDataCounter;
  73. unsigned __int32 ulAuthTypeMsgId;
  74. #ifdef MIDL_PASS
  75. [string, unique]
  76. wchar_t *wszUniqueName;
  77. #else // MIDL_PASS
  78. LPWSTR wszUniqueName;
  79. #endif // MIDL_PASS
  80. unsigned char ulMode;
  81. unsigned char ulStratum;
  82. unsigned char ulReachability;
  83. unsigned char ulPeerPollInterval;
  84. unsigned char ulHostPollInterval;
  85. } W32TIME_NTP_PEER_INFO, *PW32TIME_NTP_PEER_INFO;
  86. typedef struct _W32TIME_NTP_PROVIDER_DATA {
  87. unsigned __int32 ulSize;
  88. unsigned __int32 ulError;
  89. unsigned __int32 ulErrorMsgId;
  90. unsigned __int32 cPeerInfo;
  91. #ifdef MIDL_PASS
  92. [size_is(cPeerInfo)]
  93. #endif // MIDL_PASS
  94. W32TIME_NTP_PEER_INFO *pPeerInfo;
  95. } W32TIME_NTP_PROVIDER_DATA, *PW32TIME_NTP_PROVIDER_DATA;
  96. #endif // TimeSyncFlag_SoftResync
  97. //============================================================================================================
  98. // *** Globals ***
  99. //============================================================================================================
  100. const static DWORD aInternetTimeHelpIds[] = {
  101. DATETIME_AUTOSETFROMINTERNET, IDH_DATETIME_AUTOSETFROMINTERNET,
  102. DATETIME_INTERNET_SERVER_LABLE, IDH_DATETIME_SERVER_EDIT,
  103. DATETIME_INTERNET_SERVER_EDIT, IDH_DATETIME_SERVER_EDIT,
  104. DATETIME_INFOTEXTTOP, IDH_DATETIME_INFOTEXT,
  105. DATETIME_INFOTEXTPROXY, IDH_DATETIME_INFOTEXT,
  106. DATETIME_INTERNET_ERRORTEXT, IDH_DATETIME_INFOTEXT,
  107. DATETIME_INTERNET_UPDATENOW, IDH_DATETIME_UPDATEFROMINTERNET,
  108. 0, 0
  109. };
  110. #define SZ_HELPFILE_INTERNETTIME TEXT("windows.hlp")
  111. #define HINST_THISDLL g_hInst
  112. BOOL g_fCustomServer;
  113. 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?
  114. HRESULT StrReplaceToken(IN LPCTSTR pszToken, IN LPCTSTR pszReplaceValue, IN LPTSTR pszString, IN DWORD cchSize)
  115. {
  116. HRESULT hr = S_OK;
  117. LPTSTR pszTempLastHalf = NULL;
  118. LPTSTR pszNextToken = pszString;
  119. while (0 != (pszNextToken = StrStrI(pszNextToken, pszToken)))
  120. {
  121. // We found one.
  122. LPTSTR pszPastToken = pszNextToken + lstrlen(pszToken);
  123. Str_SetPtr(&pszTempLastHalf, pszPastToken); // Keep a copy because we will overwrite it.
  124. pszNextToken[0] = 0; // Remove the rest of the string.
  125. StringCchCat(pszString, cchSize, pszReplaceValue );
  126. StringCchCat(pszString, cchSize, pszTempLastHalf );
  127. pszNextToken += lstrlen(pszReplaceValue);
  128. }
  129. Str_SetPtr(&pszTempLastHalf, NULL);
  130. return hr;
  131. }
  132. HRESULT GetW32TimeServer(BOOL fRemoveJunk, LPWSTR pszServer, DWORD cchSize)
  133. {
  134. DWORD dwType = REG_SZ;
  135. DWORD cbSize = (sizeof(pszServer[0]) * cchSize);
  136. HRESULT hr = W32TimeQueryConfig(W32TIME_CONFIG_MANUAL_PEER_LIST, &dwType, (BYTE *) pszServer, &cbSize);
  137. if (ResultFromWin32(ERROR_PROC_NOT_FOUND) == hr)
  138. {
  139. DWORD dwError = SHGetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_NTPSERVERLIST, NULL, (BYTE *)pszServer, &cbSize);
  140. hr = ResultFromWin32(dwError);
  141. }
  142. if (SUCCEEDED(hr) && fRemoveJunk)
  143. {
  144. LPWSTR pszJunk = StrStr(pszServer, L",0x");
  145. if (pszJunk)
  146. {
  147. pszJunk[0] = 0;
  148. }
  149. pszJunk = StrStr(pszServer, L" ");
  150. if (pszJunk)
  151. {
  152. pszJunk[0] = 0;
  153. }
  154. }
  155. return hr;
  156. }
  157. HRESULT RemoveDuplicateServers(LPWSTR pszServer, DWORD cchSize)
  158. {
  159. TCHAR szResults[MAX_URL_STRING];
  160. StringCchCopy( szResults, ARRAYSIZE(szResults), pszServer );
  161. LPTSTR pszBeginning = szResults;
  162. LPTSTR pszEnd;
  163. while (NULL != (pszEnd = StrChr(pszBeginning, TEXT(' '))))
  164. {
  165. TCHAR szSearchStr[MAX_PATH];
  166. StringCchCopy( szSearchStr, (DWORD)min(ARRAYSIZE(szSearchStr), (pszEnd - pszBeginning + 1)), pszBeginning );
  167. pszEnd++; // Skip past the space
  168. StringCchCat( szSearchStr, ARRAYSIZE(szSearchStr), L" " );
  169. StrReplaceToken(szSearchStr, TEXT(""), pszEnd, (ARRAYSIZE(szResults) - (DWORD)(pszEnd - &szResults[0])));
  170. szSearchStr[ lstrlen(szSearchStr) - 1 ] = 0;
  171. StrReplaceToken(szSearchStr, TEXT(""), pszEnd, (ARRAYSIZE(szResults) - (DWORD)(pszEnd - &szResults[0])));
  172. pszBeginning = pszEnd;
  173. }
  174. PathRemoveBlanks(szResults);
  175. StringCchCopy( pszServer, cchSize, szResults );
  176. return S_OK;
  177. }
  178. BOOL ComparePeers(LPTSTR szPeer1, LPTSTR szPeer2) {
  179. BOOL fResult;
  180. LPTSTR szFlags1;
  181. LPTSTR szFlags2;
  182. TCHAR szSave1;
  183. TCHAR szSave2;
  184. szFlags1 = StrChr(szPeer1, TEXT(','));
  185. if (NULL == szFlags1)
  186. {
  187. szFlags1 = StrChr(szPeer1, TEXT(' '));
  188. }
  189. szFlags2 = StrChr(szPeer2, TEXT(','));
  190. if (NULL == szFlags2)
  191. {
  192. szFlags2 = StrChr(szPeer2, TEXT(' '));
  193. }
  194. if (NULL != szFlags1) {
  195. szSave1 = szFlags1[0];
  196. szFlags1[0] = TEXT('\0');
  197. }
  198. if (NULL != szFlags2) {
  199. szSave2 = szFlags2[0];
  200. szFlags2[0] = TEXT('\0');
  201. }
  202. fResult = 0 == StrCmpI(szPeer1, szPeer2);
  203. if (NULL != szFlags1) {
  204. szFlags1[0] = szSave1;
  205. }
  206. if (NULL != szFlags2) {
  207. szFlags2[0] = szSave2;
  208. }
  209. return fResult;
  210. }
  211. BOOL ContainsServer(LPWSTR pwszServerList, LPWSTR pwszServer)
  212. {
  213. DWORD dwNextServerOffset = 0;
  214. LPWSTR pwszNext = pwszServerList;
  215. while (NULL != pwszNext)
  216. {
  217. pwszNext += dwNextServerOffset;
  218. if (ComparePeers(pwszNext, pwszServer))
  219. {
  220. return TRUE;
  221. }
  222. pwszNext = StrChr(pwszNext, TEXT(' '));
  223. dwNextServerOffset = 1;
  224. }
  225. return FALSE;
  226. }
  227. HRESULT AddTerminators(LPWSTR pszServer, DWORD cchSize)
  228. {
  229. TCHAR szServer[MAX_URL_STRING];
  230. TCHAR szTemp[MAX_URL_STRING];
  231. szTemp[0] = 0;
  232. StringCchCopy(szServer, ARRAYSIZE(szServer), pszServer );
  233. LPTSTR pszBeginning = szServer;
  234. LPTSTR pszEnd;
  235. while (NULL != (pszEnd = StrChr(pszBeginning, TEXT(' '))))
  236. {
  237. TCHAR szTemp2[MAX_PATH];
  238. pszEnd[0] = 0;
  239. if (!StrStrI(pszBeginning, L",0x"))
  240. {
  241. StringCchPrintf( szTemp2, ARRAYSIZE(szTemp2), L"%s,0x1 ", pszBeginning );
  242. StringCchCat( szTemp, ARRAYSIZE(szTemp), szTemp2 );
  243. }
  244. else
  245. {
  246. StringCchCat( szTemp, ARRAYSIZE(szTemp), pszBeginning );
  247. StringCchCat( szTemp, ARRAYSIZE(szTemp), L" " );
  248. }
  249. pszBeginning = &pszEnd[1];
  250. }
  251. StringCchCat( szTemp, ARRAYSIZE(szTemp), pszBeginning );
  252. if ( 0 != pszBeginning[0] && 0 != szTemp[0] && NULL == StrStrI(pszBeginning, L",0x") )
  253. {
  254. // We need to indicate which kind of sync method
  255. StringCchCat( szTemp, ARRAYSIZE(szTemp), L",0x1" );
  256. }
  257. StringCchCopy( pszServer, cchSize, szTemp );
  258. return S_OK;
  259. }
  260. HRESULT SetW32TimeServer(LPCWSTR pszServer)
  261. {
  262. TCHAR szServer[MAX_PATH];
  263. StringCchCopy( szServer, ARRAYSIZE(szServer), pszServer );
  264. AddTerminators(szServer, ARRAYSIZE(szServer));
  265. RemoveDuplicateServers(szServer, ARRAYSIZE(szServer));
  266. DWORD cbSize = ((lstrlen(szServer) + 1) * sizeof(szServer[0]));
  267. HRESULT hr = W32TimeSetConfig(W32TIME_CONFIG_MANUAL_PEER_LIST, REG_SZ, (BYTE *) szServer, cbSize);
  268. if (ResultFromWin32(ERROR_PROC_NOT_FOUND) == hr)
  269. {
  270. DWORD dwError = SHSetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_NTPSERVERLIST, REG_SZ, (BYTE *)szServer, cbSize);
  271. hr = ResultFromWin32(dwError);
  272. }
  273. return hr;
  274. }
  275. HRESULT SetW32TimeSyncFreq(DWORD dwSyncFreq)
  276. {
  277. return S_OK;
  278. // return W32TimeSetConfig(W32TIME_CONFIG_SPECIAL_POLL_INTERVAL, REG_DWORD, (BYTE *) dwSyncFreq, sizeof(dwSyncFreq));
  279. }
  280. enum eBackgroundThreadAction
  281. {
  282. eBKAWait = 0, // The bkthread should wait for a command
  283. eBKAGetInfo, // The bkthread should get the info on the last sync
  284. eBKAUpdate, // The bkthread should start synching
  285. eBKAUpdating, // The bkthread is synching now
  286. eBKAQuit, // The bkthread should shut down
  287. };
  288. class CInternetTime
  289. {
  290. public:
  291. // Forground methods
  292. BOOL IsInternetTimeAvailable(void);
  293. HRESULT AddInternetPage(void);
  294. INT_PTR _AdvancedDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  295. CInternetTime(HWND hDlg, HWND hwndDate);
  296. virtual ~CInternetTime(void);
  297. // Background methods
  298. void AsyncCheck(void);
  299. private:
  300. // Private Member Variables
  301. HWND m_hDlg; // hwnd of parent (property sheet).
  302. HWND m_hwndDate; // Date tab hwnd
  303. HWND m_hwndInternet; // InternetTime tab hwnd
  304. HCURSOR m_hCurOld; // The old cursor while we display the wait cursor
  305. LPTSTR m_pszStatusString; // CROSS-THREAD: This is used to pass between threads.
  306. LPTSTR m_pszNextSyncTime; // CROSS-THREAD: This is used to pass between threads.
  307. eBackgroundThreadAction m_eAction; // CROSS-THREAD: This is used to pass between threads.
  308. // Private Member Functions
  309. // Forground methods
  310. HRESULT _InitAdvancedPage(HWND hDlg);
  311. HRESULT _OnUpdateButton(void);
  312. HRESULT _OnUpdateStatusString(void);
  313. HRESULT _OnSetFeature(HWND hDlg, BOOL fOn);
  314. HRESULT _OnToggleFeature(HWND hDlg);
  315. HRESULT _GoGetHelp(void);
  316. HRESULT _ResetServer(void);
  317. HRESULT _PersistInternetTimeSettings(HWND hDlg);
  318. HRESULT _StartServiceAndRefresh(BOOL fStartIfOff);
  319. INT_PTR _OnCommandAdvancedPage(HWND hDlg, WPARAM wParam, LPARAM lParam);
  320. INT_PTR _OnNotifyAdvancedPage(HWND hDlg, NMHDR * pNMHdr, int idControl);
  321. // Background methods
  322. HRESULT _ProcessBkThreadActions(void);
  323. HRESULT _SyncNow(BOOL fOnlyUpdateInfo);
  324. HRESULT _CreateW32TimeSuccessErrorString(DWORD dwError, LPTSTR pszString, DWORD cchSize, LPTSTR pszNextSync, DWORD cchNextSyncSize, LPTSTR pszServerToQuery);
  325. HRESULT _GetDateTimeString(FILETIME * pftTimeDate, BOOL fDate, LPTSTR pszString, DWORD cchSize);
  326. };
  327. CInternetTime * g_pInternetTime = NULL;
  328. // This function is called on the forground thread.
  329. CInternetTime::CInternetTime(HWND hDlg, HWND hwndDate)
  330. {
  331. m_hDlg = hDlg;
  332. m_hwndDate = hwndDate;
  333. m_eAction = eBKAGetInfo;
  334. m_pszStatusString = NULL;
  335. m_hwndInternet = NULL;
  336. m_hCurOld = NULL;
  337. m_pszStatusString = NULL;
  338. m_pszNextSyncTime = NULL;
  339. }
  340. CInternetTime::~CInternetTime()
  341. {
  342. ENTERCRITICAL;
  343. if (m_pszStatusString)
  344. {
  345. LocalFree(m_pszStatusString);
  346. m_pszStatusString = NULL;
  347. }
  348. if (m_pszNextSyncTime)
  349. {
  350. LocalFree(m_pszNextSyncTime);
  351. m_pszNextSyncTime = NULL;
  352. }
  353. if (m_hCurOld)
  354. {
  355. SetCursor(m_hCurOld);
  356. m_hCurOld = NULL;
  357. }
  358. LEAVECRITICAL;
  359. }
  360. HRESULT FormatMessageWedge(LPCWSTR pwzTemplate, LPWSTR pwzStrOut, DWORD cchSize, ...)
  361. {
  362. va_list vaParamList;
  363. HRESULT hr = S_OK;
  364. va_start(vaParamList, cchSize);
  365. if (0 == FormatMessageW(FORMAT_MESSAGE_FROM_STRING, pwzTemplate, 0, 0, pwzStrOut, cchSize, &vaParamList))
  366. {
  367. hr = ResultFromLastError();
  368. }
  369. va_end(vaParamList);
  370. return hr;
  371. }
  372. /////////////////////////////////////////////////////////////////////
  373. // Private Internal Helpers
  374. /////////////////////////////////////////////////////////////////////
  375. // This function is called on the forground thread.
  376. HRESULT CInternetTime::_OnSetFeature(HWND hDlg, BOOL fOn)
  377. {
  378. HWND hwndOnOffCheckbox = GetDlgItem(m_hwndInternet, DATETIME_AUTOSETFROMINTERNET);
  379. HWND hwndServerLable = GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_LABLE);
  380. HWND hwndServerEdit = GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_EDIT);
  381. CheckDlgButton(hDlg, DATETIME_AUTOSETFROMINTERNET, (fOn ? BST_CHECKED : BST_UNCHECKED));
  382. EnableWindow(hwndOnOffCheckbox, g_fWriteAccess);
  383. EnableWindow(hwndServerLable, (g_fWriteAccess ? fOn : FALSE));
  384. EnableWindow(hwndServerEdit, (g_fWriteAccess ? fOn : FALSE));
  385. EnableWindow(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), (g_fWriteAccess ? fOn : FALSE));
  386. EnableWindow(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_UPDATENOW), (g_fWriteAccess ? fOn : FALSE));
  387. EnableWindow(GetDlgItem(m_hwndInternet, DATETIME_INFOTEXTTOP), (g_fWriteAccess ? fOn : FALSE));
  388. if (fOn)
  389. {
  390. // If the user just turned on the feature and the editbox is empty,
  391. // replace the text with the default server name.
  392. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  393. if (!GetWindowText(hwndServerEdit, szServer, ARRAYSIZE(szServer)) ||
  394. !szServer[0])
  395. {
  396. SetWindowText(hwndServerEdit, SZ_DEFAULT_NTP_SERVER);
  397. }
  398. }
  399. return S_OK;
  400. }
  401. // This function is called on the forground thread.
  402. HRESULT CInternetTime::_OnToggleFeature(HWND hDlg)
  403. {
  404. BOOL fIsFeatureOn = ((BST_CHECKED == IsDlgButtonChecked(hDlg, DATETIME_AUTOSETFROMINTERNET)) ? TRUE : FALSE);
  405. PropSheet_Changed(GetParent(hDlg), hDlg); // Say we need to enable the apply button
  406. return _OnSetFeature(hDlg, fIsFeatureOn);
  407. }
  408. // This function is called on the forground thread.
  409. HRESULT AddServerList(HWND hwndCombo, HKEY hkey, int nIndex)
  410. {
  411. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  412. DWORD cbSizeServer = sizeof(szServer);
  413. TCHAR szIndex[MAX_PATH];
  414. DWORD dwType;
  415. StringCchPrintf( szIndex, ARRAYSIZE(szIndex), TEXT("%d"), nIndex );
  416. DWORD dwError = SHGetValue(hkey, NULL, szIndex, &dwType, (void *)szServer, &cbSizeServer);
  417. HRESULT hr = ResultFromWin32(dwError);
  418. if (SUCCEEDED(hr))
  419. {
  420. LPTSTR pszFlags = StrStr(szServer, SZ_DIFFERENT_SYNCFREQUENCY);
  421. if (pszFlags)
  422. {
  423. pszFlags[0] = 0; // Remove the flags.
  424. }
  425. dwError = ComboBox_AddString(hwndCombo, szServer);
  426. if ((dwError == CB_ERR) || (dwError == CB_ERRSPACE))
  427. {
  428. hr = E_FAIL;
  429. }
  430. }
  431. return hr;
  432. }
  433. // This function is called on the forground thread.
  434. HRESULT PopulateComboBox(IN HWND hwndCombo, IN BOOL * pfCustomServer)
  435. {
  436. HRESULT hr;
  437. HKEY hkey;
  438. *pfCustomServer = FALSE;
  439. DWORD dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, 0, KEY_READ, &hkey);
  440. hr = ResultFromWin32(dwError);
  441. if (SUCCEEDED(hr))
  442. {
  443. // Try to add the custom slot.
  444. if (SUCCEEDED(AddServerList(hwndCombo, hkey, 0)))
  445. {
  446. // We do have an existing custom server, so let the caller know so
  447. // they know to increase the index by 1.
  448. *pfCustomServer = TRUE;
  449. }
  450. for (int nIndex = 1; SUCCEEDED(hr); nIndex++)
  451. {
  452. hr = AddServerList(hwndCombo, hkey, nIndex);
  453. }
  454. RegCloseKey(hkey);
  455. }
  456. return hr;
  457. }
  458. // This function is called on the forground thread.
  459. HRESULT CInternetTime::_OnUpdateButton(void)
  460. {
  461. HRESULT hr = S_OK;
  462. ENTERCRITICAL;
  463. // We don't need to do anything if it's already working on another task.
  464. if (eBKAWait == m_eAction)
  465. {
  466. TCHAR szMessage[2 * MAX_PATH];
  467. TCHAR szTemplate[MAX_PATH];
  468. TCHAR szServer[MAX_PATH];
  469. if (!m_hCurOld)
  470. {
  471. m_hCurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  472. }
  473. LoadString(HINST_THISDLL, IDS_IT_WAITFORSYNC, szTemplate, ARRAYSIZE(szTemplate));
  474. GetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_EDIT), szServer, ARRAYSIZE(szServer));
  475. FormatMessageWedge(szTemplate, szMessage, ARRAYSIZE(szMessage), szServer);
  476. SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), szMessage);
  477. m_eAction = eBKAUpdate;
  478. }
  479. LEAVECRITICAL;
  480. return hr;
  481. }
  482. // This function is called on the forground thread.
  483. BOOL IsManualPeerListOn(void)
  484. {
  485. BOOL fIsManualPeerListOn = TRUE;
  486. TCHAR szSyncType[MAX_PATH];
  487. DWORD cbSize = sizeof(szSyncType);
  488. DWORD dwType;
  489. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_W32TIME_SYNCFROMFLAGS, &dwType, (LPBYTE)szSyncType, &cbSize))
  490. {
  491. if (!StrCmpI(szSyncType, SZ_SYNC_DS) || !StrCmpI(szSyncType, SZ_SYNC_NONE))
  492. {
  493. fIsManualPeerListOn = FALSE;
  494. }
  495. }
  496. return fIsManualPeerListOn;
  497. }
  498. // This function is called on the forground thread.
  499. HRESULT CInternetTime::_InitAdvancedPage(HWND hDlg)
  500. {
  501. DWORD dwType;
  502. TCHAR szIndex[MAX_PATH];
  503. HWND hwndServerEdit = GetDlgItem(hDlg, DATETIME_INTERNET_SERVER_EDIT);
  504. HWND hwndOnOffCheckbox = GetDlgItem(hDlg, DATETIME_AUTOSETFROMINTERNET);
  505. DWORD cbSize = sizeof(szIndex);
  506. BOOL fIsFeatureOn = IsManualPeerListOn();
  507. m_hwndInternet = hDlg;
  508. HRESULT hr = PopulateComboBox(hwndServerEdit, &g_fCustomServer);
  509. // Does the user have access to change the setting in the registry?
  510. HKEY hKeyTemp;
  511. g_fWriteAccess = FALSE;
  512. DWORD dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, 0, KEY_WRITE, &hKeyTemp);
  513. if (ERROR_SUCCESS == dwError)
  514. {
  515. // We have access to read & write so we can enable the UI.
  516. RegCloseKey(hKeyTemp);
  517. dwError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyTemp, NULL);
  518. if (ERROR_SUCCESS == dwError)
  519. {
  520. RegCloseKey(hKeyTemp);
  521. dwError = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyTemp, NULL);
  522. if (ERROR_SUCCESS == dwError)
  523. {
  524. g_fWriteAccess = TRUE;
  525. RegCloseKey(hKeyTemp);
  526. }
  527. }
  528. }
  529. dwError = SHGetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, NULL, &dwType, (void *) szIndex, &cbSize);
  530. hr = ResultFromWin32(dwError);
  531. if (SUCCEEDED(hr))
  532. {
  533. int nIndex = StrToInt(szIndex);
  534. if (!g_fCustomServer)
  535. {
  536. nIndex -= 1; // We don't have slot zero (the custom server), so reduce the index.
  537. }
  538. ComboBox_SetCurSel(hwndServerEdit, nIndex);
  539. }
  540. _OnSetFeature(hDlg, fIsFeatureOn);
  541. if (fIsFeatureOn)
  542. {
  543. // The feature is on, so select the text and set focus
  544. // to the combobox.
  545. ComboBox_SetEditSel(hwndServerEdit, 0, (LPARAM)-1); // Select all the Text
  546. SetFocus(hwndServerEdit);
  547. }
  548. else
  549. {
  550. // The feature is off so set focus to the checkbox.
  551. SetFocus(hwndOnOffCheckbox);
  552. }
  553. ENTERCRITICAL;
  554. if (m_pszStatusString)
  555. {
  556. SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), m_pszStatusString);
  557. }
  558. if (m_pszNextSyncTime)
  559. {
  560. SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INFOTEXTTOP), m_pszNextSyncTime);
  561. }
  562. LEAVECRITICAL;
  563. return S_OK;
  564. }
  565. // This function is called on the background thread.
  566. HRESULT SetSyncFlags(DWORD dwSyncFromFlags)
  567. {
  568. LPCTSTR pszFlags = SZ_SYNC_BOTH;
  569. switch (dwSyncFromFlags)
  570. {
  571. case NCSF_DomainHierarchy:
  572. pszFlags = SZ_SYNC_DS;
  573. break;
  574. case NCSF_ManualPeerList:
  575. pszFlags = SZ_SYNC_NTP;
  576. break;
  577. case 0x00000000:
  578. pszFlags = SZ_SYNC_NONE;
  579. break;
  580. };
  581. DWORD cbSize = ((lstrlen(pszFlags) + 1) * sizeof(pszFlags[0]));
  582. DWORD dwError = SHSetValue(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE_PARAMETERS, SZ_REGVALUE_W32TIME_SYNCFROMFLAGS, REG_SZ, (LPBYTE)pszFlags, cbSize);
  583. HRESULT hr = ResultFromWin32(dwError);
  584. return hr;
  585. }
  586. /*****************************************************************************\
  587. DESCRIPTION:
  588. The following reg keys determine if the W32Time service is on or off:
  589. HKLM,"System\CurrentControlSet\Services\W32Time\"
  590. "Start", 0x00000002 (Manual?) 0x0000003 (Automatic?)
  591. "Type", 0x00000020 (Nt5Ds?) 0x0000120 (NTP?)
  592. HKLM,"System\CurrentControlSet\Services\W32Time\Parameters"
  593. "LocalNTP", 0x00000000 (Off) 0x0000001 (On)
  594. "Type", "Nt5Ds" (NTP off) "NTP" (NTP?)
  595. HKLM,"System\CurrentControlSet\Services\W32Time\TimeProviders\NTPClient"
  596. "SyncFromFlags", 0x00000001 (???) 0x0000002 (???)
  597. The following reg keys determine which NTP server to use:
  598. HKLM,"System\CurrentControlSet\Services\W32Time\TimeProviders\NTPClient"
  599. "ManualPeerList", REG_SZ_EXPAND "time.nist.gov"
  600. \*****************************************************************************/
  601. // This function is called on the forground thread.
  602. HRESULT CInternetTime::_PersistInternetTimeSettings(HWND hDlg)
  603. {
  604. BOOL fIsFeatureOn = ((BST_CHECKED == IsDlgButtonChecked(hDlg, DATETIME_AUTOSETFROMINTERNET)) ? TRUE : FALSE);
  605. DWORD dwSyncFromFlags = (fIsFeatureOn ? NCSF_ManualPeerList : 0 /*none*/ );
  606. DWORD dwError;
  607. DWORD cbSize;
  608. HRESULT hr = SetSyncFlags(dwSyncFromFlags);
  609. HWND hwndServerEdit = GetDlgItem(hDlg, DATETIME_INTERNET_SERVER_EDIT);
  610. // No, so the editbox has a customer server. We need to store the custom server.
  611. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  612. TCHAR szServerReg[INTERNET_MAX_HOST_NAME_LENGTH];
  613. szServer[0] = 0;
  614. GetWindowText(hwndServerEdit, szServer, ARRAYSIZE(szServer));
  615. // Here, we want to detect the case where someone typed in the same name as
  616. // in the drop down list. So if "time.nist.gov" is in the list, we want to
  617. // select it instead of saving another "time.nist.gov" in the custom slot.
  618. int nDup = ComboBox_FindString(hwndServerEdit, 0, szServer);
  619. if (CB_ERR != nDup)
  620. {
  621. ComboBox_SetCurSel(hwndServerEdit, nDup);
  622. }
  623. // The ",0x1" denotes that we want to sync less frequently and not use the NTP RFC's
  624. // sync frequency.
  625. StringCchCopy( szServerReg, ARRAYSIZE(szServerReg), szServer );
  626. StringCchCat( szServerReg, ARRAYSIZE(szServerReg), SZ_DIFFERENT_SYNCFREQUENCY );
  627. // Write the default server name into "ManualPeerList", which is where the
  628. // service reads it from.
  629. SetW32TimeServer(szServerReg);
  630. if (!StrCmpI(szServerReg, L"time.windows.com"))
  631. {
  632. // We need time.windows.com to scale to a large number of users, so don't let it sync more frequently
  633. // than once a week.
  634. SetW32TimeSyncFreq(SYNC_ONCE_PER_WEEK);
  635. }
  636. int nIndex = ComboBox_GetCurSel(hwndServerEdit);
  637. if (CB_ERR == nIndex) // Is anything selected?
  638. {
  639. cbSize = ((lstrlenW(szServer) + 1) * sizeof(szServer[0]));
  640. dwError = SHSetValueW(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, SZ_INDEXTO_CUSTOMHOST, REG_SZ, (void *)szServer, cbSize);
  641. nIndex = 0;
  642. }
  643. else
  644. {
  645. if (!g_fCustomServer)
  646. {
  647. nIndex += 1; // Push the index down by one because the listbox doesn't have a custom server
  648. }
  649. }
  650. TCHAR szIndex[MAX_PATH];
  651. StringCchPrintf( szIndex, ARRAYSIZE(szIndex), L"%d", nIndex );
  652. cbSize = ((lstrlenW(szIndex) + 1) * sizeof(szIndex[0]));
  653. dwError = SHSetValueW(HKEY_LOCAL_MACHINE, SZ_REGKEY_DATETIME_SERVERS, NULL, REG_SZ, (void *)szIndex, cbSize);
  654. _StartServiceAndRefresh(fIsFeatureOn); // Make sure the service is on and make it update it's settings.
  655. return S_OK;
  656. }
  657. // This function is called on either the forground or background thread.
  658. HRESULT CInternetTime::_StartServiceAndRefresh(BOOL fStartIfOff)
  659. {
  660. HRESULT hr = S_OK;
  661. // We need to let the service know that settings may have changed. If the user
  662. // wanted this feature on, then we should make sure the service starts.
  663. SC_HANDLE hServiceMgr = OpenSCManager(NULL, NULL, (SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS));
  664. if (hServiceMgr)
  665. {
  666. SC_HANDLE hService = OpenService(hServiceMgr, SZ_SERVICE_W32TIME, (/*SERVICE_START |*/ SERVICE_PAUSE_CONTINUE));
  667. DWORD dwServiceStartMode = 0x00000002; // This will cause the service to start Automatically on reboot.
  668. DWORD dwError = SHSetValueW(HKEY_LOCAL_MACHINE, SZ_REGKEY_W32TIME_SERVICE, SZ_REGVALUE_W32TIME_STARTSERVICE, REG_DWORD, &dwServiceStartMode, sizeof(dwServiceStartMode));
  669. if (hService)
  670. {
  671. SERVICE_STATUS serviceStatus = {0};
  672. BOOL fSucceeded;
  673. if (fStartIfOff)
  674. {
  675. // We should start up the service in case it's not turned on.
  676. fSucceeded = StartService(hService, 0, NULL);
  677. hr = (fSucceeded ? S_OK : ResultFromLastError());
  678. if (ResultFromWin32(ERROR_SERVICE_ALREADY_RUNNING) == hr)
  679. {
  680. hr = S_OK; // We can ignore this err value because it's benign.
  681. }
  682. }
  683. // Tell the service to re-read the registry entry settings.
  684. fSucceeded = ControlService(hService, SERVICE_CONTROL_PARAMCHANGE, &serviceStatus);
  685. CloseServiceHandle(hService);
  686. }
  687. else
  688. {
  689. hr = ResultFromLastError();
  690. }
  691. CloseServiceHandle(hServiceMgr);
  692. }
  693. else
  694. {
  695. hr = ResultFromLastError();
  696. }
  697. return hr;
  698. }
  699. HRESULT CInternetTime::_OnUpdateStatusString(void)
  700. {
  701. HRESULT hr = S_OK;
  702. ENTERCRITICAL; // Be safe while using m_pszStatusString
  703. SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_ERRORTEXT), (m_pszStatusString ? m_pszStatusString : TEXT("")));
  704. SetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INFOTEXTTOP), (m_pszNextSyncTime ? m_pszNextSyncTime : TEXT("")));
  705. if (m_hCurOld)
  706. {
  707. SetCursor(m_hCurOld);
  708. m_hCurOld = NULL;
  709. }
  710. LEAVECRITICAL;
  711. return S_OK;
  712. }
  713. // This function is called on the forground thread.
  714. HRESULT CInternetTime::_GoGetHelp(void)
  715. {
  716. HRESULT hr = S_OK;
  717. TCHAR szCommand[MAX_PATH];
  718. LoadString(HINST_THISDLL, IDS_TROUBLESHOOT_INTERNETIME, szCommand, ARRAYSIZE(szCommand));
  719. ShellExecute(m_hwndDate, NULL, szCommand, NULL, NULL, SW_NORMAL);
  720. return S_OK;
  721. }
  722. // This function is called on the forground thread.
  723. INT_PTR CInternetTime::_OnCommandAdvancedPage(HWND hDlg, WPARAM wParam, LPARAM lParam)
  724. {
  725. BOOL fHandled = 1; // Not handled (WM_COMMAND seems to be different)
  726. WORD wMsg = GET_WM_COMMAND_CMD(wParam, lParam);
  727. WORD idCtrl = GET_WM_COMMAND_ID(wParam, lParam);
  728. switch (idCtrl)
  729. {
  730. case DATETIME_AUTOSETFROMINTERNET:
  731. switch (wMsg)
  732. {
  733. case BN_CLICKED:
  734. _OnToggleFeature(hDlg);
  735. break;
  736. }
  737. break;
  738. case DATETIME_INTERNET_SERVER_EDIT:
  739. switch (wMsg)
  740. {
  741. case EN_CHANGE:
  742. case CBN_EDITCHANGE:
  743. case CBN_SELCHANGE:
  744. PropSheet_Changed(GetParent(hDlg), hDlg);
  745. break;
  746. }
  747. break;
  748. case DATETIME_INTERNET_UPDATENOW:
  749. switch (wMsg)
  750. {
  751. case BN_CLICKED:
  752. _OnUpdateButton();
  753. break;
  754. }
  755. break;
  756. }
  757. return fHandled;
  758. }
  759. // This function is called on the forground thread.
  760. INT_PTR CInternetTime::_OnNotifyAdvancedPage(HWND hDlg, NMHDR * pNMHdr, int idControl)
  761. {
  762. BOOL fHandled = 1; // Not handled (WM_COMMAND seems to be different)
  763. if (pNMHdr)
  764. {
  765. switch (pNMHdr->idFrom)
  766. {
  767. case 0:
  768. {
  769. switch (pNMHdr->code)
  770. {
  771. case PSN_APPLY:
  772. _PersistInternetTimeSettings(hDlg);
  773. break;
  774. }
  775. break;
  776. }
  777. default:
  778. switch (pNMHdr->code)
  779. {
  780. case NM_RETURN:
  781. case NM_CLICK:
  782. {
  783. PNMLINK pNMLink = (PNMLINK) pNMHdr;
  784. if (!StrCmpW(pNMLink->item.szID, L"HelpMe"))
  785. {
  786. _GoGetHelp();
  787. }
  788. break;
  789. }
  790. }
  791. break;
  792. }
  793. }
  794. return fHandled;
  795. }
  796. // This function is called on the forground thread.
  797. EXTERN_C INT_PTR CALLBACK AdvancedDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  798. {
  799. if (g_pInternetTime)
  800. {
  801. return g_pInternetTime->_AdvancedDlgProc(hDlg, uMsg, wParam, lParam);
  802. }
  803. return (TRUE);
  804. }
  805. // This function is called on the forground thread.
  806. INT_PTR CInternetTime::_AdvancedDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  807. {
  808. switch (uMsg)
  809. {
  810. case WM_INITDIALOG:
  811. _InitAdvancedPage(hDlg);
  812. break;
  813. case WM_DESTROY:
  814. break;
  815. case WM_NOTIFY:
  816. _OnNotifyAdvancedPage(hDlg, (NMHDR *)lParam, (int) wParam);
  817. break;
  818. case WM_COMMAND:
  819. _OnCommandAdvancedPage(hDlg, wParam, lParam);
  820. break;
  821. case WMUSER_UPDATED_STATUS_TEXT:
  822. _OnUpdateStatusString();
  823. break;
  824. case WM_HELP:
  825. WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, SZ_HELPFILE_INTERNETTIME, HELP_WM_HELP, (DWORD_PTR) aInternetTimeHelpIds);
  826. break;
  827. case WM_CONTEXTMENU: // right mouse click
  828. WinHelp((HWND) wParam, SZ_HELPFILE_INTERNETTIME, HELP_CONTEXTMENU, (DWORD_PTR) aInternetTimeHelpIds);
  829. break;
  830. default:
  831. return FALSE;
  832. }
  833. return (TRUE);
  834. }
  835. // This function is called on the forground thread.
  836. HRESULT CInternetTime::AddInternetPage(void)
  837. {
  838. HRESULT hr = S_OK;
  839. PROPSHEETPAGE pspAdvanced;
  840. INITCOMMONCONTROLSEX initComctl32;
  841. initComctl32.dwSize = sizeof(initComctl32);
  842. initComctl32.dwICC = (ICC_STANDARD_CLASSES | ICC_LINK_CLASS);
  843. InitCommonControlsEx(&initComctl32); // Register the comctl32 LinkWindow
  844. pspAdvanced.dwSize = sizeof(PROPSHEETPAGE);
  845. pspAdvanced.dwFlags = PSP_DEFAULT;
  846. pspAdvanced.hInstance = HINST_THISDLL;
  847. pspAdvanced.pszTemplate = MAKEINTRESOURCE(DLG_ADVANCED);
  848. pspAdvanced.pfnDlgProc = AdvancedDlgProc;
  849. pspAdvanced.lParam = (LPARAM) this;
  850. if (IsWindow(m_hDlg))
  851. {
  852. HPROPSHEETPAGE hPropSheet = CreatePropertySheetPage(&pspAdvanced);
  853. if (hPropSheet)
  854. {
  855. PropSheet_AddPage(m_hDlg, hPropSheet);
  856. }
  857. }
  858. return hr;
  859. }
  860. /////////////////////////////////////////////////////////////////////
  861. // Background Thread Functions
  862. /////////////////////////////////////////////////////////////////////
  863. ///////////
  864. // These functions will cause the background thread to sync
  865. ///////////
  866. #define SECONDS_FROM_100NS 10000000
  867. // ulResolveAttempts -- the number of times the NTP provider has attempted to
  868. // resolve this peer unsuccessfully. Setting this
  869. // value to 0 indicates that the peer has been successfully
  870. // resolved.
  871. // u64TimeRemaining -- the number of 100ns intervals until the provider will
  872. // poll this peer again
  873. // u64LastSuccessfulSync -- the number of 100ns intervals since (0h 1-Jan 1601)
  874. // ulLastSyncError -- S_OK if the last sync with this peer was successful, otherwise,
  875. // the error that occurred attempting to sync
  876. // ulLastSyncErrorMsgId -- the resource identifier of a string representing the last
  877. // error that occurred syncing from this peer. 0 if there is no
  878. // string associated with this error.
  879. // This function is called on the background thread.
  880. HRESULT W32TimeGetErrorInfoWrap(UINT * pulResolveAttempts, ULONG * pulValidDataCounter, UINT64 * pu64TimeRemaining, UINT64 * pu64LastSuccessfulSync, HRESULT * phrLastSyncError,
  881. UINT * pulLastSyncErrorMsgId, LPTSTR pszServer, DWORD cchSize, LPTSTR pszServerToQuery)
  882. {
  883. HRESULT hr = S_OK;
  884. *pulResolveAttempts = 0;
  885. *pulValidDataCounter = 0;
  886. *pu64TimeRemaining = 0;
  887. *pu64LastSuccessfulSync = 0;
  888. *phrLastSyncError = E_FAIL;
  889. *pulLastSyncErrorMsgId = 0;
  890. pszServer[0] = 0;
  891. // NOTE: Server should return ERROR_TIME_SKEW if time is too far out of date to sync.
  892. W32TIME_NTP_PROVIDER_DATA * pProviderInfo = NULL;
  893. DWORD dwError = W32TimeQueryNTPProviderStatus(SZ_COMPUTER_LOCAL, 0, SZ_NTPCLIENT, &pProviderInfo);
  894. if ((ERROR_SUCCESS == dwError) && pProviderInfo)
  895. {
  896. *phrLastSyncError = pProviderInfo->ulError;
  897. *pulLastSyncErrorMsgId = pProviderInfo->ulErrorMsgId;
  898. DWORD dwMostRecentlySyncdPeerIndex = 0xFFFFFFFF;
  899. UINT64 u64LastSuccessfulSync = 0;
  900. for (DWORD dwIndex = 0; dwIndex < pProviderInfo->cPeerInfo; dwIndex++)
  901. {
  902. // Only want to query those peers which we explictly sync'd from.
  903. // If we haven't explicitly requested a sync, we'll just take the most recently sync'd peer.
  904. if (NULL == pszServerToQuery || ComparePeers(pszServerToQuery, pProviderInfo->pPeerInfo[dwIndex].wszUniqueName))
  905. {
  906. if (u64LastSuccessfulSync <= pProviderInfo->pPeerInfo[dwIndex].u64LastSuccessfulSync)
  907. {
  908. dwMostRecentlySyncdPeerIndex = dwIndex;
  909. u64LastSuccessfulSync = pProviderInfo->pPeerInfo[dwIndex].u64LastSuccessfulSync;
  910. }
  911. }
  912. }
  913. if (dwMostRecentlySyncdPeerIndex < pProviderInfo->cPeerInfo && pProviderInfo->pPeerInfo)
  914. {
  915. *pulResolveAttempts = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulResolveAttempts;
  916. *pulValidDataCounter = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulValidDataCounter;
  917. *pu64TimeRemaining = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].u64TimeRemaining;
  918. *pu64LastSuccessfulSync = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].u64LastSuccessfulSync;
  919. *phrLastSyncError = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulLastSyncError;
  920. *pulLastSyncErrorMsgId = pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].ulLastSyncErrorMsgId;
  921. if (pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].wszUniqueName)
  922. {
  923. StringCchCopy( pszServer, cchSize, pProviderInfo->pPeerInfo[dwMostRecentlySyncdPeerIndex].wszUniqueName );
  924. // Strip off non-user friendly information which may have been tacked on to the peer name:
  925. LPTSTR pszJunk = StrStrW(pszServer, L" (");
  926. if (pszJunk)
  927. {
  928. pszJunk[0] = 0;
  929. }
  930. pszJunk = StrStrW(pszServer, L",");
  931. if (pszJunk)
  932. {
  933. pszJunk[0] = 0;
  934. }
  935. }
  936. }
  937. else
  938. {
  939. *phrLastSyncError = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
  940. if (NULL != pszServerToQuery)
  941. {
  942. StringCchCopy( pszServer, cchSize, pszServerToQuery );
  943. }
  944. }
  945. W32TimeBufferFree(pProviderInfo);
  946. }
  947. else
  948. {
  949. hr = ResultFromWin32(dwError);
  950. }
  951. return hr;
  952. }
  953. // pftTimeRemaining will be returned with the time/date of the next sync, not time from now.
  954. HRESULT W32TimeGetErrorInfoWrapHelper(UINT * pulResolveAttempts, ULONG * pulValidDataCounter, FILETIME * pftTimeRemaining, FILETIME * pftLastSuccessfulSync, HRESULT * phrLastSyncError,
  955. UINT * pulLastSyncErrorMsgId, LPTSTR pszServer, DWORD cchSize, LPTSTR pszServerToQuery)
  956. {
  957. UINT64 * pu64LastSuccessfulSync = (UINT64 *) pftLastSuccessfulSync;
  958. UINT64 u64TimeRemaining;
  959. HRESULT hr = W32TimeGetErrorInfoWrap(pulResolveAttempts, pulValidDataCounter, &u64TimeRemaining, pu64LastSuccessfulSync, phrLastSyncError, pulLastSyncErrorMsgId, pszServer, cchSize, pszServerToQuery);
  960. if (SUCCEEDED(hr))
  961. {
  962. SYSTEMTIME stCurrent;
  963. FILETIME ftCurrent;
  964. GetSystemTime(&stCurrent);
  965. SystemTimeToFileTime(&stCurrent, &ftCurrent);
  966. ULONGLONG * pNextSync = (ULONGLONG *) pftTimeRemaining;
  967. ULONGLONG * pCurrent = (ULONGLONG *) &ftCurrent;
  968. *pNextSync = (*pCurrent + u64TimeRemaining);
  969. }
  970. return hr;
  971. }
  972. // This function is called on the background thread.
  973. HRESULT CInternetTime::_GetDateTimeString(FILETIME * pftTimeDate, BOOL fDate, LPTSTR pszString, DWORD cchSize)
  974. {
  975. HRESULT hr = S_OK;
  976. TCHAR szFormat[MAX_PATH];
  977. pszString[0] = 0;
  978. if (GetLocaleInfo(LOCALE_USER_DEFAULT, (fDate ? LOCALE_SSHORTDATE : LOCALE_STIMEFORMAT), szFormat, ARRAYSIZE(szFormat)))
  979. {
  980. SYSTEMTIME stTimeDate;
  981. if (FileTimeToSystemTime(pftTimeDate, &stTimeDate))
  982. {
  983. if (fDate)
  984. {
  985. if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, &stTimeDate, szFormat, pszString, cchSize))
  986. {
  987. hr = ResultFromLastError();
  988. }
  989. }
  990. else
  991. {
  992. if (!GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &stTimeDate, szFormat, pszString, cchSize))
  993. {
  994. hr = ResultFromLastError();
  995. }
  996. }
  997. }
  998. else
  999. {
  1000. hr = ResultFromLastError();
  1001. }
  1002. }
  1003. else
  1004. {
  1005. hr = ResultFromLastError();
  1006. }
  1007. return hr;
  1008. }
  1009. HRESULT _CleanUpErrorString(LPWSTR pszTemp4, DWORD cchSize)
  1010. {
  1011. PathRemoveBlanks(pszTemp4);
  1012. while (TRUE)
  1013. {
  1014. DWORD cchSizeTemp = lstrlen(pszTemp4);
  1015. if (cchSizeTemp && ((TEXT('\n') == pszTemp4[cchSizeTemp-1]) ||
  1016. (13 == pszTemp4[cchSizeTemp-1])))
  1017. {
  1018. pszTemp4[cchSizeTemp-1] = 0;
  1019. }
  1020. else
  1021. {
  1022. break;
  1023. }
  1024. }
  1025. return S_OK;
  1026. }
  1027. // This function is called on the background thread.
  1028. HRESULT CInternetTime::_CreateW32TimeSuccessErrorString(DWORD dwError, LPTSTR pszString, DWORD cchSize, LPTSTR pszNextSync, DWORD cchNextSyncSize, LPTSTR pszServerToQuery)
  1029. {
  1030. HRESULT hr = S_OK;
  1031. UINT ulResolveAttempts = 0;
  1032. FILETIME ftTimeRemainingUTC = {0};
  1033. FILETIME ftLastSuccessfulSyncUTC = {0};
  1034. FILETIME ftTimeRemaining = {0};
  1035. FILETIME ftLastSuccessfulSync = {0};
  1036. UINT ulLastSyncErrorMsgId = 0;
  1037. TCHAR szServer[MAX_PATH];
  1038. HRESULT hrLastSyncError = 0;
  1039. ULONG ulValidDataCounter = 0;
  1040. TCHAR szTemplate[MAX_PATH];
  1041. TCHAR szTemp1[MAX_PATH];
  1042. TCHAR szTemp2[MAX_PATH];
  1043. TCHAR szTemp3[MAX_URL_STRING];
  1044. TCHAR szTemp4[MAX_URL_STRING];
  1045. pszString[0] = 0;
  1046. pszNextSync[0] = 0;
  1047. hr = W32TimeGetErrorInfoWrapHelper(&ulResolveAttempts, &ulValidDataCounter, &ftTimeRemainingUTC, &ftLastSuccessfulSyncUTC, &hrLastSyncError, &ulLastSyncErrorMsgId, szServer, ARRAYSIZE(szServer), pszServerToQuery);
  1048. if (SUCCEEDED(hr))
  1049. {
  1050. // ftTimeRemaining and ftLastSuccessfulSync are stored in UTC, so translate to our time zone.
  1051. FileTimeToLocalFileTime(&ftTimeRemainingUTC, &ftTimeRemaining);
  1052. FileTimeToLocalFileTime(&ftLastSuccessfulSyncUTC, &ftLastSuccessfulSync);
  1053. // Create the string showing the next time we plan on syncing.
  1054. LoadString(HINST_THISDLL, IDS_IT_NEXTSYNC, szTemplate, ARRAYSIZE(szTemplate));
  1055. if (SUCCEEDED(_GetDateTimeString(&ftTimeRemaining, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
  1056. SUCCEEDED(_GetDateTimeString(&ftTimeRemaining, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
  1057. FAILED(FormatMessageWedge(szTemplate, pszNextSync, cchNextSyncSize, szTemp1, szTemp2)))
  1058. {
  1059. pszNextSync[0] = 0;
  1060. }
  1061. if (ResyncResult_ChangeTooBig == dwError)
  1062. {
  1063. hrLastSyncError = E_FAIL;
  1064. }
  1065. if ((ResyncResult_NoData == dwError || ResyncResult_StaleData == dwError) && SUCCEEDED(hrLastSyncError))
  1066. {
  1067. // We've synchronized from our peer, but it didn't provide good enough samples to update our clock.
  1068. hrLastSyncError = HRESULT_FROM_WIN32(ERROR_TIMEOUT); // approximately the right error
  1069. }
  1070. // We should never hit the following case. But if the operation failed (NOT ResyncResult_Success)
  1071. // then we need hrLastSyncError to be a failure value.
  1072. if ((ResyncResult_Success != dwError) && SUCCEEDED(hrLastSyncError))
  1073. {
  1074. hrLastSyncError = E_FAIL;
  1075. }
  1076. switch (hrLastSyncError)
  1077. {
  1078. case S_OK:
  1079. if (!ftLastSuccessfulSyncUTC.dwLowDateTime && !ftLastSuccessfulSyncUTC.dwHighDateTime)
  1080. {
  1081. // We have never sync from the server.
  1082. LoadString(HINST_THISDLL, IDS_NEVER_TRIED_TOSYNC, pszString, cchSize);
  1083. }
  1084. else
  1085. {
  1086. if (szServer[0])
  1087. {
  1088. // Format: "Successfully synchronized the clock on 12/23/2001 at 11:03:32am from time.windows.com."
  1089. LoadString(HINST_THISDLL, IDS_IT_SUCCESS, szTemplate, ARRAYSIZE(szTemplate));
  1090. if (SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
  1091. SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
  1092. FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szTemp1, szTemp2, szServer)))
  1093. {
  1094. pszString[0] = 0;
  1095. }
  1096. }
  1097. else
  1098. {
  1099. // Format: "Successfully synchronized the clock on 12/23/2001 at 11:03:32am."
  1100. LoadString(HINST_THISDLL, IDS_IT_SUCCESS2, szTemplate, ARRAYSIZE(szTemplate));
  1101. if (SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
  1102. SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
  1103. FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szTemp1, szTemp2)))
  1104. {
  1105. pszString[0] = 0;
  1106. }
  1107. }
  1108. }
  1109. break;
  1110. case S_FALSE:
  1111. pszString[0] = 0;
  1112. break;
  1113. default:
  1114. if (ulValidDataCounter &&
  1115. ((0 != ftLastSuccessfulSyncUTC.dwLowDateTime) || (0 != ftLastSuccessfulSyncUTC.dwHighDateTime)))
  1116. {
  1117. // Getting this far means we may have failed this last sync attempt, but we have succeeded
  1118. // previously
  1119. hr = E_FAIL;
  1120. szTemp4[0] = 0; // This will be the error message.
  1121. szTemp3[0] = 0;
  1122. if (ResyncResult_ChangeTooBig == dwError)
  1123. {
  1124. // If the date is too far off, we fail the sync for security reasons. That happened
  1125. // here.
  1126. LoadString(HINST_THISDLL, IDS_ERR_DATETOOWRONG, szTemp4, ARRAYSIZE(szTemp4));
  1127. }
  1128. else if (ulLastSyncErrorMsgId) // We have a bonus error string.
  1129. {
  1130. HMODULE hW32Time = LoadLibrary( L"w32time.dll" ); // should have been loaded by linker already.
  1131. if (hW32Time)
  1132. {
  1133. // Load the specific reason the sync failed.
  1134. if (0 == FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID) hW32Time, ulLastSyncErrorMsgId, 0, szTemp4, ARRAYSIZE(szTemp4), NULL))
  1135. {
  1136. szTemp4[0] = 0; // We will get the value below
  1137. hr = S_OK;
  1138. }
  1139. else
  1140. {
  1141. _CleanUpErrorString(szTemp4, ARRAYSIZE(szTemp4));
  1142. }
  1143. FreeLibrary( hW32Time );
  1144. }
  1145. }
  1146. if ( 0 == szTemp4[0])
  1147. {
  1148. if (0 != FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_CODE(hrLastSyncError), 0, szTemp4, ARRAYSIZE(szTemp4), NULL))
  1149. {
  1150. _CleanUpErrorString(szTemp4, ARRAYSIZE(szTemp4));
  1151. }
  1152. else
  1153. {
  1154. szTemp4[0] = 0;
  1155. }
  1156. }
  1157. if (szTemp4[0])
  1158. {
  1159. LoadString(HINST_THISDLL, IDS_IT_FAIL1, szTemplate, ARRAYSIZE(szTemplate));
  1160. if (FAILED(FormatMessageWedge(szTemplate, szTemp3, ARRAYSIZE(szTemp3), szServer, szTemp4)))
  1161. {
  1162. szTemp3[0] = 0;
  1163. }
  1164. }
  1165. else
  1166. {
  1167. // <------------------------------------------------------------->
  1168. // Format:
  1169. // "An error occurred synchronizing the clock from time.windows.com
  1170. // on 2/3/2001 at 11:03:32am. Unable to connect to the server."
  1171. LoadString(HINST_THISDLL, IDS_IT_FAIL2, szTemplate, ARRAYSIZE(szTemplate));
  1172. if (FAILED(FormatMessageWedge(szTemplate, szTemp3, ARRAYSIZE(szTemp3), szServer)))
  1173. {
  1174. szTemp3[0] = 0;
  1175. }
  1176. hr = S_OK;
  1177. }
  1178. LoadString(HINST_THISDLL, IDS_IT_FAILLAST, szTemplate, ARRAYSIZE(szTemplate));
  1179. if (SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, TRUE, szTemp1, ARRAYSIZE(szTemp1))) && // Get the date
  1180. SUCCEEDED(_GetDateTimeString(&ftLastSuccessfulSync, FALSE, szTemp2, ARRAYSIZE(szTemp2))) && // Get the time
  1181. FAILED(FormatMessageWedge(szTemplate, szTemp4, ARRAYSIZE(szTemp4), szTemp1, szTemp2)))
  1182. {
  1183. szTemp4[0] = 0;
  1184. }
  1185. StringCchPrintf( pszString, cchSize, L"%s\n\n%s", szTemp3, szTemp4 );
  1186. }
  1187. else
  1188. {
  1189. // Getting this far means we may have failed to sync this time and we have never succeeded before.
  1190. if (ulLastSyncErrorMsgId) // We have a bonus error string.
  1191. {
  1192. szTemp3[0] = 0;
  1193. HMODULE hW32Time = LoadLibrary( L"w32time.dll" );
  1194. if (hW32Time)
  1195. {
  1196. // <------------------------------------------------------------->
  1197. // Format:
  1198. // "An error occurred synchronizing the clock from time.windows.com
  1199. // on 2/3/2001 at 11:03:32am. Unable to connect to the server."
  1200. if (0 == FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID) hW32Time, ulLastSyncErrorMsgId, 0, szTemp4, ARRAYSIZE(szTemp4), NULL))
  1201. {
  1202. szTemp4[0] = 0;
  1203. }
  1204. else
  1205. {
  1206. _CleanUpErrorString(szTemp4, ARRAYSIZE(szTemp4));
  1207. }
  1208. if (szServer[0])
  1209. {
  1210. LoadString(HINST_THISDLL, IDS_IT_FAIL1, szTemplate, ARRAYSIZE(szTemplate));
  1211. if (FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szServer, szTemp4)))
  1212. {
  1213. pszString[0] = 0;
  1214. }
  1215. }
  1216. else
  1217. {
  1218. LoadString(HINST_THISDLL, IDS_IT_FAIL3, szTemplate, ARRAYSIZE(szTemplate));
  1219. if (FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szTemp4)))
  1220. {
  1221. pszString[0] = 0;
  1222. }
  1223. }
  1224. FreeLibrary( hW32Time );
  1225. }
  1226. }
  1227. else
  1228. {
  1229. // <------------------------------------------------------------->
  1230. // Format:
  1231. // "An error occurred synchronizing the clock from time.windows.com
  1232. // on 2/3/2001 at 11:03:32am. Unable to connect to the server."
  1233. LoadString(HINST_THISDLL, IDS_IT_FAIL2, szTemplate, ARRAYSIZE(szTemplate));
  1234. if (FAILED(FormatMessageWedge(szTemplate, pszString, cchSize, szServer)))
  1235. {
  1236. pszString[0] = 0;
  1237. }
  1238. }
  1239. }
  1240. break;
  1241. };
  1242. }
  1243. else
  1244. {
  1245. LoadString(HINST_THISDLL, IDS_ERR_GETINFO_FAIL, szTemplate, ARRAYSIZE(szTemplate));
  1246. if (0 == FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_CODE(hr), 0, szTemp1, ARRAYSIZE(szTemp1), NULL))
  1247. {
  1248. szTemp1[0] = 0;
  1249. }
  1250. else
  1251. {
  1252. _CleanUpErrorString(szTemp1, ARRAYSIZE(szTemp1));
  1253. }
  1254. StringCchPrintf( pszString, cchSize, szTemplate, szTemp1 );
  1255. }
  1256. return hr;
  1257. }
  1258. ///////////
  1259. // These functions will cause the background thread to check if we should add the "Internet Time" tab.
  1260. ///////////
  1261. // This function is called on the background thread.
  1262. DWORD CALLBACK _AsyncCheckDCThread(void * pv)
  1263. {
  1264. if (g_pInternetTime)
  1265. {
  1266. g_pInternetTime->AsyncCheck();
  1267. }
  1268. return 0;
  1269. }
  1270. // This function is called on the background thread.
  1271. BOOL CInternetTime::IsInternetTimeAvailable(void)
  1272. {
  1273. return SHRegGetBoolUSValue(SZ_REGKEY_DATETIME, SZ_REGVALUE_INTERNET_FEATURE_AVAILABLE, FALSE, FEATURE_INTERNET_TIME);
  1274. }
  1275. // This function is called on the background thread.
  1276. EXTERN_C BOOL DoesTimeComeFromDC(void)
  1277. {
  1278. BOOL fTimeFromDomain = FALSE;
  1279. LPWSTR pszDomain = NULL;
  1280. NETSETUP_JOIN_STATUS joinStatus = NetSetupUnknownStatus;
  1281. DWORD dwError = NetGetJoinInformation(NULL, &pszDomain, &joinStatus);
  1282. // We will act like there isn't a DC if we have the test registry set.
  1283. if (NERR_Success == dwError)
  1284. {
  1285. // If we are connected to a domain, we need to do the expensive net search
  1286. // to see that is where we will get the time from.
  1287. if (NetSetupDomainName == joinStatus)
  1288. {
  1289. PDOMAIN_CONTROLLER_INFO pdomainInfo = {0};
  1290. dwError = DsGetDcName(NULL, NULL, NULL, NULL, DS_TIMESERV_REQUIRED, &pdomainInfo);
  1291. // We will act like there isn't a DC if we have the test registry set.
  1292. if (ERROR_SUCCESS == dwError)
  1293. {
  1294. if (FALSE == SHRegGetBoolUSValue(SZ_REGKEY_DATETIME, SZ_REGVALUE_TEST_SIMULATENODC, FALSE, FALSE))
  1295. {
  1296. fTimeFromDomain = TRUE;
  1297. }
  1298. NetApiBufferFree(pdomainInfo);
  1299. }
  1300. }
  1301. if (pszDomain)
  1302. {
  1303. NetApiBufferFree(pszDomain);
  1304. }
  1305. }
  1306. return fTimeFromDomain;
  1307. }
  1308. // This function is called on the background thread.
  1309. HRESULT CInternetTime::_SyncNow(BOOL fOnlyUpdateInfo)
  1310. {
  1311. HRESULT hr = S_OK;
  1312. ENTERCRITICAL;
  1313. BOOL fContinue = ((eBKAUpdate == m_eAction) || (eBKAGetInfo == m_eAction));
  1314. if (fContinue)
  1315. {
  1316. m_eAction = eBKAUpdating;
  1317. }
  1318. LEAVECRITICAL;
  1319. if (fContinue)
  1320. {
  1321. hr = E_OUTOFMEMORY;
  1322. DWORD cchSize = 4024;
  1323. LPTSTR pszString = (LPTSTR) LocalAlloc(LPTR, sizeof(pszString[0]) * cchSize);
  1324. if (pszString)
  1325. {
  1326. WCHAR szExistingServer[MAX_URL_STRING];
  1327. HRESULT hrServer = E_FAIL;
  1328. TCHAR szNewServer[MAX_PATH];
  1329. TCHAR szNextSync[MAX_PATH];
  1330. DWORD dwError = 0;
  1331. DWORD dwSyncFlags;
  1332. if (!fOnlyUpdateInfo)
  1333. {
  1334. hrServer = GetW32TimeServer(FALSE, szExistingServer, ARRAYSIZE(szExistingServer));
  1335. GetWindowText(GetDlgItem(m_hwndInternet, DATETIME_INTERNET_SERVER_EDIT), szNextSync, ARRAYSIZE(szNextSync));
  1336. // save the new server away for future processing
  1337. StringCchCopy( szNewServer, ARRAYSIZE(szNewServer), szNextSync );
  1338. if (!ContainsServer(szExistingServer, szNextSync))
  1339. {
  1340. // The servers don't match. We want to add the new server to the beginning of the list. This
  1341. // will work around a problem in W32time. If we don't do this, then:
  1342. // 1. It will cause a second sync with the original server (which is bad for perf and affects statistics)
  1343. // 2. The Last Updated sync time will then be from the wrong peer. This is really bad because it's result is basic
  1344. // on the user's previously bad time
  1345. // 3. It will do an extra DNS resolution causing slowness on our side, increasing server traffic, and dragging down
  1346. // the intranet.
  1347. TCHAR szTemp[MAX_URL_STRING];
  1348. StringCchCopy( szTemp, ARRAYSIZE(szTemp), szNextSync );
  1349. StringCchPrintf( szNextSync, ARRAYSIZE(szNextSync), L"%s %s", szTemp, szExistingServer );
  1350. dwSyncFlags = TimeSyncFlag_ReturnResult | TimeSyncFlag_UpdateAndResync;
  1351. }
  1352. else
  1353. {
  1354. // We've already got this server in our list of servers. Just cause our peers to resync.
  1355. dwSyncFlags = TimeSyncFlag_ReturnResult | TimeSyncFlag_HardResync;
  1356. }
  1357. SetW32TimeServer(szNextSync);
  1358. // I will ignore the error value because I will get the error info in _CreateW32TimeSuccessErrorString.
  1359. dwError = W32TimeSyncNow(SZ_COMPUTER_LOCAL, TRUE /* Synchronous */, dwSyncFlags);
  1360. if ((ResyncResult_StaleData == dwError) && (0 == (TimeSyncFlag_HardResync & dwSyncFlags)))
  1361. {
  1362. // We've got stale data preventing us from resyncing. Try again with a full resync.
  1363. dwSyncFlags = TimeSyncFlag_ReturnResult | TimeSyncFlag_HardResync;
  1364. dwError = W32TimeSyncNow(SZ_COMPUTER_LOCAL, TRUE /* Synchronous */, dwSyncFlags);
  1365. }
  1366. }
  1367. pszString[0] = 0;
  1368. szNextSync[0] = 0;
  1369. hr = _CreateW32TimeSuccessErrorString(dwError, pszString, cchSize, szNextSync, ARRAYSIZE(szNextSync), (SUCCEEDED(hrServer) ? szNewServer : NULL));
  1370. if (SUCCEEDED(hrServer))
  1371. {
  1372. SetW32TimeServer(szExistingServer);
  1373. _StartServiceAndRefresh(TRUE); // Make sure the service is on and make it update it's settings.
  1374. }
  1375. ENTERCRITICAL;
  1376. Str_SetPtr(&m_pszNextSyncTime, szNextSync);
  1377. if (m_pszStatusString)
  1378. {
  1379. LocalFree(m_pszStatusString);
  1380. }
  1381. m_pszStatusString = pszString;
  1382. PostMessage(m_hwndInternet, WMUSER_UPDATED_STATUS_TEXT, 0, 0); // Tell the forground thread to pick up the new string.
  1383. m_eAction = eBKAWait;
  1384. LEAVECRITICAL;
  1385. }
  1386. }
  1387. return hr;
  1388. }
  1389. // This function is called on the background thread.
  1390. void CInternetTime::AsyncCheck(void)
  1391. {
  1392. HRESULT hr = S_OK;
  1393. if (m_hDlg && !DoesTimeComeFromDC())
  1394. {
  1395. // Tell the forground thread to add us.
  1396. PostMessage(m_hwndDate, WMUSER_ADDINTERNETTAB, 0, 0);
  1397. _ProcessBkThreadActions();
  1398. }
  1399. }
  1400. // This function is called on the background thread.
  1401. HRESULT CInternetTime::_ProcessBkThreadActions(void)
  1402. {
  1403. HRESULT hr = S_OK;
  1404. while (eBKAQuit != m_eAction) // Okay since we are only reading
  1405. {
  1406. switch (m_eAction)
  1407. {
  1408. case eBKAGetInfo:
  1409. _SyncNow(TRUE);
  1410. break;
  1411. case eBKAUpdate:
  1412. _SyncNow(FALSE);
  1413. break;
  1414. case eBKAUpdating:
  1415. case eBKAWait:
  1416. default:
  1417. Sleep(300); // We don't care if there is up to 100ms latency between button press and action starting.
  1418. break;
  1419. }
  1420. }
  1421. return hr;
  1422. }
  1423. /////////////////////////////////////////////////////////////////////
  1424. // Public Functions
  1425. /////////////////////////////////////////////////////////////////////
  1426. // This function is called on the forground thread.
  1427. EXTERN_C HRESULT AddInternetPageAsync(HWND hDlg, HWND hwndDate)
  1428. {
  1429. HRESULT hr = E_OUTOFMEMORY;
  1430. if (!g_pInternetTime)
  1431. {
  1432. g_pInternetTime = new CInternetTime(hDlg, hwndDate);
  1433. }
  1434. if (g_pInternetTime)
  1435. {
  1436. // We only want to add the page that allows the user to get the time from the internet if:
  1437. // 1. The feature is turned on, and
  1438. // 2. The user doesn't get the time from an intranet Domain Control.
  1439. if (g_pInternetTime->IsInternetTimeAvailable())
  1440. {
  1441. // Start the thread to find out if we are in a domain and need the advanced page. We need
  1442. // to do this on a background thread because the DsGetDcName() API may take 10-20 seconds.
  1443. hr = (SHCreateThread(_AsyncCheckDCThread, hDlg, (CTF_INSIST | CTF_FREELIBANDEXIT), NULL) ? S_OK : E_FAIL);
  1444. }
  1445. }
  1446. return hr;
  1447. }
  1448. // This function is called on the forground thread.
  1449. EXTERN_C HRESULT AddInternetTab(HWND hDlg)
  1450. {
  1451. HRESULT hr = E_OUTOFMEMORY;
  1452. if (g_pInternetTime)
  1453. {
  1454. hr = g_pInternetTime->AddInternetPage();
  1455. }
  1456. return hr;
  1457. }