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.

1965 lines
60 KiB

  1. #include "cabinet.h"
  2. #include "rcids.h"
  3. #include <regstr.h>
  4. #include "startmnu.h"
  5. #include <shdguid.h> // for IID_IShellService
  6. #include <shlguid.h>
  7. #include <desktray.h>
  8. #include <wininet.h>
  9. #include <trayp.h>
  10. #include "tray.h"
  11. #include "util.h"
  12. #include "atlstuff.h"
  13. #include <runonce.c> // shared runonce processing code
  14. // global so that it is shared between TS sessions
  15. #define SZ_SCMCREATEDEVENT_NT5 TEXT("Global\\ScmCreatedEvent")
  16. #define SZ_WINDOWMETRICS TEXT("Control Panel\\Desktop\\WindowMetrics")
  17. #define SZ_APPLIEDDPI TEXT("AppliedDPI")
  18. #define SZ_CONTROLPANEL TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel")
  19. #define SZ_ORIGINALDPI TEXT("OriginalDPI")
  20. // exports from shdocvw.dll
  21. STDAPI_(void) RunInstallUninstallStubs(void);
  22. int ExplorerWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR pszCmdLine, int nCmdShow);
  23. #ifdef FEATURE_STARTPAGE
  24. #include <DUIThread.h>
  25. void CreateStartPage(HINSTANCE hInstance, HANDLE hDesktop);
  26. BOOL Tray_ShowStartPageEnabled();
  27. #endif
  28. BOOL _ShouldFixResolution(void);
  29. #ifdef PERF_ENABLESETMARK
  30. #include <wmistr.h>
  31. #include <ntwmi.h> // PWMI_SET_MARK_INFORMATION is defined in ntwmi.h
  32. #include <wmiumkm.h>
  33. #define NTPERF
  34. #include <ntperf.h>
  35. void DoSetMark(LPCSTR pszMark, ULONG cbSz)
  36. {
  37. PWMI_SET_MARK_INFORMATION MarkInfo;
  38. HANDLE hTemp;
  39. ULONG cbBufferSize;
  40. ULONG cbReturnSize;
  41. cbBufferSize = FIELD_OFFSET(WMI_SET_MARK_INFORMATION, Mark) + cbSz;
  42. MarkInfo = (PWMI_SET_MARK_INFORMATION) LocalAlloc(LPTR, cbBufferSize);
  43. // Failed to init, no big deal
  44. if (MarkInfo == NULL)
  45. return;
  46. BYTE *pMarkBuffer = (BYTE *) (&MarkInfo->Mark[0]);
  47. memcpy(pMarkBuffer, pszMark, cbSz);
  48. // WMI_SET_MARK_WITH_FLUSH will flush the working set when setting the mark
  49. MarkInfo->Flag = PerformanceMmInfoMark;
  50. hTemp = CreateFile(WMIDataDeviceName,
  51. GENERIC_READ | GENERIC_WRITE,
  52. 0,
  53. NULL,
  54. OPEN_EXISTING,
  55. FILE_ATTRIBUTE_NORMAL |
  56. FILE_FLAG_OVERLAPPED,
  57. NULL);
  58. if (hTemp != INVALID_HANDLE_VALUE)
  59. {
  60. // here's the piece that actually puts the mark in the buffer
  61. BOOL fIoctlSuccess = DeviceIoControl(hTemp,
  62. IOCTL_WMI_SET_MARK,
  63. MarkInfo,
  64. cbBufferSize,
  65. NULL,
  66. 0,
  67. &cbReturnSize,
  68. NULL);
  69. CloseHandle(hTemp);
  70. }
  71. LocalFree(MarkInfo);
  72. }
  73. #endif // PERF_ENABLESETMARK
  74. //Do not change this stock5.lib use this as a BOOL not a bit.
  75. BOOL g_bMirroredOS = FALSE;
  76. HINSTANCE hinstCabinet = 0;
  77. CRITICAL_SECTION g_csDll = { 0 };
  78. HKEY g_hkeyExplorer = NULL;
  79. #define MAGIC_FAULT_TIME (1000 * 60 * 5)
  80. #define MAGIC_FAULT_LIMIT (2)
  81. BOOL g_fLogonCycle = FALSE;
  82. BOOL g_fCleanShutdown = TRUE;
  83. BOOL g_fExitExplorer = TRUE; // set to FALSE on WM_ENDSESSION shutdown case
  84. BOOL g_fEndSession = FALSE; // set to TRUE if we rx a WM_ENDSESSION during RunOnce etc
  85. BOOL g_fFakeShutdown = FALSE; // set to TRUE if we do Ctrl+Alt+Shift+Cancel shutdown
  86. DWORD g_dwStopWatchMode; // to minimize impact of perf logging on retail
  87. // helper function to check to see if a given regkey is has any subkeys
  88. BOOL SHKeyHasSubkeys(HKEY hk, LPCTSTR pszSubKey)
  89. {
  90. HKEY hkSub;
  91. BOOL bHasSubKeys = FALSE;
  92. // need to open this with KEY_QUERY_VALUE or else RegQueryInfoKey will fail
  93. if (RegOpenKeyEx(hk,
  94. pszSubKey,
  95. 0,
  96. (KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE),
  97. &hkSub) == ERROR_SUCCESS)
  98. {
  99. DWORD dwSubKeys;
  100. if (RegQueryInfoKey(hkSub, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
  101. {
  102. bHasSubKeys = (dwSubKeys != 0);
  103. }
  104. RegCloseKey(hkSub);
  105. }
  106. return bHasSubKeys;
  107. }
  108. #ifdef _WIN64
  109. // helper function to check to see if a given regkey is has values (ignores the default value)
  110. BOOL SHKeyHasValues(HKEY hk, LPCTSTR pszSubKey)
  111. {
  112. HKEY hkSub;
  113. BOOL bHasValues = FALSE;
  114. if (RegOpenKeyEx(hk,
  115. pszSubKey,
  116. 0,
  117. KEY_QUERY_VALUE,
  118. &hkSub) == ERROR_SUCCESS)
  119. {
  120. DWORD dwValues;
  121. DWORD dwSubKeys;
  122. if (RegQueryInfoKey(hkSub, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, &dwValues, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
  123. {
  124. bHasValues = (dwValues != 0);
  125. }
  126. RegCloseKey(hkSub);
  127. }
  128. return bHasValues;
  129. }
  130. #endif // _WIN64
  131. void CreateShellDirectories()
  132. {
  133. TCHAR szPath[MAX_PATH];
  134. // Create the shell directories if they don't exist
  135. SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, TRUE);
  136. SHGetSpecialFolderPath(NULL, szPath, CSIDL_PROGRAMS, TRUE);
  137. SHGetSpecialFolderPath(NULL, szPath, CSIDL_STARTMENU, TRUE);
  138. SHGetSpecialFolderPath(NULL, szPath, CSIDL_STARTUP, TRUE);
  139. SHGetSpecialFolderPath(NULL, szPath, CSIDL_RECENT, TRUE);
  140. SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE);
  141. }
  142. // returns:
  143. // TRUE if the user wants to abort the startup sequence
  144. // FALSE keep going
  145. //
  146. // note: this is a switch, once on it will return TRUE to all
  147. // calls so these keys don't need to be pressed the whole time
  148. BOOL AbortStartup()
  149. {
  150. static BOOL bAborted = FALSE; // static so it sticks!
  151. if (bAborted)
  152. {
  153. return TRUE; // don't do funky startup stuff
  154. }
  155. else
  156. {
  157. bAborted = (g_fCleanBoot || ((GetKeyState(VK_CONTROL) < 0) || (GetKeyState(VK_SHIFT) < 0)));
  158. return bAborted;
  159. }
  160. }
  161. BOOL ExecStartupEnumProc(IShellFolder *psf, LPITEMIDLIST pidlItem)
  162. {
  163. IContextMenu *pcm;
  164. HRESULT hr = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlItem, IID_PPV_ARG_NULL(IContextMenu, &pcm));
  165. if (SUCCEEDED(hr))
  166. {
  167. HMENU hmenu = CreatePopupMenu();
  168. if (hmenu)
  169. {
  170. pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, CMF_DEFAULTONLY);
  171. INT idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  172. if (idCmd)
  173. {
  174. CMINVOKECOMMANDINFOEX ici = {0};
  175. ici.cbSize = sizeof(ici);
  176. ici.fMask = CMIC_MASK_FLAG_NO_UI;
  177. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CONTEXTMENU_IDCMD_FIRST);
  178. ici.nShow = SW_NORMAL;
  179. if (FAILED(pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici)))
  180. {
  181. c_tray.LogFailedStartupApp();
  182. }
  183. }
  184. DestroyMenu(hmenu);
  185. }
  186. pcm->Release();
  187. }
  188. return !AbortStartup();
  189. }
  190. typedef BOOL (*PFNENUMFOLDERCALLBACK)(IShellFolder *psf, LPITEMIDLIST pidlItem);
  191. void EnumFolder(LPITEMIDLIST pidlFolder, DWORD grfFlags, PFNENUMFOLDERCALLBACK pfn)
  192. {
  193. IShellFolder *psf;
  194. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlFolder, &psf))))
  195. {
  196. IEnumIDList *penum;
  197. if (S_OK == psf->EnumObjects(NULL, grfFlags, &penum))
  198. {
  199. LPITEMIDLIST pidl;
  200. ULONG celt;
  201. while (S_OK == penum->Next(1, &pidl, &celt))
  202. {
  203. BOOL bRet = pfn(psf, pidl);
  204. SHFree(pidl);
  205. if (!bRet)
  206. break;
  207. }
  208. penum->Release();
  209. }
  210. psf->Release();
  211. }
  212. }
  213. const UINT c_rgStartupFolders[] = {
  214. CSIDL_COMMON_STARTUP,
  215. CSIDL_COMMON_ALTSTARTUP, // non-localized "Common StartUp" group if exists.
  216. CSIDL_STARTUP,
  217. CSIDL_ALTSTARTUP // non-localized "StartUp" group if exists.
  218. };
  219. void _ExecuteStartupPrograms()
  220. {
  221. if (!AbortStartup())
  222. {
  223. for (int i = 0; i < ARRAYSIZE(c_rgStartupFolders); i++)
  224. {
  225. LPITEMIDLIST pidlStartup = SHCloneSpecialIDList(NULL, c_rgStartupFolders[i], FALSE);
  226. if (pidlStartup)
  227. {
  228. EnumFolder(pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, ExecStartupEnumProc);
  229. ILFree(pidlStartup);
  230. }
  231. }
  232. }
  233. }
  234. // helper function for parsing the run= stuff
  235. BOOL ExecuteOldEqualsLine(LPTSTR pszCmdLine, int nCmdShow)
  236. {
  237. BOOL bRet = FALSE;
  238. BOOL bFinished = FALSE;
  239. TCHAR szWindowsDir[MAX_PATH];
  240. // Load and Run lines are done relative to windows directory.
  241. GetWindowsDirectory(szWindowsDir, ARRAYSIZE(szWindowsDir));
  242. while (!bFinished && !AbortStartup())
  243. {
  244. LPTSTR pEnd = pszCmdLine;
  245. // NOTE: I am guessing from the code below that you can have multiple entries seperated
  246. // by a ' ' or a ',' and we will exec all of them.
  247. while ((*pEnd) && (*pEnd != TEXT(' ')) && (*pEnd != TEXT(',')))
  248. {
  249. pEnd = (LPTSTR)CharNext(pEnd);
  250. }
  251. if (*pEnd == 0)
  252. {
  253. bFinished = TRUE;
  254. }
  255. else
  256. {
  257. *pEnd = 0;
  258. }
  259. if (lstrlen(pszCmdLine) != 0)
  260. {
  261. SHELLEXECUTEINFO ei = {0};
  262. ei.cbSize = sizeof(ei);
  263. ei.lpFile = pszCmdLine;
  264. ei.lpDirectory = szWindowsDir;
  265. ei.nShow = nCmdShow;
  266. if (!ShellExecuteEx(&ei))
  267. {
  268. ShellMessageBox(hinstCabinet,
  269. NULL,
  270. MAKEINTRESOURCE(IDS_WINININORUN),
  271. MAKEINTRESOURCE(IDS_DESKTOP),
  272. MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL,
  273. pszCmdLine);
  274. }
  275. else
  276. {
  277. bRet = TRUE;
  278. }
  279. }
  280. pszCmdLine = pEnd + 1;
  281. }
  282. return bRet;
  283. }
  284. // we check for the old "load=" and "run=" from the [Windows] section of the win.ini, which
  285. // is mapped nowadays to HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
  286. BOOL _ProcessOldRunAndLoadEquals()
  287. {
  288. BOOL bRet = FALSE;
  289. // don't do the run= section if we are in safemode
  290. if (!g_fCleanBoot)
  291. {
  292. HKEY hk;
  293. if (RegOpenKeyEx(HKEY_CURRENT_USER,
  294. TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"),
  295. 0,
  296. KEY_QUERY_VALUE,
  297. &hk) == ERROR_SUCCESS)
  298. {
  299. DWORD dwType;
  300. DWORD cbData;
  301. TCHAR szBuffer[255]; // max size of load= & run= lines...
  302. // "Load" apps before "Run"ning any.
  303. cbData = sizeof(szBuffer);
  304. if ((SHGetValue(hk, NULL, TEXT("Load"), &dwType, (void*)szBuffer, &cbData) == ERROR_SUCCESS) &&
  305. (dwType == REG_SZ))
  306. {
  307. // we want load= to be hidden, so SW_SHOWMINNOACTIVE is needed
  308. if (ExecuteOldEqualsLine(szBuffer, SW_SHOWMINNOACTIVE))
  309. {
  310. bRet = TRUE;
  311. }
  312. }
  313. cbData = sizeof(szBuffer);
  314. if ((SHGetValue(hk, NULL, TEXT("Run"), &dwType, (void*)szBuffer, &cbData) == ERROR_SUCCESS) &&
  315. (dwType == REG_SZ))
  316. {
  317. if (ExecuteOldEqualsLine(szBuffer, SW_SHOWNORMAL))
  318. {
  319. bRet = TRUE;
  320. }
  321. }
  322. RegCloseKey(hk);
  323. }
  324. }
  325. return bRet;
  326. }
  327. //---------------------------------------------------------------------------
  328. // Use IERnonce.dll to process RunOnceEx key
  329. //
  330. typedef void (WINAPI *RUNONCEEXPROCESS)(HWND, HINSTANCE, LPSTR, int);
  331. BOOL _ProcessRunOnceEx()
  332. {
  333. BOOL bRet = FALSE;
  334. if (SHKeyHasSubkeys(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCEEX))
  335. {
  336. PROCESS_INFORMATION pi = {0};
  337. TCHAR szArgString[MAX_PATH];
  338. TCHAR szRunDll32[MAX_PATH];
  339. BOOL fInTSInstallMode = FALSE;
  340. // See if we are in "Applications Server" mode, if so we need to trigger install mode
  341. if (IsOS(OS_TERMINALSERVER))
  342. {
  343. fInTSInstallMode = SHSetTermsrvAppInstallMode(TRUE);
  344. }
  345. // we used to call LoadLibrary("IERNONCE.DLL") and do all of the processing in-proc. Since
  346. // ierunonce.dll in turn calls LoadLibrary on whatever is in the registry and those setup dll's
  347. // can leak handles, we do this all out-of-proc now.
  348. GetSystemDirectory(szArgString, ARRAYSIZE(szArgString));
  349. PathAppend(szArgString, TEXT("iernonce.dll"));
  350. PathQuoteSpaces(szArgString);
  351. StrCatBuff(szArgString, TEXT(",RunOnceExProcess"), ARRAYSIZE(szArgString));
  352. GetSystemDirectory(szRunDll32, ARRAYSIZE(szRunDll32));
  353. PathAppend(szRunDll32, TEXT("rundll32.exe"));
  354. if (CreateProcessWithArgs(szRunDll32, szArgString, NULL, &pi))
  355. {
  356. SHProcessMessagesUntilEvent(NULL, pi.hProcess, INFINITE);
  357. CloseHandle(pi.hProcess);
  358. CloseHandle(pi.hThread);
  359. bRet = TRUE;
  360. }
  361. if (fInTSInstallMode)
  362. {
  363. SHSetTermsrvAppInstallMode(FALSE);
  364. }
  365. }
  366. #ifdef _WIN64
  367. //
  368. // check and see if we need to do 32-bit RunOnceEx processing for wow64
  369. //
  370. if (SHKeyHasSubkeys(HKEY_LOCAL_MACHINE, TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx")))
  371. {
  372. TCHAR szWow64Path[MAX_PATH];
  373. if (ExpandEnvironmentStrings(TEXT("%SystemRoot%\\SysWOW64"), szWow64Path, ARRAYSIZE(szWow64Path)))
  374. {
  375. TCHAR sz32BitRunOnce[MAX_PATH];
  376. PROCESS_INFORMATION pi = {0};
  377. wnsprintf(sz32BitRunOnce, ARRAYSIZE(sz32BitRunOnce), TEXT("%s\\runonce.exe"), szWow64Path);
  378. if (CreateProcessWithArgs(sz32BitRunOnce, TEXT("/RunOnceEx6432"), szWow64Path, &pi))
  379. {
  380. // have to wait for the ruonceex processing before we can return
  381. SHProcessMessagesUntilEvent(NULL, pi.hProcess, INFINITE);
  382. CloseHandle(pi.hProcess);
  383. CloseHandle(pi.hThread);
  384. bRet = TRUE;
  385. }
  386. }
  387. }
  388. #endif // _WIN64
  389. return bRet;
  390. }
  391. BOOL _ProcessRunOnce()
  392. {
  393. BOOL bRet = FALSE;
  394. if (!SHRestricted(REST_NOLOCALMACHINERUNONCE))
  395. {
  396. bRet = Cabinet_EnumRegApps(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCE, RRA_DELETE | RRA_WAIT, ExecuteRegAppEnumProc, 0);
  397. #ifdef _WIN64
  398. //
  399. // check and see if we need to do 32-bit RunOnce processing for wow64
  400. //
  401. // NOTE: we do not support per-user (HKCU) 6432 runonce
  402. //
  403. if (SHKeyHasValues(HKEY_LOCAL_MACHINE, TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")))
  404. {
  405. TCHAR szWow64Path[MAX_PATH];
  406. if (ExpandEnvironmentStrings(TEXT("%SystemRoot%\\SysWOW64"), szWow64Path, ARRAYSIZE(szWow64Path)))
  407. {
  408. TCHAR sz32BitRunOnce[MAX_PATH];
  409. PROCESS_INFORMATION pi = {0};
  410. wnsprintf(sz32BitRunOnce, ARRAYSIZE(sz32BitRunOnce), TEXT("%s\\runonce.exe"), szWow64Path);
  411. // NOTE: since the 32-bit and 64-bit registries are different, we don't wait since it should not affect us
  412. if (CreateProcessWithArgs(sz32BitRunOnce, TEXT("/RunOnce6432"), szWow64Path, &pi))
  413. {
  414. CloseHandle(pi.hProcess);
  415. CloseHandle(pi.hThread);
  416. bRet = TRUE;
  417. }
  418. }
  419. }
  420. #endif // _WIN64
  421. }
  422. return bRet;
  423. }
  424. #define REGTIPS REGSTR_PATH_EXPLORER TEXT("\\Tips")
  425. #define SZ_REGKEY_SRVWIZ_ROOT TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\srvWiz")
  426. #define SZ_REGVAL_SRVWIZ_RUN_ALWAYS TEXT("CYSMustRun")
  427. // Srvwiz is the Configure Your Server Wizard that runs on srv and ads skus
  428. bool _ShouldStartCys()
  429. {
  430. bool result = false;
  431. do
  432. {
  433. // Only run on srv or ads sku
  434. if (!IsOS(OS_SERVER) && !IsOS(OS_ADVSERVER))
  435. {
  436. break;
  437. }
  438. if (!IsUserAnAdmin())
  439. {
  440. break;
  441. }
  442. DWORD dwType = 0;
  443. DWORD dwData = 0;
  444. DWORD cbSize = sizeof(dwData);
  445. // if the must-run value is present and non-zero, then we need to
  446. // start the wizard.
  447. if (
  448. SHGetValue(
  449. HKEY_LOCAL_MACHINE,
  450. SZ_REGKEY_SRVWIZ_ROOT,
  451. SZ_REGVAL_SRVWIZ_RUN_ALWAYS,
  452. &dwType,
  453. reinterpret_cast<BYTE*>(&dwData),
  454. &cbSize) == ERROR_SUCCESS)
  455. {
  456. if (dwData)
  457. {
  458. result = true;
  459. break;
  460. }
  461. }
  462. // If the user's preference is present and zero, then don't show
  463. // the wizard, else continue with other tests
  464. if (
  465. !SHGetValue(
  466. HKEY_CURRENT_USER,
  467. REGTIPS,
  468. TEXT("Show"),
  469. NULL,
  470. reinterpret_cast<BYTE*>(&dwData),
  471. &cbSize))
  472. {
  473. if (!dwData)
  474. {
  475. break;
  476. }
  477. }
  478. // If the user's preference is absent or non-zero, then we need to
  479. // start the wizard.
  480. dwData = 0;
  481. if (
  482. SHGetValue(
  483. HKEY_CURRENT_USER,
  484. SZ_REGKEY_SRVWIZ_ROOT,
  485. NULL,
  486. &dwType,
  487. reinterpret_cast<BYTE*>(&dwData),
  488. &cbSize) != ERROR_SUCCESS)
  489. {
  490. result = true;
  491. break;
  492. }
  493. if (dwData)
  494. {
  495. result = true;
  496. }
  497. }
  498. while (0);
  499. return result;
  500. }
  501. void _RunWelcome()
  502. {
  503. if (_ShouldStartCys())
  504. {
  505. // NTRAID #94718: The SHGetValue above should be replaced with an SHRestricted call. The above is a highly non-standard
  506. // place for this "policy" to live plus it doesn't allow for per machine and per user settings.
  507. TCHAR szCmdLine[MAX_PATH];
  508. PROCESS_INFORMATION pi;
  509. // launch Configure Your Server for system administrators on Win2000 Server and Advanced Server
  510. GetSystemDirectory(szCmdLine, ARRAYSIZE(szCmdLine));
  511. PathAppend(szCmdLine, TEXT("cys.exe"));
  512. if (CreateProcessWithArgs(szCmdLine, TEXT("/explorer"), NULL, &pi))
  513. {
  514. // OLE created a secret window for us, so we can't use
  515. // WaitForSingleObject or we will deadlock
  516. SHWaitForSendMessageThread(pi.hProcess, INFINITE);
  517. CloseHandle(pi.hProcess);
  518. CloseHandle(pi.hThread);
  519. }
  520. }
  521. // Once that's all done, see if the Start Menu needs to auto-open.
  522. // Don't auto-open if we are going to offer to fix the user's screen
  523. // resolution, though, because that causes us to cover up the screen
  524. // resolution fix wizard! The screen resolution fix wizard will post
  525. // this message when the user has finished fixing the screen.
  526. //
  527. // Also, don't auto-open if we are on Tablet. Tablet's OOBE will post
  528. // us the Welcome Finished message when it's ready.
  529. //
  530. if (!_ShouldFixResolution() &&
  531. !IsOS(OS_TABLETPC))
  532. {
  533. PostMessage(v_hwndTray, RegisterWindowMessage(TEXT("Welcome Finished")), 0, 0);
  534. }
  535. }
  536. // On NT, run the TASKMAN= line from the registry
  537. void _AutoRunTaskMan(void)
  538. {
  539. HKEY hkeyWinLogon;
  540. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
  541. 0, KEY_READ, &hkeyWinLogon) == ERROR_SUCCESS)
  542. {
  543. TCHAR szBuffer[MAX_PATH];
  544. DWORD cbBuffer = sizeof(szBuffer);
  545. if (RegQueryValueEx(hkeyWinLogon, TEXT("Taskman"), 0, NULL, (LPBYTE)szBuffer, &cbBuffer) == ERROR_SUCCESS)
  546. {
  547. if (szBuffer[0])
  548. {
  549. PROCESS_INFORMATION pi;
  550. STARTUPINFO startup = {0};
  551. startup.cb = sizeof(startup);
  552. startup.wShowWindow = SW_SHOWNORMAL;
  553. if (CreateProcess(NULL, szBuffer, NULL, NULL, FALSE, 0,
  554. NULL, NULL, &startup, &pi))
  555. {
  556. CloseHandle(pi.hProcess);
  557. CloseHandle(pi.hThread);
  558. }
  559. }
  560. }
  561. RegCloseKey(hkeyWinLogon);
  562. }
  563. }
  564. // try to create this by sending a wm_command directly to
  565. // the desktop.
  566. BOOL MyCreateFromDesktop(HINSTANCE hInst, LPCTSTR pszCmdLine, int nCmdShow)
  567. {
  568. NEWFOLDERINFO fi = {0};
  569. BOOL bRet = FALSE;
  570. fi.nShow = nCmdShow;
  571. // since we have browseui fill out the fi,
  572. // SHExplorerParseCmdLine() does a GetCommandLine()
  573. if (SHExplorerParseCmdLine(&fi))
  574. bRet = SHCreateFromDesktop(&fi);
  575. // should we also have it cleanup after itself??
  576. // SHExplorerParseCmdLine() can allocate this buffer...
  577. if (fi.uFlags & COF_PARSEPATH)
  578. LocalFree(fi.pszPath);
  579. ILFree(fi.pidl);
  580. ILFree(fi.pidlRoot);
  581. return bRet;
  582. }
  583. BOOL g_fDragFullWindows=FALSE;
  584. int g_cxEdge=0;
  585. int g_cyEdge=0;
  586. int g_cySize=0;
  587. int g_cxTabSpace=0;
  588. int g_cyTabSpace=0;
  589. int g_cxBorder=0;
  590. int g_cyBorder=0;
  591. int g_cxPrimaryDisplay=0;
  592. int g_cyPrimaryDisplay=0;
  593. int g_cxDlgFrame=0;
  594. int g_cyDlgFrame=0;
  595. int g_cxFrame=0;
  596. int g_cyFrame=0;
  597. int g_cxMinimized=0;
  598. int g_fCleanBoot=0;
  599. int g_cxVScroll=0;
  600. int g_cyHScroll=0;
  601. UINT g_uDoubleClick=0;
  602. void Cabinet_InitGlobalMetrics(WPARAM wParam, LPTSTR lpszSection)
  603. {
  604. BOOL fForce = (!lpszSection || !*lpszSection);
  605. if (fForce || wParam == SPI_SETDRAGFULLWINDOWS)
  606. {
  607. SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &g_fDragFullWindows, 0);
  608. }
  609. if (fForce || !lstrcmpi(lpszSection, TEXT("WindowMetrics")) ||
  610. wParam == SPI_SETNONCLIENTMETRICS)
  611. {
  612. g_cxEdge = GetSystemMetrics(SM_CXEDGE);
  613. g_cyEdge = GetSystemMetrics(SM_CYEDGE);
  614. g_cxTabSpace = (g_cxEdge * 3) / 2;
  615. g_cyTabSpace = (g_cyEdge * 3) / 2; // cause the graphic designers really really want 3.
  616. g_cySize = GetSystemMetrics(SM_CYSIZE);
  617. g_cxBorder = GetSystemMetrics(SM_CXBORDER);
  618. g_cyBorder = GetSystemMetrics(SM_CYBORDER);
  619. g_cxVScroll = GetSystemMetrics(SM_CXVSCROLL);
  620. g_cyHScroll = GetSystemMetrics(SM_CYHSCROLL);
  621. g_cxDlgFrame = GetSystemMetrics(SM_CXDLGFRAME);
  622. g_cyDlgFrame = GetSystemMetrics(SM_CYDLGFRAME);
  623. g_cxFrame = GetSystemMetrics(SM_CXFRAME);
  624. g_cyFrame = GetSystemMetrics(SM_CYFRAME);
  625. g_cxMinimized = GetSystemMetrics(SM_CXMINIMIZED);
  626. g_cxPrimaryDisplay = GetSystemMetrics(SM_CXSCREEN);
  627. g_cyPrimaryDisplay = GetSystemMetrics(SM_CYSCREEN);
  628. }
  629. if (fForce || wParam == SPI_SETDOUBLECLICKTIME)
  630. {
  631. g_uDoubleClick = GetDoubleClickTime();
  632. }
  633. }
  634. //---------------------------------------------------------------------------
  635. void _CreateAppGlobals()
  636. {
  637. g_fCleanBoot = GetSystemMetrics(SM_CLEANBOOT); // also known as "Safe Mode"
  638. Cabinet_InitGlobalMetrics(0, NULL);
  639. //
  640. // Check if the mirroring APIs exist on the current
  641. // platform.
  642. //
  643. g_bMirroredOS = IS_MIRRORING_ENABLED();
  644. }
  645. //
  646. // This function checks if any of the shell windows is already created by
  647. // another instance of explorer and returns TRUE if so.
  648. //
  649. BOOL IsAnyShellWindowAlreadyPresent()
  650. {
  651. return GetShellWindow() || FindWindow(TEXT("Proxy Desktop"), NULL);
  652. }
  653. // See if the Shell= line indicates that we are the shell
  654. BOOL ExplorerIsShell()
  655. {
  656. TCHAR *pszPathName, szPath[MAX_PATH];
  657. TCHAR *pszModuleName, szModulePath[MAX_PATH];
  658. ASSERT(!IsAnyShellWindowAlreadyPresent());
  659. GetModuleFileName(NULL, szModulePath, ARRAYSIZE(szModulePath));
  660. pszModuleName = PathFindFileName(szModulePath);
  661. GetPrivateProfileString(TEXT("boot"), TEXT("shell"), pszModuleName, szPath, ARRAYSIZE(szPath), TEXT("system.ini"));
  662. PathRemoveArgs(szPath);
  663. PathRemoveBlanks(szPath);
  664. pszPathName = PathFindFileName(szPath);
  665. // NB Special case shell=install.exe - assume we are the shell.
  666. // Symantec un-installers temporarily set shell=installer.exe so
  667. // we think we're not the shell when we are. They fail to clean up
  668. // a bunch of links if we don't do this.
  669. return StrCmpNI(pszPathName, pszModuleName, lstrlen(pszModuleName)) == 0 ||
  670. lstrcmpi(pszPathName, TEXT("install.exe")) == 0;
  671. }
  672. // Returns TRUE of this is the first time the explorer is run
  673. BOOL ShouldStartDesktopAndTray()
  674. {
  675. // We need to be careful on which window we look for. If we look for
  676. // our desktop window class and Progman is running we will find the
  677. // progman window. So Instead we should ask user for the shell window.
  678. // We can not depend on any values being set here as this is the
  679. // start of a new process. This wont be called when we start new
  680. // threads.
  681. return !IsAnyShellWindowAlreadyPresent() && ExplorerIsShell();
  682. }
  683. void DisplayCleanBootMsg()
  684. {
  685. // On server sku's or anytime on ia64, just show a message with
  686. // an OK button for safe boot
  687. UINT uiMessageBoxFlags = MB_ICONEXCLAMATION | MB_SYSTEMMODAL | MB_OK;
  688. UINT uiMessage = IDS_CLEANBOOTMSG;
  689. #ifndef _WIN64
  690. if (!IsOS(OS_ANYSERVER))
  691. {
  692. // On x86 per and pro, also offer an option to start system restore
  693. uiMessageBoxFlags = MB_ICONEXCLAMATION | MB_SYSTEMMODAL | MB_YESNO;
  694. uiMessage = IDS_CLEANBOOTMSGRESTORE;
  695. }
  696. #endif // !_WIN64
  697. WCHAR szTitle[80];
  698. WCHAR szMessage[1024];
  699. LoadString(hinstCabinet, IDS_DESKTOP, szTitle, ARRAYSIZE(szTitle));
  700. LoadString(hinstCabinet, uiMessage, szMessage, ARRAYSIZE(szMessage));
  701. // on IA64 the msgbox will always return IDOK, so this "if" will always fail.
  702. if (IDNO == MessageBox(NULL, szMessage, szTitle, uiMessageBoxFlags))
  703. {
  704. TCHAR szPath[MAX_PATH];
  705. ExpandEnvironmentStrings(TEXT("%SystemRoot%\\system32\\restore\\rstrui.exe"), szPath, ARRAYSIZE(szPath));
  706. PROCESS_INFORMATION pi;
  707. STARTUPINFO si = {0};
  708. if (CreateProcess(szPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
  709. {
  710. CloseHandle(pi.hThread);
  711. CloseHandle(pi.hProcess);
  712. }
  713. }
  714. }
  715. BOOL IsExecCmd(LPCTSTR pszCmd)
  716. {
  717. return *pszCmd && !StrStrI(pszCmd, TEXT("-embedding"));
  718. }
  719. // run the cmd line passed up from win.com
  720. void _RunWinComCmdLine(LPCTSTR pszCmdLine, UINT nCmdShow)
  721. {
  722. if (IsExecCmd(pszCmdLine))
  723. {
  724. SHELLEXECUTEINFO ei = { sizeof(ei), 0, NULL, NULL, pszCmdLine, NULL, NULL, nCmdShow};
  725. ei.lpParameters = PathGetArgs(pszCmdLine);
  726. if (*ei.lpParameters)
  727. *((LPTSTR)ei.lpParameters - 1) = 0; // const -> non const
  728. ShellExecuteEx(&ei);
  729. }
  730. }
  731. // stolen from the CRT, used to shirink our code
  732. LPTSTR _SkipCmdLineCrap(LPTSTR pszCmdLine)
  733. {
  734. if (*pszCmdLine == TEXT('\"'))
  735. {
  736. //
  737. // Scan, and skip over, subsequent characters until
  738. // another double-quote or a null is encountered.
  739. //
  740. while (*++pszCmdLine && (*pszCmdLine != TEXT('\"')))
  741. ;
  742. //
  743. // If we stopped on a double-quote (usual case), skip
  744. // over it.
  745. //
  746. if (*pszCmdLine == TEXT('\"'))
  747. pszCmdLine++;
  748. }
  749. else
  750. {
  751. while (*pszCmdLine > TEXT(' '))
  752. pszCmdLine++;
  753. }
  754. //
  755. // Skip past any white space preceeding the second token.
  756. //
  757. while (*pszCmdLine && (*pszCmdLine <= TEXT(' ')))
  758. pszCmdLine++;
  759. return pszCmdLine;
  760. }
  761. STDAPI_(int) ModuleEntry()
  762. {
  763. PERFSETMARK("ExplorerStartup");
  764. DoInitialization();
  765. // We don't want the "No disk in drive X:" requesters, so we set
  766. // the critical error mask such that calls will just silently fail
  767. SetErrorMode(SEM_FAILCRITICALERRORS);
  768. LPTSTR pszCmdLine = GetCommandLine();
  769. pszCmdLine = _SkipCmdLineCrap(pszCmdLine);
  770. STARTUPINFO si = {0};
  771. si.cb = sizeof(si);
  772. GetStartupInfo(&si);
  773. int nCmdShow = si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT;
  774. int iRet = ExplorerWinMain(GetModuleHandle(NULL), NULL, pszCmdLine, nCmdShow);
  775. DoCleanup();
  776. // Since we now have a way for an extension to tell us when it is finished,
  777. // we will terminate all processes when the main thread goes away.
  778. if (g_fExitExplorer) // desktop told us not to exit
  779. ExitProcess(iRet);
  780. return iRet;
  781. }
  782. HANDLE CreateDesktopAndTray()
  783. {
  784. HANDLE hDesktop = NULL;
  785. if (g_dwProfileCAP & 0x00008000)
  786. StartCAPAll();
  787. if (v_hwndTray || c_tray.Init())
  788. {
  789. ASSERT(v_hwndTray);
  790. if (!v_hwndDesktop)
  791. {
  792. // cache the handle to the desktop...
  793. hDesktop = SHCreateDesktop(c_tray.GetDeskTray());
  794. }
  795. }
  796. if (g_dwProfileCAP & 0x80000000)
  797. StopCAPAll();
  798. return hDesktop;
  799. }
  800. // Removes the session key from the registry.
  801. void NukeSessionKey(void)
  802. {
  803. HKEY hkDummy;
  804. SHCreateSessionKey(0xFFFFFFFF, &hkDummy);
  805. }
  806. BOOL IsFirstInstanceAfterLogon()
  807. {
  808. BOOL fResult = FALSE;
  809. HKEY hkSession;
  810. HRESULT hr = SHCreateSessionKey(KEY_WRITE, &hkSession);
  811. if (SUCCEEDED(hr))
  812. {
  813. HKEY hkStartup;
  814. DWORD dwDisposition;
  815. LONG lRes;
  816. lRes = RegCreateKeyEx(hkSession, TEXT("StartupHasBeenRun"), 0,
  817. NULL,
  818. REG_OPTION_VOLATILE,
  819. KEY_WRITE,
  820. NULL,
  821. &hkStartup,
  822. &dwDisposition);
  823. if (lRes == ERROR_SUCCESS)
  824. {
  825. RegCloseKey(hkStartup);
  826. if (dwDisposition == REG_CREATED_NEW_KEY)
  827. fResult = TRUE;
  828. }
  829. RegCloseKey(hkSession);
  830. }
  831. return fResult;
  832. }
  833. DWORD ReadFaultCount()
  834. {
  835. DWORD dwValue = 0;
  836. DWORD dwSize = sizeof(dwValue);
  837. RegQueryValueEx(g_hkeyExplorer, TEXT("FaultCount"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
  838. return dwValue;
  839. }
  840. void WriteFaultCount(DWORD dwValue)
  841. {
  842. RegSetValueEx(g_hkeyExplorer, TEXT("FaultCount"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
  843. // If we are clearing the fault count or this is the first fault, clear or set the fault time.
  844. if (!dwValue || (dwValue == 1))
  845. {
  846. if (dwValue)
  847. dwValue = GetTickCount();
  848. RegSetValueEx(g_hkeyExplorer, TEXT("FaultTime"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
  849. }
  850. }
  851. // This function assumes it is only called when a fault has occured previously...
  852. BOOL ShouldDisplaySafeMode()
  853. {
  854. BOOL fRet = FALSE;
  855. SHELLSTATE ss;
  856. SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE);
  857. if (ss.fDesktopHTML)
  858. {
  859. if (ReadFaultCount() >= MAGIC_FAULT_LIMIT)
  860. {
  861. DWORD dwValue = 0;
  862. DWORD dwSize = sizeof(dwValue);
  863. RegQueryValueEx(g_hkeyExplorer, TEXT("FaultTime"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
  864. fRet = ((GetTickCount() - dwValue) < MAGIC_FAULT_TIME);
  865. // We had enough faults but they weren't over a sufficiently short period of time. Reset the fault
  866. // count to 1 so that we start counting from this fault now.
  867. if (!fRet)
  868. WriteFaultCount(1);
  869. }
  870. }
  871. else
  872. {
  873. // We don't care about faults that occured if AD is off.
  874. WriteFaultCount(0);
  875. }
  876. return fRet;
  877. }
  878. //
  879. // dwValue is FALSE if this is startup, TRUE if this is shutdown,
  880. //
  881. void WriteCleanShutdown(DWORD dwValue)
  882. {
  883. RegSetValueEx(g_hkeyExplorer, TEXT("CleanShutdown"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
  884. // If we are shutting down for real (i.e., not fake), then clean up the
  885. // session key so we don't leak a bazillion volatile keys into the
  886. // registry on a TS system when people log on and off and on and off...
  887. if (dwValue && !g_fFakeShutdown)
  888. {
  889. NukeSessionKey();
  890. }
  891. }
  892. BOOL ReadCleanShutdown()
  893. {
  894. DWORD dwValue = 1; // default: it was clean
  895. DWORD dwSize = sizeof(dwValue);
  896. RegQueryValueEx(g_hkeyExplorer, TEXT("CleanShutdown"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
  897. return (BOOL)dwValue;
  898. }
  899. //
  900. // Synopsis: Waits for the OLE SCM process to finish its initialization.
  901. // This is called before the first call to OleInitialize since
  902. // the SHELL runs early in the boot process.
  903. //
  904. // Arguments: None.
  905. //
  906. // Returns: S_OK - SCM is running. OK to call OleInitialize.
  907. // CO_E_INIT_SCM_EXEC_FAILURE - timed out waiting for SCM
  908. // other - create event failed
  909. //
  910. // History: 26-Oct-95 Rickhi Extracted from CheckAndStartSCM so
  911. // that only the SHELL need call it.
  912. //
  913. HRESULT WaitForSCMToInitialize()
  914. {
  915. static BOOL s_fScmStarted = FALSE;
  916. if (s_fScmStarted)
  917. return S_OK;
  918. SECURITY_ATTRIBUTES *psa = CreateAllAccessSecurityAttributes(NULL, NULL, NULL);
  919. // on NT5 we need a global event that is shared between TS sessions
  920. HANDLE hEvent = CreateEvent(psa, TRUE, FALSE, SZ_SCMCREATEDEVENT_NT5);
  921. if (!hEvent && GetLastError() == ERROR_ACCESS_DENIED)
  922. {
  923. //
  924. // Win2K OLE32 has tightened security such that if this object
  925. // already exists, we aren't allowed to open it with EVENT_ALL_ACCESS
  926. // (CreateEvent fails with ERROR_ACCESS_DENIED in this case).
  927. // Fall back by calling OpenEvent requesting SYNCHRONIZE access.
  928. //
  929. hEvent = OpenEvent(SYNCHRONIZE, FALSE, SZ_SCMCREATEDEVENT_NT5);
  930. }
  931. if (hEvent)
  932. {
  933. // wait for the SCM to signal the event, then close the handle
  934. // and return a code based on the WaitEvent result.
  935. int rc = WaitForSingleObject(hEvent, 60000);
  936. CloseHandle(hEvent);
  937. if (rc == WAIT_OBJECT_0)
  938. {
  939. s_fScmStarted = TRUE;
  940. return S_OK;
  941. }
  942. else if (rc == WAIT_TIMEOUT)
  943. {
  944. return CO_E_INIT_SCM_EXEC_FAILURE;
  945. }
  946. }
  947. return HRESULT_FROM_WIN32(GetLastError()); // event creation failed or WFSO failed.
  948. }
  949. STDAPI OleInitializeWaitForSCM()
  950. {
  951. HRESULT hr = WaitForSCMToInitialize();
  952. if (SUCCEEDED(hr))
  953. {
  954. hr = SHCoInitialize(); // make sure we get no OLE1 DDE crap
  955. OleInitialize(NULL);
  956. }
  957. return hr;
  958. }
  959. // we need to figure out the fFirstShellBoot on a per-user
  960. // basis rather than once per machine. We want the welcome
  961. // splash screen to come up for every new user.
  962. BOOL IsFirstShellBoot()
  963. {
  964. DWORD dwDisp;
  965. HKEY hkey;
  966. BOOL fFirstShellBoot = TRUE; // default value
  967. if (RegCreateKeyEx(HKEY_CURRENT_USER, REGTIPS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  968. NULL, &hkey, &dwDisp) == ERROR_SUCCESS)
  969. {
  970. DWORD dwSize = sizeof(fFirstShellBoot);
  971. RegQueryValueEx(hkey, TEXT("DisplayInitialTipWindow"), NULL, NULL, (LPBYTE)&fFirstShellBoot, &dwSize);
  972. if (fFirstShellBoot)
  973. {
  974. // Turn off the initial tip window for future shell starts.
  975. BOOL bTemp = FALSE;
  976. RegSetValueEx(hkey, TEXT("DisplayInitialTipWindow"), 0, REG_DWORD, (LPBYTE) &bTemp, sizeof(bTemp));
  977. }
  978. RegCloseKey(hkey);
  979. }
  980. return fFirstShellBoot;
  981. }
  982. // the following locale fixes (for NT5 378948) are dependent on desk.cpl changes
  983. // Since Millennium does not ship updated desk.cpl, we don't want to do this on Millennium
  984. //
  985. // Given the Locale ID, this returns the corresponding charset
  986. //
  987. UINT GetCharsetFromLCID(LCID lcid)
  988. {
  989. TCHAR szData[6+1]; // 6 chars are max allowed for this lctype
  990. UINT uiRet;
  991. if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, szData, ARRAYSIZE(szData)) > 0)
  992. {
  993. UINT uiCp = (UINT)StrToInt(szData);
  994. CHARSETINFO csinfo;
  995. TranslateCharsetInfo(IntToPtr_(DWORD *, uiCp), &csinfo, TCI_SRCCODEPAGE);
  996. uiRet = csinfo.ciCharset;
  997. }
  998. else
  999. {
  1000. // at worst non penalty for charset
  1001. uiRet = DEFAULT_CHARSET;
  1002. }
  1003. return uiRet;
  1004. }
  1005. // In case of system locale change, the only way to update UI fonts is opening
  1006. // Desktop->Properties->Appearance.
  1007. // If the end user never open it the UI fonts are never changed.
  1008. // So compare the charset from system locale with the UI fonts charset then
  1009. // call desk.cpl if those are different.
  1010. #define MAX_CHARSETS 4
  1011. typedef HRESULT (STDAPICALLTYPE *LPUPDATECHARSETCHANGES)();
  1012. void CheckDefaultUIFonts()
  1013. {
  1014. UINT uiCharsets[MAX_CHARSETS];
  1015. DWORD dwSize = sizeof(UINT) * MAX_CHARSETS;
  1016. DWORD dwError;
  1017. dwError = SHGetValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance"), TEXT("RecentFourCharsets"), NULL, (void *)uiCharsets, &dwSize);
  1018. if (dwError != ERROR_SUCCESS || uiCharsets[0] != GetCharsetFromLCID(GetSystemDefaultLCID()))
  1019. {
  1020. HINSTANCE hInst;
  1021. LPUPDATECHARSETCHANGES pfnUpdateCharsetChanges;
  1022. if (hInst = LoadLibrary(TEXT("desk.cpl")))
  1023. {
  1024. // Call desk.cpl to change the UI fonts in case of
  1025. // system locale change.
  1026. if (pfnUpdateCharsetChanges = (LPUPDATECHARSETCHANGES)(GetProcAddress(hInst, "UpdateCharsetChanges")))
  1027. {
  1028. (*pfnUpdateCharsetChanges)();
  1029. }
  1030. FreeLibrary(hInst);
  1031. }
  1032. }
  1033. }
  1034. //
  1035. // This function calls an desk.cpl function to update the UI fonts to use the new DPI value.
  1036. // UpdateUIfonts() in desk.cpl checks to see if the DPI value has changed. If not, it returns
  1037. // immediately; If the dpi value has changed, it changes the size of all the UI fonts to reflect
  1038. // the dpi change and then returns.
  1039. //
  1040. typedef HRESULT (WINAPI *LPUPDATEUIFONTS)(int, int);
  1041. void ChangeUIfontsToNewDPI()
  1042. {
  1043. int iNewDPI, iOldDPI;
  1044. //Get the current system DPI.
  1045. HDC hdc = GetDC(NULL);
  1046. iNewDPI = GetDeviceCaps(hdc, LOGPIXELSY);
  1047. ReleaseDC(NULL, hdc);
  1048. DWORD dwSize = sizeof(iOldDPI);
  1049. //Get the last saved DPI value for the current user.
  1050. if (SHGetValue(HKEY_CURRENT_USER, SZ_WINDOWMETRICS, SZ_APPLIEDDPI, NULL, (void *)&iOldDPI, &dwSize) != ERROR_SUCCESS)
  1051. {
  1052. //"AppliedDPI" for the current user is missing.
  1053. // Now, see if the "OriginalDPI" value exists under HKLM
  1054. dwSize = sizeof(iOldDPI);
  1055. if (SHGetValue(HKEY_LOCAL_MACHINE, SZ_CONTROLPANEL, SZ_ORIGINALDPI, NULL, (void *)&iOldDPI, &dwSize) != ERROR_SUCCESS)
  1056. {
  1057. //If "OriginalDPI" value is also missing, that means that nobody has changed DPI.
  1058. // Old and New are one and the same!!!
  1059. iOldDPI = iNewDPI;
  1060. }
  1061. }
  1062. if (iNewDPI != iOldDPI) //Has the dpi value changed?
  1063. {
  1064. HINSTANCE hInst = LoadLibrary(TEXT("desk.cpl"));
  1065. if (hInst)
  1066. {
  1067. LPUPDATEUIFONTS pfnUpdateUIfonts;
  1068. //Call desk.cpl to update the UI fonts to reflect the DPI change.
  1069. if (pfnUpdateUIfonts = (LPUPDATEUIFONTS)(GetProcAddress(hInst, "UpdateUIfontsDueToDPIchange")))
  1070. {
  1071. (*pfnUpdateUIfonts)(iOldDPI, iNewDPI);
  1072. }
  1073. FreeLibrary(hInst);
  1074. }
  1075. }
  1076. }
  1077. #define SZ_EXPLORERMUTEX TEXT("ExplorerIsShellMutex")
  1078. CComModule _Module;
  1079. BEGIN_OBJECT_MAP(ObjectMap)
  1080. // add your OBJECT_ENTRY's here
  1081. END_OBJECT_MAP()
  1082. typedef BOOL (*PFNICOMCTL32)(LPINITCOMMONCONTROLSEX);
  1083. void _InitComctl32()
  1084. {
  1085. HMODULE hmod = LoadLibrary(TEXT("comctl32.dll"));
  1086. if (hmod)
  1087. {
  1088. PFNICOMCTL32 pfn = (PFNICOMCTL32)GetProcAddress(hmod, "InitCommonControlsEx");
  1089. if (pfn)
  1090. {
  1091. INITCOMMONCONTROLSEX icce;
  1092. icce.dwICC = 0x00003FFF;
  1093. icce.dwSize = sizeof(icce);
  1094. pfn(&icce);
  1095. }
  1096. }
  1097. }
  1098. BOOL _ShouldFixResolution(void)
  1099. {
  1100. BOOL fRet = FALSE;
  1101. #ifndef _WIN64 // This feature is not supported on 64-bit machine
  1102. if (!IsOS(OS_ANYSERVER))
  1103. {
  1104. DISPLAY_DEVICE dd;
  1105. ZeroMemory(&dd, sizeof(DISPLAY_DEVICE));
  1106. dd.cb = sizeof(DISPLAY_DEVICE);
  1107. if (SHRegGetBoolUSValue(REGSTR_PATH_EXPLORER TEXT("\\DontShowMeThisDialogAgain"), TEXT("ScreenCheck"), FALSE, TRUE))
  1108. {
  1109. // Don't fix SafeMode or Terminal Clients
  1110. if ((GetSystemMetrics(SM_CLEANBOOT) == 0) && (GetSystemMetrics(SM_REMOTESESSION) == FALSE))
  1111. {
  1112. fRet = TRUE;
  1113. for (DWORD dwMon = 0; EnumDisplayDevices(NULL, dwMon, &dd, 0); dwMon++)
  1114. {
  1115. if (!(dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
  1116. {
  1117. DEVMODE dm = {0};
  1118. dm.dmSize = sizeof(DEVMODE);
  1119. if (EnumDisplaySettingsEx(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm, 0))
  1120. {
  1121. if ((dm.dmFields & DM_POSITION) &&
  1122. ((dm.dmPelsWidth >= 600) &&
  1123. (dm.dmPelsHeight >= 600) &&
  1124. (dm.dmBitsPerPel >= 15)))
  1125. {
  1126. fRet = FALSE;
  1127. }
  1128. }
  1129. }
  1130. }
  1131. }
  1132. }
  1133. }
  1134. #endif // _WIN64
  1135. return fRet;
  1136. }
  1137. BOOL _ShouldOfferTour(void)
  1138. {
  1139. BOOL fRet = FALSE;
  1140. #ifndef _WIN64 // This feature is not supported on 64-bit machine
  1141. // we don't allow guest to get offered tour b/c guest's registry is wiped every time she logs out,
  1142. // so she would get tour offered every single she logged in.
  1143. if (!IsOS(OS_ANYSERVER) && !IsOS(OS_EMBEDDED) && !(SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS)))
  1144. {
  1145. DWORD dwCount;
  1146. DWORD cbCount = sizeof(DWORD);
  1147. // we assume if we can't read the RunCount it's because it's not there (we haven't tried to offer the tour yet), so we default to 3.
  1148. if (ERROR_SUCCESS != SHRegGetUSValue(REGSTR_PATH_SETUP TEXT("\\Applets\\Tour"), TEXT("RunCount"), NULL, &dwCount, &cbCount, FALSE, NULL, 0))
  1149. {
  1150. dwCount = 3;
  1151. }
  1152. if (dwCount)
  1153. {
  1154. HUSKEY hkey1;
  1155. if (ERROR_SUCCESS == SHRegCreateUSKey(REGSTR_PATH_SETUP TEXT("\\Applets"), KEY_WRITE, NULL, &hkey1, SHREGSET_HKCU))
  1156. {
  1157. HUSKEY hkey2;
  1158. if (ERROR_SUCCESS == SHRegCreateUSKey(TEXT("Tour"), KEY_WRITE, hkey1, &hkey2, SHREGSET_HKCU))
  1159. {
  1160. if (ERROR_SUCCESS == SHRegWriteUSValue(hkey2, TEXT("RunCount"), REG_DWORD, &(--dwCount), cbCount, SHREGSET_FORCE_HKCU))
  1161. {
  1162. fRet = TRUE;
  1163. }
  1164. SHRegCloseUSKey(hkey2);
  1165. }
  1166. SHRegCloseUSKey(hkey1);
  1167. }
  1168. }
  1169. }
  1170. #endif // _WIN64
  1171. return fRet;
  1172. }
  1173. typedef BOOL (*CHECKFUNCTION)(void);
  1174. void _ConditionalBalloonLaunch(CHECKFUNCTION pCheckFct, SHELLREMINDER* psr)
  1175. {
  1176. if (pCheckFct())
  1177. {
  1178. IShellReminderManager* psrm;
  1179. HRESULT hr = CoCreateInstance(CLSID_PostBootReminder, NULL, CLSCTX_INPROC_SERVER,
  1180. IID_PPV_ARG(IShellReminderManager, &psrm));
  1181. if (SUCCEEDED(hr))
  1182. {
  1183. psrm->Add(psr);
  1184. psrm->Release();
  1185. }
  1186. }
  1187. }
  1188. void _CheckScreenResolution(void)
  1189. {
  1190. WCHAR szTitle[256];
  1191. WCHAR szText[512];
  1192. SHELLREMINDER sr = {0};
  1193. LoadString(hinstCabinet, IDS_FIXSCREENRES_TITLE, szTitle, ARRAYSIZE(szTitle));
  1194. LoadString(hinstCabinet, IDS_FIXSCREENRES_TEXT, szText, ARRAYSIZE(szText));
  1195. sr.cbSize = sizeof (sr);
  1196. sr.pszName = L"Microsoft.FixScreenResolution";
  1197. sr.pszTitle = szTitle;
  1198. sr.pszText = szText;
  1199. sr.pszIconResource = L"explorer.exe,9";
  1200. sr.dwTypeFlags = NIIF_INFO;
  1201. sr.pclsid = (GUID*)&CLSID_ScreenResFixer; // Try to run the Screen Resolution Fixing code over in ThemeUI
  1202. sr.pszShellExecute = L"desk.cpl"; // Open the Display Control Panel as a backup
  1203. _ConditionalBalloonLaunch(_ShouldFixResolution, &sr);
  1204. }
  1205. void _OfferTour(void)
  1206. {
  1207. WCHAR szTitle[256];
  1208. WCHAR szText[512];
  1209. SHELLREMINDER sr = {0};
  1210. LoadString(hinstCabinet, IDS_OFFERTOUR_TITLE, szTitle, ARRAYSIZE(szTitle));
  1211. LoadString(hinstCabinet, IDS_OFFERTOUR_TEXT, szText, ARRAYSIZE(szText));
  1212. sr.cbSize = sizeof (sr);
  1213. sr.pszName = L"Microsoft.OfferTour";
  1214. sr.pszTitle = szTitle;
  1215. sr.pszText = szText;
  1216. sr.pszIconResource = L"tourstart.exe,0";
  1217. sr.dwTypeFlags = NIIF_INFO;
  1218. sr.pszShellExecute = L"tourstart.exe";
  1219. sr.dwShowTime = 60000;
  1220. _ConditionalBalloonLaunch(_ShouldOfferTour, &sr);
  1221. }
  1222. void _FixWordMailRegKey(void)
  1223. {
  1224. // If we don't have permissions, fine this is just correction code
  1225. HKEY hkey;
  1226. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Applications", 0, KEY_ALL_ACCESS, &hkey))
  1227. {
  1228. HKEY hkeyTemp;
  1229. if (ERROR_SUCCESS != RegOpenKeyEx(hkey, L"WINWORD.EXE", 0, KEY_ALL_ACCESS, &hkeyTemp))
  1230. {
  1231. HKEY hkeyWinWord;
  1232. DWORD dwResult;
  1233. if (ERROR_SUCCESS == RegCreateKeyEx(hkey, L"WINWORD.EXE", 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyWinWord, &dwResult))
  1234. {
  1235. HKEY hkeyTBExcept;
  1236. if (ERROR_SUCCESS == RegCreateKeyEx(hkeyWinWord, L"TaskbarExceptionsIcons", 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyTBExcept, &dwResult))
  1237. {
  1238. HKEY hkeyIcon;
  1239. if (ERROR_SUCCESS == RegCreateKeyEx(hkeyTBExcept, L"explorer.exe,16", 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyIcon, &dwResult))
  1240. {
  1241. LPCTSTR szTemp = L"%ProgramFiles%\\Microsoft Office\\Office10\\OUTLOOK.EXE";
  1242. DWORD cb = sizeof(szTemp);
  1243. RegSetValue(hkeyIcon, NULL, REG_SZ, szTemp, cb);
  1244. RegCloseKey(hkeyIcon);
  1245. }
  1246. RegCloseKey(hkeyTBExcept);
  1247. }
  1248. RegCloseKey(hkeyWinWord);
  1249. }
  1250. }
  1251. else
  1252. {
  1253. RegCloseKey(hkeyTemp);
  1254. }
  1255. RegCloseKey(hkey);
  1256. }
  1257. }
  1258. void CheckUpdateShortcuts()
  1259. {
  1260. HKEY hKeyInitiallyClear;
  1261. // Check to see if during an update we made any notes of shortcuts of interest that were not present.
  1262. // If so, call shmgrate.exe to cleanup for us. This is to handle the case of a user deleting a shortcut
  1263. // to say Internet Explorer and then having our service pack process recreate them. Apparently this is
  1264. // frowned upon.
  1265. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
  1266. TEXT("Software\\Microsoft\\Active Setup\\Installed Components\\InitiallyClear"),
  1267. 0,
  1268. KEY_QUERY_VALUE,
  1269. &hKeyInitiallyClear))
  1270. {
  1271. RegCloseKey(hKeyInitiallyClear);
  1272. TCHAR szExe[MAX_PATH];
  1273. if (GetSystemDirectory(szExe, ARRAYSIZE(szExe)))
  1274. {
  1275. PathAppend(szExe, TEXT("shmgrate.exe"));
  1276. ShellExecute(NULL, NULL, szExe, TEXT("OCInstallCleanupInitiallyClear"), NULL, SW_SHOWNORMAL);
  1277. }
  1278. }
  1279. }
  1280. int ExplorerWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR pszCmdLine, int nCmdShow)
  1281. {
  1282. SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  1283. SHFusionInitializeFromModule(hInstance);
  1284. CcshellGetDebugFlags();
  1285. g_dwStopWatchMode = StopWatchMode();
  1286. if (g_dwProfileCAP & 0x00000001)
  1287. StartCAP();
  1288. hinstCabinet = hInstance;
  1289. if (SUCCEEDED(_Module.Init(ObjectMap, hInstance)))
  1290. {
  1291. _CreateAppGlobals();
  1292. // Run IEAK via Wininet initialization if the autoconfig url is present.
  1293. // No need to unload wininet in this case. Also only do this first time
  1294. // Explorer loads (GetShellWindow() returns NULL).
  1295. if (!GetShellWindow() && !g_fCleanBoot && SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\Internet Settings"),
  1296. TEXT("AutoConfigURL"),
  1297. NULL, NULL, NULL, FALSE, NULL, 0) == ERROR_SUCCESS)
  1298. {
  1299. LoadLibrary(TEXT("WININET.DLL"));
  1300. }
  1301. // Very Important: Make sure to init dde prior to any Get/Peek/Wait().
  1302. InitializeCriticalSection(&g_csDll);
  1303. // Need to initialize the version 5 common controls for AppCompat. Apps sucked into explorer's
  1304. // process expect the shell to register controls for them.
  1305. _InitComctl32();
  1306. #ifdef FULL_DEBUG
  1307. // Turn off GDI batching so that paints are performed immediately
  1308. GdiSetBatchLimit(1);
  1309. #endif
  1310. RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, &g_hkeyExplorer);
  1311. if (g_hkeyExplorer == NULL)
  1312. {
  1313. TraceMsg(TF_ERROR, "ExplorerWinMain: unable to create reg explorer key");
  1314. }
  1315. HANDLE hMutex = NULL;
  1316. BOOL fExplorerIsShell = ShouldStartDesktopAndTray();
  1317. if (fExplorerIsShell)
  1318. {
  1319. // Grab the mutex and do the check again. We do it this
  1320. // way so that we don't bother with the mutex for the common
  1321. // case of opening a browser window.
  1322. hMutex = CreateMutex(NULL, FALSE, SZ_EXPLORERMUTEX);
  1323. if (hMutex)
  1324. {
  1325. WaitForSingleObject(hMutex, INFINITE);
  1326. }
  1327. fExplorerIsShell = ShouldStartDesktopAndTray();
  1328. }
  1329. if (!fExplorerIsShell)
  1330. {
  1331. // We're not going to be the shell, relinquish the mutex
  1332. if (hMutex)
  1333. ReleaseMutex(hMutex);
  1334. // we purposely do NOT want to init OLE or COM in this case since we are delegating the creation work
  1335. // to an existing explorer and we want to keep from loading lots of extra dlls that would slow us down.
  1336. MyCreateFromDesktop(hInstance, pszCmdLine, nCmdShow);
  1337. }
  1338. else
  1339. {
  1340. MSG msg;
  1341. DWORD dwShellStartTime = GetTickCount(); // Compute shell startup time for perf automation
  1342. ShellDDEInit(TRUE); // use shdocvw shell DDE code.
  1343. // Specify the shutdown order of the shell process. 2 means
  1344. // the explorer should shutdown after everything but ntsd/windbg
  1345. // (level 0). (Taskman used to use 1, but is no more.)
  1346. SetProcessShutdownParameters(2, 0);
  1347. _AutoRunTaskMan();
  1348. // NB Make this the primary thread by calling peek message
  1349. // for a message we know we're not going to get.
  1350. // If we don't do it really soon, the notify thread can sometimes
  1351. // become the primary thread by accident. There's a bunch of
  1352. // special code in user to implement DDE hacks by assuming that
  1353. // the primary thread is handling DDE.
  1354. // Also, the PeekMsg() will cause us to set the WaitForInputIdle()
  1355. // event so we better be ready to do all dde.
  1356. PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE);
  1357. // We do this here, since FileIconInit will call SHCoInitialize anyway
  1358. HRESULT hrInit = OleInitializeWaitForSCM();
  1359. // Make sure we are the first one to call the FileIconInit...
  1360. FileIconInit(TRUE); // Tell the shell we want to play with a full deck
  1361. g_fLogonCycle = IsFirstInstanceAfterLogon();
  1362. g_fCleanShutdown = ReadCleanShutdown();
  1363. CheckDefaultUIFonts();
  1364. ChangeUIfontsToNewDPI(); //Check dpi values and update the fonts if needed.
  1365. if (g_fLogonCycle)
  1366. {
  1367. _ProcessRunOnceEx();
  1368. _ProcessRunOnce();
  1369. }
  1370. if (g_fCleanBoot)
  1371. {
  1372. // let users know we are in safe mode
  1373. DisplayCleanBootMsg();
  1374. }
  1375. // Create the other special folders.
  1376. CreateShellDirectories();
  1377. // Run install stubs for the current user, mostly to propagate
  1378. // shortcuts to apps installed by another user.
  1379. if (!g_fCleanBoot)
  1380. {
  1381. HANDLE hCanRegister = CreateEvent(NULL, TRUE, TRUE, TEXT("_fCanRegisterWithShellService"));
  1382. RunInstallUninstallStubs();
  1383. CheckUpdateShortcuts();
  1384. if (hCanRegister)
  1385. {
  1386. CloseHandle(hCanRegister);
  1387. }
  1388. }
  1389. if (!g_fCleanShutdown)
  1390. {
  1391. IActiveDesktopP *piadp;
  1392. DWORD dwFaultCount;
  1393. // Increment and store away fault count
  1394. dwFaultCount = ReadFaultCount();
  1395. WriteFaultCount(++dwFaultCount);
  1396. // Put the active desktop in safe mode if we faulted 3 times previously and this is a subsequent instance
  1397. if (ShouldDisplaySafeMode() && SUCCEEDED(CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC, IID_PPV_ARG(IActiveDesktopP, &piadp))))
  1398. {
  1399. piadp->SetSafeMode(SSM_SET | SSM_UPDATE);
  1400. piadp->Release();
  1401. }
  1402. }
  1403. WriteCleanShutdown(FALSE); // assume we will have a bad shutdown
  1404. WinList_Init();
  1405. // If any of the shellwindows are already present, then we want to bail out.
  1406. //
  1407. // NOTE: Compaq shell changes the "shell=" line during RunOnce time and
  1408. // that will make ShouldStartDesktopAndTray() return FALSE
  1409. HANDLE hDesktop = NULL;
  1410. if (!IsAnyShellWindowAlreadyPresent())
  1411. {
  1412. hDesktop = CreateDesktopAndTray();
  1413. }
  1414. // Now that we've had a chance to create the desktop, release the mutex
  1415. if (hMutex)
  1416. {
  1417. ReleaseMutex(hMutex);
  1418. }
  1419. if (hDesktop)
  1420. {
  1421. // Enable display of balloons in the tray...
  1422. PostMessage(v_hwndTray, TM_SHOWTRAYBALLOON, TRUE, 0);
  1423. _CheckScreenResolution();
  1424. _OfferTour();
  1425. _FixWordMailRegKey();
  1426. _RunWinComCmdLine(pszCmdLine, nCmdShow);
  1427. if (g_dwStopWatchMode)
  1428. {
  1429. // We used to save these off into global vars, and then write them at
  1430. // WM_ENDSESSION, but that seems too unreliable
  1431. DWORD dwShellStopTime = GetTickCount();
  1432. StopWatch_StartTimed(SWID_STARTUP, TEXT("Shell Startup: Start"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwShellStartTime);
  1433. StopWatch_StopTimed(SWID_STARTUP, TEXT("Shell Startup: Stop"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwShellStopTime);
  1434. }
  1435. #ifdef FEATURE_STARTPAGE
  1436. BOOL fIsStartPage = Tray_ShowStartPageEnabled();
  1437. if (fIsStartPage)
  1438. CreateStartPage(hInstance, hDesktop);
  1439. #endif
  1440. if (g_dwProfileCAP & 0x00010000)
  1441. StopCAP();
  1442. PERFSETMARK("ExplorerStartMsgLoop");
  1443. SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
  1444. // this must be whomever is the window on this thread
  1445. SHDesktopMessageLoop(hDesktop);
  1446. #ifdef FEATURE_STARTPAGE
  1447. if (fIsStartPage)
  1448. {
  1449. // DirectUI uninit thread
  1450. DirectUI::UnInitThread();
  1451. }
  1452. #endif
  1453. WriteCleanShutdown(TRUE); // we made it out ok, record that fact
  1454. WriteFaultCount(0); // clear our count of faults, we are exiting normally
  1455. }
  1456. WinList_Terminate(); // Turn off our window list processing
  1457. OleUninitialize();
  1458. SHCoUninitialize(hrInit);
  1459. ShellDDEInit(FALSE); // use shdocvw shell DDE code
  1460. }
  1461. _Module.Term();
  1462. }
  1463. SHFusionUninitialize();
  1464. DebugMsg(DM_TRACE, TEXT("c.App Exit."));
  1465. return TRUE;
  1466. }
  1467. #ifdef _WIN64
  1468. //
  1469. // The purpose of this function is to spawn rundll32.exe if we have 32-bit stuff in
  1470. // HKLM\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run that needs to be executed.
  1471. //
  1472. BOOL _ProcessRun6432()
  1473. {
  1474. BOOL bRet = FALSE;
  1475. if (!SHRestricted(REST_NOLOCALMACHINERUN))
  1476. {
  1477. if (SHKeyHasValues(HKEY_LOCAL_MACHINE, TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")))
  1478. {
  1479. TCHAR szWow64Path[MAX_PATH];
  1480. if (ExpandEnvironmentStrings(TEXT("%SystemRoot%\\SysWOW64"), szWow64Path, ARRAYSIZE(szWow64Path)))
  1481. {
  1482. TCHAR sz32BitRunOnce[MAX_PATH];
  1483. PROCESS_INFORMATION pi = {0};
  1484. wnsprintf(sz32BitRunOnce, ARRAYSIZE(sz32BitRunOnce), TEXT("%s\\runonce.exe"), szWow64Path);
  1485. if (CreateProcessWithArgs(sz32BitRunOnce, TEXT("/Run6432"), szWow64Path, &pi))
  1486. {
  1487. CloseHandle(pi.hProcess);
  1488. CloseHandle(pi.hThread);
  1489. bRet = TRUE;
  1490. }
  1491. }
  1492. }
  1493. }
  1494. return bRet;
  1495. }
  1496. #endif // _WIN64
  1497. STDAPI_(BOOL) Startup_ExecuteRegAppEnumProc(LPCTSTR szSubkey, LPCTSTR szCmdLine, RRA_FLAGS fFlags, LPARAM lParam)
  1498. {
  1499. BOOL bRet = ExecuteRegAppEnumProc(szSubkey, szCmdLine, fFlags, lParam);
  1500. if (!bRet && !(fFlags & RRA_DELETE))
  1501. {
  1502. c_tray.LogFailedStartupApp();
  1503. }
  1504. return bRet;
  1505. }
  1506. typedef struct
  1507. {
  1508. RESTRICTIONS rest;
  1509. HKEY hKey;
  1510. const TCHAR* psz;
  1511. DWORD dwRRAFlags;
  1512. }
  1513. STARTUPGROUP;
  1514. BOOL _RunStartupGroup(const STARTUPGROUP* pGroup, int cGroup)
  1515. {
  1516. BOOL bRet = FALSE;
  1517. // make sure SHRestricted is working ok
  1518. ASSERT(!SHRestricted(REST_NONE));
  1519. for (int i = 0; i < cGroup; i++)
  1520. {
  1521. if (!SHRestricted(pGroup[i].rest))
  1522. {
  1523. bRet = Cabinet_EnumRegApps(pGroup[i].hKey, pGroup[i].psz, pGroup[i].dwRRAFlags, Startup_ExecuteRegAppEnumProc, 0);
  1524. }
  1525. }
  1526. return bRet;
  1527. }
  1528. BOOL _ProcessRun()
  1529. {
  1530. static const STARTUPGROUP s_RunTasks [] =
  1531. {
  1532. { REST_NONE, HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN_POLICY, RRA_NOUI }, // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run
  1533. { REST_NOLOCALMACHINERUN, HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, RRA_NOUI }, // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
  1534. { REST_NONE, HKEY_CURRENT_USER, REGSTR_PATH_RUN_POLICY, RRA_NOUI }, // HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run
  1535. { REST_NOCURRENTUSERRUN, HKEY_CURRENT_USER, REGSTR_PATH_RUN, RRA_NOUI }, // HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
  1536. };
  1537. BOOL bRet = _RunStartupGroup(s_RunTasks, ARRAYSIZE(s_RunTasks));
  1538. #ifdef _WIN64
  1539. // see if we need to launch any 32-bit apps under wow64
  1540. _ProcessRun6432();
  1541. #endif
  1542. return bRet;
  1543. }
  1544. BOOL _ProcessPerUserRunOnce()
  1545. {
  1546. static const STARTUPGROUP s_PerUserRunOnceTasks [] =
  1547. {
  1548. { REST_NOCURRENTUSERRUNONCE, HKEY_CURRENT_USER, REGSTR_PATH_RUNONCE, RRA_DELETE | RRA_NOUI }, // HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce
  1549. };
  1550. return _RunStartupGroup(s_PerUserRunOnceTasks, ARRAYSIZE(s_PerUserRunOnceTasks));
  1551. }
  1552. DWORD WINAPI RunStartupAppsThread(void *pv)
  1553. {
  1554. // Some of the items we launch during startup assume that com is initialized. Make this
  1555. // assumption true.
  1556. HRESULT hrInit = SHCoInitialize();
  1557. // These global flags are set once long before our thread starts and are then only
  1558. // read so we don't need to worry about timing issues.
  1559. if (g_fLogonCycle && !g_fCleanBoot)
  1560. {
  1561. // We only run these startup items if g_fLogonCycle is TRUE. This prevents
  1562. // them from running again if the shell crashes and restarts.
  1563. _ProcessOldRunAndLoadEquals();
  1564. _ProcessRun();
  1565. _ExecuteStartupPrograms();
  1566. }
  1567. // As a best guess, the HKCU RunOnce key is executed regardless of the g_fLogonCycle
  1568. // becuase it was once hoped that we could install newer versions of IE without
  1569. // requiring a reboot. They would place something in the CU\RunOnce key and then
  1570. // shutdown and restart the shell to continue their setup process. I believe this
  1571. // idea was later abandoned but the code change is still here. Since that could
  1572. // some day be a useful feature I'm leaving it the same.
  1573. _ProcessPerUserRunOnce();
  1574. // we need to run all the non-blocking items first. Then we spend the rest of this threads life
  1575. // runing the synchronized objects one after another.
  1576. if (g_fLogonCycle && !g_fCleanBoot)
  1577. {
  1578. _RunWelcome();
  1579. }
  1580. PostMessage(v_hwndTray, TM_STARTUPAPPSLAUNCHED, 0, 0);
  1581. SHCoUninitialize(hrInit);
  1582. return TRUE;
  1583. }
  1584. void RunStartupApps()
  1585. {
  1586. DWORD dwThreadID;
  1587. HANDLE handle = CreateThread(NULL, 0, RunStartupAppsThread, 0, 0, &dwThreadID);
  1588. if (handle)
  1589. {
  1590. CloseHandle(handle);
  1591. }
  1592. }