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

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