Source code of Windows XP (NT5)
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.

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