initcab.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 69k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //--------------------------------------------------------------------------
  2. // Init the Cabinet (ie the top level browser).
  3. //---------------------------------------------------------------------------
  4. //---------------------------------------------------------------------------
  5. // Includes...
  6. #include "cabinet.h"
  7. #include "rcids.h"
  8. #include "cabwnd.h"
  9. #include <regstr.h>
  10. #include "startmnu.h"
  11. #include <shdguid.h>    // for IID_IShellService
  12. #include <shlguid.h>
  13. #include "..shdocvwwinlist.h"     // BUGBUG: get rid of this
  14. #include <desktray.h>
  15. #include <wininet.h>
  16. #include <dbgmem.h>
  17. #define DM_SHUTDOWN     DM_TRACE    // shutdown
  18. // copied from desktop.cpp
  19. #define PEEK_NORMAL     0
  20. #define PEEK_QUIT       1
  21. // on NT5 we need this event to be global so that it is shared between hydra
  22. // sessions
  23. #define SZ_SCMCREATEDEVENT_NT5      TEXT("Global\ScmCreatedEvent")
  24. #define SZ_SCMCREATEDEVENT          TEXT("ScmCreatedEvent")
  25. // exports from shdocvw.dll
  26. STDAPI_(void) RunInstallUninstallStubs(void);
  27. // from win32kernelutctime.c (private)
  28. DWORD APIENTRY RefreshDaylightInformation(BOOL fChangeTime);
  29. // shell32.dll exports, shelldllbinder.c
  30. STDAPI_(void) SHFreeUnusedLibraries();
  31. int ExplorerWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR pszCmdLine, int nCmdShow);
  32. //Do not change this stock5.lib use this as a BOOL not a bit.
  33. BOOL g_bMirroredOS = FALSE;
  34. HINSTANCE hinstCabinet = 0;
  35. CRITICAL_SECTION g_csDll = { 0 };
  36. HKEY g_hkeyExplorer = NULL;
  37. BOOL g_fLogonCycle = FALSE;
  38. BOOL g_fCleanShutdown = TRUE;
  39. BOOL g_fExitExplorer = TRUE;            // set to FALSE on WM_ENDSESSION shutdown case
  40. BOOL g_fEndSession = FALSE;             // set to TRUE if we rx a WM_ENDSESSION during RunOnce etc
  41. BOOL g_fFakeShutdown = FALSE;           // set to TRUE if we do Ctrl+Alt+Shift+Cancel shutdown
  42. BOOL g_fRuningOnTerminalServer = FALSE; // Assume we are not running on Hydra 
  43. BOOL Cabinet_IsExplorerWindow(HWND hwnd)
  44. {
  45.     TCHAR szClass[32];
  46.     GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  47.     return lstrcmpi(szClass, TEXT("ExploreWClass")) == 0;
  48. }
  49. // BUGBUG: does not account for "Browser" windows
  50. BOOL Cabinet_IsFolderWindow(HWND hwnd)
  51. {
  52.     TCHAR szClass[32];
  53.     GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  54.     return lstrcmpi(szClass, TEXT("CabinetWClass")) == 0;
  55. }
  56. //---------------------------------------------------------------------------
  57. typedef enum {
  58.     RRA_DEFAULT             = 0x0000,
  59.     RRA_DELETE              = 0x0001,       // delete each reg value when we're done with it
  60.     RRA_WAIT                = 0x0002,       // Wait for current item to finish before launching next item
  61.     RRA_SHELLSERVICEOBJECTS = 0x0004,       // treat as a shell service object instead of a command sting
  62.     RRA_NOUI                = 0x0008,       // prevents ShellExecuteEx from displaying error dialogs
  63.     RRA_RUNSUBKEYS          = 0x0010,       // Run items in sub keys in alphabetical order
  64. } RRA_FLAGS;
  65. // The following handles running an application and optionally waiting for it
  66. // to terminate.
  67. void ShellExecuteRegApp(LPTSTR szCmdLine, RRA_FLAGS fFlags)
  68. {
  69.     TCHAR szQuotedCmdLine[MAX_PATH+2];
  70.     SHELLEXECUTEINFO ei;
  71.     LPTSTR pszArgs;
  72.     //
  73.     // We used to call CreateProcess( NULL, szCmdLine, ...) here,
  74.     // but thats not useful for people with apppaths stuff.
  75.     //
  76.     // Don't let empty strings through, they will endup doing something dumb
  77.     // like opening a command prompt or the like
  78.     if (!szCmdLine || !*szCmdLine)
  79.         return;
  80.     // Gross, but if the process command fails, copy the command line to let
  81.     // shell execute report the errors
  82.     if (PathProcessCommand(szCmdLine, szQuotedCmdLine, ARRAYSIZE(szQuotedCmdLine),
  83.                            PPCF_ADDARGUMENTS|PPCF_FORCEQUALIFY) == -1)
  84.         lstrcpy(szQuotedCmdLine, szCmdLine);
  85.     pszArgs = PathGetArgs(szQuotedCmdLine);
  86.     if (*pszArgs)
  87.         *(pszArgs - 1) = 0; // Strip args
  88.     PathUnquoteSpaces(szQuotedCmdLine);
  89.     ei.cbSize          = sizeof(SHELLEXECUTEINFO);
  90.     ei.hwnd            = NULL;
  91.     ei.lpVerb          = NULL;
  92.     ei.lpFile          = szQuotedCmdLine;
  93.     ei.lpParameters    = pszArgs;
  94.     ei.lpDirectory     = NULL;
  95.     ei.nShow           = SW_SHOWNORMAL;
  96.     ei.fMask           = (fFlags & RRA_NOUI)?
  97.             (SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI) :  SEE_MASK_NOCLOSEPROCESS;
  98.     // Hydra comment:
  99.     //      If we were sure that this func would not be called for anything but handling entries in RunOnce, then
  100.     //      I would have out the system in in stall-mode and back to execute-mode right here.
  101.     //      However, although I do not see this func being used by anything other than entries in RunOnce, 
  102.     //      there is no guaranty.
  103.     if (ShellExecuteEx(&ei))
  104.     {
  105.         if ( NULL != ei.hProcess)
  106.         {
  107.             if (fFlags & RRA_WAIT)
  108.             {
  109.                 MsgWaitForMultipleObjectsLoop(ei.hProcess, INFINITE);
  110.             }
  111.             CloseHandle(ei.hProcess);
  112.         }
  113.     }
  114. }
  115. // The following code manages shell service objects.  We load inproc dlls
  116. // from the registry key and QI them for IOleCommandTarget. Note that all
  117. // Shell Service Objects are loaded on the desktop thread.
  118. // CGID_ShellServiceObject notifications are sent to these objects letting
  119. // them know about shell status.
  120. HDSA g_hdsaShellServiceObjects=NULL;
  121. void LoadShellServiceObject(LPCTSTR szValueName, LPCTSTR szCmdLine, RRA_FLAGS fFlags)
  122. {
  123.     SHELLSERVICEOBJECT sso = {0};
  124.     DebugMsg(DM_TRACE, TEXT("%s %s"), szValueName, szCmdLine);
  125.     if (!g_hdsaShellServiceObjects &&
  126.         !(g_hdsaShellServiceObjects = DSA_Create(sizeof(SHELLSERVICEOBJECT), 2)))
  127.     {
  128.         // Fail
  129.         return;
  130.     }
  131.     if (SHCLSIDFromString(szCmdLine, &sso.clsid) == S_OK)
  132.     {
  133.         if (SUCCEEDED(CoCreateInstance(&sso.clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
  134.                                 &IID_IOleCommandTarget, (void **)&sso.pct)))
  135.         {
  136.             lstrcpyn(sso.szName, szValueName, ARRAYSIZE(sso.szName));
  137.             if (DSA_AppendItem(g_hdsaShellServiceObjects, &sso) == -1)
  138.             {
  139.                 DebugMsg( DM_ERROR, TEXT("Cannot add to dsa <%s>"), szValueName );
  140.                 sso.pct->lpVtbl->Release(sso.pct);
  141.             }
  142.         }
  143.     }
  144. }
  145. //
  146. //  While we are doing our RunOnce processing, we want to change the
  147. //  system cursor.  We can't use SetClassLong on the desktop class
  148. //  because that's illegal under Win32.  And we can't use SetSystemCursor
  149. //  because on NT, CopyImage on a system cursor doesn't actually copy
  150. //  the cursor.  It merely returns the original cursor handle back,
  151. //  which leaves us in a bit of a fix because the original cursor is
  152. //  about to be obliterated by SetSystemCursor!
  153. //
  154. //  So instead, we create a full-virtual-screen window that always ducks
  155. //  to the bottom of the Z-order.  We can't use SetShellWindow and make
  156. //  USER do the ducking for us, because RunOnce applets might get confused
  157. //  if they see a GetShellWindow before the shell is initialized.
  158. //
  159. //  We can't use a shlwapi worker window because we need to make the
  160. //  class background brush COLOR_DESKTOP to ensure that we don't get
  161. //  ugly white flashes if the user quickly drags a window around our
  162. //  fake desktop.
  163. //
  164. //  You'd think this would be easy, but there are a lot of subtleties.
  165. //
  166. LRESULT RegAppsWaitWndProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
  167. {
  168.     switch (wm) {
  169.     case WM_WINDOWPOSCHANGING:
  170.         ((LPWINDOWPOS)lp)->hwndInsertAfter = HWND_BOTTOM; // force to bottom
  171.         break;          // proceed with default action
  172.     // Subtlety: Must erase background with PaintDesktop so user's
  173.     // wallpaper shows through.
  174.     //
  175.     // Double subtlety: Don't paint directly through the HDC that comes
  176.     // in via the wParam, because that HDC is >not clipped<.  Consequently,
  177.     // you get horrible flickering since we repaint the entire desktop.
  178.     //
  179.     // Triple subtlety: You have to do this in the WM_ERASEBKGND handler,
  180.     // not the WM_PAINT handler, because MsgWaitForMultipleObjectsLoop
  181.     // waits only for QS_SENDMESSAGE and not QS_PAINT.
  182.     case WM_ERASEBKGND:
  183.         {
  184.             PAINTSTRUCT ps;
  185.             BeginPaint(hwnd, &ps);
  186.             PaintDesktop(ps.hdc);
  187.             EndPaint(hwnd, &ps);
  188.         }
  189.         return 0;
  190.     case WM_ENDSESSION:
  191.         g_fEndSession = (BOOL)wp;
  192.         break;
  193.     }
  194.     return DefWindowProc(hwnd, wm, wp, lp);
  195. }
  196. #define REGAPPSWAIT_CLASS   TEXT("RegAppsWait")
  197. HWND CreateRegAppsWaitWindow(void)
  198. {
  199.     WNDCLASS wc;
  200.     // Should never try to do this if the main desktop is already up
  201.     ASSERT(!GetShellWindow());
  202.     wc.style            = 0;
  203.     wc.lpfnWndProc      = RegAppsWaitWndProc;
  204.     wc.cbClsExtra       = 0;
  205.     wc.cbWndExtra       = 0;
  206.     wc.hInstance        = hinstCabinet;
  207.     wc.hIcon            = 0;
  208.     //
  209.     //  Doesn't matter what cursor we choose because NT USER will say
  210.     //  "Stupid app not waiting for QS_POSTMESSAGE so I will always display
  211.     //  an hourglass cursor."  But do the right thing in case NT USER
  212.     //  decides actually to pay attention to our class cursor.
  213.     //
  214.     wc.hCursor          = LoadCursor(NULL, IDC_APPSTARTING);
  215.     // Subtlety: Don't use (HBRUSH)(COLOR_DESKTOP+1).  If the user
  216.     // drags a window across the desktop, USER will first paint the
  217.     // window with the class background brush and only later will
  218.     // actually send the WM_ERASEBKGND message.  If the user's
  219.     // desktop wallpaper has a different color from COLOR_DESKTOP,
  220.     // you will see ugly flashing as the exposed bits are first
  221.     // painted in COLOR_DESKTOP, and then later with the wallpaper.
  222.     wc.hbrBackground    = (HBRUSH)GetStockObject(NULL_BRUSH);
  223.     wc.lpszMenuName     = 0;
  224.     wc.lpszClassName    = REGAPPSWAIT_CLASS;
  225.     RegisterClass(&wc);
  226.     return CreateWindow(
  227.         REGAPPSWAIT_CLASS,                      /* Class Name */
  228.         NULL,                                   /* Title */
  229.         WS_POPUP | WS_CLIPCHILDREN | WS_VISIBLE,/* Style */
  230.         GetSystemMetrics(SM_XVIRTUALSCREEN),    /* Position */
  231.         GetSystemMetrics(SM_YVIRTUALSCREEN),    /* Position */
  232.         GetSystemMetrics(SM_CXVIRTUALSCREEN),   /* Size */
  233.         GetSystemMetrics(SM_CYVIRTUALSCREEN),   /* Size */
  234.         NULL,                                   /* Parent */
  235.         NULL,                                   /* Menu */
  236.         hinstCabinet,                           /* Instance */
  237.         0);                                     /* Special parameters */
  238. }
  239. void DestroyRegAppsWaitWindow(HWND hwnd)
  240. {
  241.     DestroyWindow(hwnd);
  242.     UnregisterClass(REGAPPSWAIT_CLASS, hinstCabinet);
  243. }
  244. BOOL RunRegAppsAndObjects(HKEY hkeyParent, LPCTSTR szSubkey, RRA_FLAGS fFlags)
  245. {
  246.     HKEY hkey;
  247.     BOOL fShellInit = FALSE;
  248.     if (RegOpenKey(hkeyParent, szSubkey, &hkey) == ERROR_SUCCESS)
  249.     {
  250.         DWORD cbData, cbValue, dwType, i;
  251.         TCHAR szValueName[80], szCmdLine[MAX_PATH];
  252.         // BUGBUG: should we do this in retail too or is it too scary :)
  253. #ifdef DEBUG
  254.         //
  255.         // we only support named values so explicitly purge default values
  256.         //
  257.         cbData = SIZEOF(szCmdLine);
  258.         if (RegQueryValue(hkey, NULL, szCmdLine, &cbData) == ERROR_SUCCESS)
  259.         {
  260.             AssertMsg((cbData <= 2), TEXT("BOGUS default entry in <%s> '%s'"), szSubkey, szCmdLine);
  261.             RegDeleteValue(hkey, NULL);
  262.         }
  263. #endif
  264.         if ( fFlags & RRA_RUNSUBKEYS )
  265.         {
  266.             // run the contents of each sub key.  Keys must be run in alphabetical order.  The sub keys
  267.             // are used to prioritize the launching of items.  We only run one level of subkey so we need
  268.             // to turn off the subkey flag.
  269.             RRA_FLAGS fSubkeyFlags = fFlags & ~(RRA_RUNSUBKEYS);
  270.             for (i=0; !g_fEndSession ; i++)
  271.             {
  272.                 LONG lEnum;
  273.                 cbValue = ARRAYSIZE(szValueName);
  274.                 
  275.                 // BUGBUG: This is a dirty assumption.  This assumes several unsafe things:
  276.                 // 1.) none of the programs being run will changes this section of the registry.
  277.                 // 2.) NT enumerates registry items in alphabetic order.
  278.                 // Since it isn't safe to make assumption #1 this needs to be made more robust.
  279.                 lEnum = RegEnumKey( hkey, i, szValueName, cbValue );
  280.                 if( ERROR_MORE_DATA == lEnum )
  281.                 {
  282.                     // ERROR_MORE_DATA means the value name or data was too large.
  283.                     // Skip to the next item.
  284.                     DebugMsg( DM_ERROR, TEXT("Explorer: RunRegAppsAndObjects cannot run oversize entry in <%s> '%s'"), szSubkey, szValueName );
  285.                     continue;
  286.                 }
  287.                 else if ( ERROR_SUCCESS != lEnum )
  288.                 {
  289.                     // could be ERROR_NO_MORE_ENTRIES, or some kind of failure
  290.                     // we can't recover from any other registry problem, anyway
  291.                     break;
  292.                 }
  293.                 RunRegAppsAndObjects( hkey, szValueName, fSubkeyFlags );
  294.                 if ( fFlags & RRA_DELETE )
  295.                 {
  296.                     // delete the sub key.
  297.                     SHDeleteKey( hkey, szValueName );
  298.                     i--;
  299.                 }
  300.             }
  301.         }
  302.         else
  303.         {
  304.             //
  305.             // now enumerate all of the values.
  306.             //
  307.             for (i = 0; !g_fEndSession ; i++)
  308.             {
  309.                 LONG lEnum;
  310.                 cbValue = ARRAYSIZE(szValueName);
  311.                 cbData = SIZEOF(szCmdLine);
  312.                 lEnum = RegEnumValue( hkey, i, szValueName, &cbValue, NULL, &dwType, (LPBYTE)szCmdLine, &cbData );
  313.                 if( ERROR_MORE_DATA == lEnum )
  314.                 {
  315.                     // ERROR_MORE_DATA means the value name or data was too large
  316.                     // skip to the next item
  317.                     DebugMsg( DM_ERROR, TEXT("Cannot run oversize entry in <%s>"), szSubkey );
  318.                     continue;
  319.                 }
  320.                 else if ( lEnum != ERROR_SUCCESS )
  321.                 {
  322.                     // could be ERROR_NO_MORE_ENTRIES, or some kind of failure
  323.                     // we can't recover from any other registry problem, anyway
  324.                     break;
  325.                 }
  326.                 if (dwType == REG_SZ)
  327.                 {
  328.                     DebugMsg(DM_TRACE, TEXT("%s %s"), szSubkey, szCmdLine);
  329.                     // only run things marked with a "*" in clean boot
  330.                     if (g_fCleanBoot && (szValueName[0] != TEXT('*')))
  331.                         continue;
  332.                     // NB Things marked with a '!' mean delete after
  333.                     // the CreateProcess not before. This is to allow
  334.                     // certain apps (runonce.exe) to be allowed to rerun
  335.                     // to if the machine goes down in the middle of execing
  336.                     // them. Be very afraid of this switch.
  337.                     if ((fFlags & RRA_DELETE) && (szValueName[0] != TEXT('!')))
  338.                     {
  339.                         // This delete can fail if the user doesn't have the privilege
  340.                         if (RegDeleteValue(hkey, szValueName) == ERROR_SUCCESS)
  341.                         {
  342.                             // adjust for shift in value index only if delete succeeded
  343.                             i--;
  344.                         }
  345.                     }
  346.                     if (fFlags & RRA_SHELLSERVICEOBJECTS)
  347.                         LoadShellServiceObject(szValueName, szCmdLine, fFlags);
  348.                     else
  349.                     {
  350. #ifdef WINNT
  351.                         // Hydra-Specific stuff
  352.                         BOOL hydraInAppInstallMode = FALSE;
  353.                 
  354.                         // In here, We only put the Hydra server in app-install-mode if RunOnce entries are 
  355.                         // being processed 
  356.                         if (!lstrcmpi(szSubkey, REGSTR_PATH_RUNONCE)) 
  357.                         {
  358.                             // See if we are on NT5, and if the terminal-services is enabled
  359.                             if (g_fRuningOnTerminalServer) 
  360.                             {
  361.                                 if (hydraInAppInstallMode = SetTermsrvAppInstallMode(TRUE)) 
  362.                                 {
  363.                                     fFlags |= RRA_WAIT;  // Changing timing blows up IE 4.0, but IE5 is ok!
  364.                                 } 
  365.                             }
  366.                         }
  367. #endif // WINNT
  368.                     
  369.                         ShellExecuteRegApp(szCmdLine, fFlags);
  370.     
  371. #ifdef WINNT    
  372.                         // Hydra-Specific stuff
  373.                         if (hydraInAppInstallMode)
  374.                         {
  375.                             SetTermsrvAppInstallMode(FALSE);
  376.                         }
  377. #endif // WINNT
  378.                     }
  379.                     // Post delete '!' things.
  380.                     if ((fFlags & RRA_DELETE) && (szValueName[0] == TEXT('!'))) {
  381.                         // This delete can fail if the user doesn't have the privilege
  382.                         if (RegDeleteValue(hkey, szValueName) == ERROR_SUCCESS)
  383.                         {
  384.                             // adjust for shift in value index only if delete succeeded
  385.                             i--;    // adjust for shift in value index
  386.                         }
  387.                     }
  388.                 }
  389.             }
  390.         }
  391.         RegCloseKey(hkey);
  392.     }
  393.     // if we rx'd a WM_ENDSESSION whilst running any of these keys we must exit the 
  394.     // process.
  395.     if ( g_fEndSession )
  396.         ExitProcess(0);
  397.     return fShellInit;
  398. }
  399. BOOL LoadShellServiceObjects(HKEY hkeyParent, LPCTSTR szSubkey)
  400. {
  401.     return RunRegAppsAndObjects(hkeyParent, szSubkey, RRA_SHELLSERVICEOBJECTS);
  402. }
  403. // clsid - NULL, send to everyone
  404. //         else restrict cmd to given class.
  405. void CTExecShellServiceObjects(const CLSID *pclsid, DWORD nCmdID, DWORD nCmdexecopt, DWORD flags)
  406. {
  407.     int iCount;
  408.     int i;
  409.     int iEnd, iStart;
  410.     HDSA hdsaShellServiceObjects;
  411.     if (!g_hdsaShellServiceObjects)
  412.     {
  413.         return;
  414.     }
  415.     // We use a temp variable to protect agains re-entancy (eg netshell calls PeekMessage during the 
  416.     // Exec callback). Basically this is single threaded since the tray thread is the only guy who ever
  417.     // calls this function, but we have to be careful that we dont re-enter ourself.
  418.     hdsaShellServiceObjects = g_hdsaShellServiceObjects;
  419.     g_hdsaShellServiceObjects = NULL;
  420.     iCount = DSA_GetItemCount(hdsaShellServiceObjects);
  421.     if (iCount)
  422.     {
  423.         // Loop through all shell service objects and send the command target the
  424.         // command id.
  425.         if (flags & CTEXECSSOF_REVERSE)
  426.         {
  427.             iStart = iCount-1;
  428.             iEnd = 0-1;
  429.         }
  430.         else
  431.         {
  432.             iStart = 0;
  433.             iEnd = iCount;
  434.         }
  435.         for (i=iStart; i != iEnd; (flags & CTEXECSSOF_REVERSE ? i-- : i++))
  436.         {
  437.             PSHELLSERVICEOBJECT psso = (PSHELLSERVICEOBJECT)DSA_GetItemPtr(hdsaShellServiceObjects, i);
  438.             if (!pclsid || IsEqualGUID(&psso->clsid, pclsid))
  439.             {
  440.                 psso->pct->lpVtbl->Exec(psso->pct,
  441.                                         &CGID_ShellServiceObject,
  442.                                         nCmdID, nCmdexecopt,
  443.                                         NULL, NULL);
  444.                 if (nCmdID==SSOCMDID_CLOSE)
  445.                     psso->pct->lpVtbl->Release(psso->pct);
  446.             }
  447.         }
  448.     }
  449.     g_hdsaShellServiceObjects = hdsaShellServiceObjects;
  450. }
  451. //---------------------------------------------------------------------------
  452. void CreateShellDirectories()
  453. {
  454.     TCHAR szPath[MAX_PATH];
  455.     //  Create the shell directories if they don't exist
  456.     SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, TRUE);
  457.     SHGetSpecialFolderPath(NULL, szPath, CSIDL_PROGRAMS, TRUE);
  458.     SHGetSpecialFolderPath(NULL, szPath, CSIDL_STARTMENU, TRUE);
  459.     SHGetSpecialFolderPath(NULL, szPath, CSIDL_STARTUP, TRUE);
  460.     SHGetSpecialFolderPath(NULL, szPath, CSIDL_RECENT, TRUE);
  461.     SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE);
  462. }
  463. //----------------------------------------------------------------------------
  464. // returns:
  465. //      TRUE if the user wants to abort the startup sequence
  466. //      FALSE keep going
  467. //
  468. // note: this is a switch, once on it will return TRUE to all
  469. // calls so these keys don't need to be pressed the whole time
  470. BOOL AbortStartup()
  471. {
  472.     static BOOL bAborted = FALSE;       // static so it sticks!
  473.     // DebugMsg(DM_TRACE, "Abort Startup?");
  474.     if (bAborted)
  475.         return TRUE;    // don't do funky startup stuff
  476.     else {
  477.         bAborted = (g_fCleanBoot || ((GetAsyncKeyState(VK_CONTROL) < 0) || (GetAsyncKeyState(VK_SHIFT) < 0)));
  478.         return bAborted;
  479.     }
  480. }
  481. // BUGBUG: hwndOwner is no longer used (NULL is passed in) remove the hwndOwner code
  482. // once we are certain that this isn't needed
  483. BOOL EnumFolder_Startup(IShellFolder * psf, HWND hwndOwner, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
  484. {
  485.     LPCONTEXTMENU pcm;
  486.     HRESULT hres;
  487. //    MSG msg;
  488.     hres = psf->lpVtbl->GetUIObjectOf(psf, hwndOwner, 1, &pidlItem, & IID_IContextMenu, NULL, &pcm);
  489.     if (SUCCEEDED(hres))
  490.     {
  491.         HMENU hmenu = CreatePopupMenu();
  492.         if (hmenu)
  493.         {
  494. #define CMD_ID_FIRST    1
  495. #define CMD_ID_LAST     0x7fff
  496.             INT idCmd;
  497.             pcm->lpVtbl->QueryContextMenu(pcm, hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST, CMF_DEFAULTONLY);
  498.             idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  499.             if (idCmd)
  500.             {
  501.                 CMINVOKECOMMANDINFOEX ici;
  502.                 ZeroMemory(&ici, SIZEOF(ici));
  503.                 ici.cbSize = SIZEOF(ici);
  504.                 ici.hwnd = hwndOwner;
  505.                 ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - 1);
  506.                 ici.nShow = SW_NORMAL;
  507.                 pcm->lpVtbl->InvokeCommand(pcm, (LPCMINVOKECOMMANDINFO)&ici);
  508.             }
  509.             DestroyMenu(hmenu);
  510.         }
  511.         pcm->lpVtbl->Release(pcm);
  512.     }
  513.     if (AbortStartup())
  514.         return FALSE;
  515.     return TRUE;
  516. }
  517. //----------------------------------------------------------------------------
  518. // BUGBUG: hwndOwner is no longer used (NULL is passed in) remove the hwndOwner code
  519. // once we are certain that this isn't needed
  520. void EnumFolder(HWND hwndOwner, LPITEMIDLIST pidlFolder, DWORD grfFlags, PFNENUMFOLDERCALLBACK pfn)
  521. {
  522.     IShellFolder *psf = BindToFolder(pidlFolder);
  523.     if (psf)
  524.     {
  525.         LPENUMIDLIST penum;
  526.         HRESULT hres = psf->lpVtbl->EnumObjects(psf, hwndOwner, grfFlags, &penum);
  527.         if (SUCCEEDED(hres))
  528.         {
  529.             LPITEMIDLIST pidl;
  530.             UINT celt;
  531.             while (penum->lpVtbl->Next(penum, 1, &pidl, &celt)==NOERROR && celt==1)
  532.             {
  533.                 if (!pfn(psf, hwndOwner, pidlFolder, pidl))
  534.                 {
  535.                     SHFree(pidl);
  536.                     break;
  537.                 }
  538.                 SHFree(pidl);
  539.             }
  540.             penum->lpVtbl->Release(penum);
  541.         }
  542.         psf->lpVtbl->Release(psf);
  543.     }
  544. }
  545. //----------------------------------------------------------------------------
  546. // BUGBUG: hwndOwner is no longer used (NULL is passed in) remove the hwndOwner code
  547. // once we are certain that this isn't needed
  548. void _ExecuteStartupPrograms(HWND hwndOwner)
  549. {
  550.     LPITEMIDLIST pidlStartup;
  551.     if (AbortStartup())
  552.         return;
  553.     pidlStartup = SHCloneSpecialIDList(NULL, CSIDL_COMMON_STARTUP, TRUE);
  554.     if (pidlStartup)
  555.     {
  556.         EnumFolder(hwndOwner, pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Startup);
  557.         ILFree(pidlStartup);
  558.     }
  559.     //
  560.     // Execute non-localized "Common StartUp" group if exists.
  561.     //
  562.     pidlStartup = SHCloneSpecialIDList(NULL, CSIDL_COMMON_ALTSTARTUP, FALSE);
  563.     if (pidlStartup)
  564.     {
  565.         EnumFolder(hwndOwner, pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Startup);
  566.         ILFree(pidlStartup);
  567.     }
  568.     pidlStartup = SHCloneSpecialIDList(NULL, CSIDL_STARTUP, TRUE);
  569.     if (pidlStartup)
  570.     {
  571.         EnumFolder(hwndOwner, pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Startup);
  572.         ILFree(pidlStartup);
  573.     }
  574.     //
  575.     // Execute non-localized "StartUp" group if exists.
  576.     //
  577.     pidlStartup = SHCloneSpecialIDList(NULL, CSIDL_ALTSTARTUP, FALSE);
  578.     if (pidlStartup)
  579.     {
  580.         EnumFolder(hwndOwner, pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, EnumFolder_Startup);
  581.         ILFree(pidlStartup);
  582.     }
  583. }
  584. // BUGBUG:: A bunch of this code can get reduced down... ie boiled
  585. const LPTSTR c_aszForceCheckWinIni[] = {TEXT("GROUPS.B$$"), NULL};
  586. void CheckWinIniForAssocs(void);
  587. void BoilThatDustSpec(LPTSTR pStart, int nCmdShow)
  588. {
  589.     BOOL bFinished;
  590.     bFinished = FALSE;
  591.     while (!bFinished && !AbortStartup())
  592.     {
  593.         SHELLEXECUTEINFO ei;
  594.         LPTSTR pEnd;
  595.         pEnd = pStart;
  596.         while ((*pEnd) && (*pEnd != TEXT(' ')) && (*pEnd != TEXT(',')))
  597.             pEnd = (LPTSTR)OFFSETOF(CharNext(pEnd));
  598.         if (*pEnd == 0)
  599.             bFinished = TRUE;
  600.         else
  601.             *pEnd = 0;
  602.         if (lstrlen(pStart) != 0)
  603.         {
  604.             LPTSTR pszFile;
  605.             TCHAR szFile[MAX_PATH];
  606.             const LPTSTR *ppszForce;
  607.             // Load and Run lines are done relative to windows directory.
  608.             GetWindowsDirectory(szFile, ARRAYSIZE(szFile));
  609.             SetCurrentDirectory(szFile);
  610.             pszFile = PathFindFileName(pStart);
  611.             lstrcpy(szFile, pszFile);
  612.             PathRemoveFileSpec(pStart);
  613.             // App hacks to get borlands Setup program to work
  614.             for (ppszForce = c_aszForceCheckWinIni; *ppszForce; ppszForce++)
  615.             {
  616.                 if (lstrcmpi(szFile, *ppszForce) == 0)
  617.                 {
  618.                     DebugMsg(DM_TRACE, TEXT("c.boil: Apphack %s force winini scan"), szFile);
  619.                     CheckWinIniForAssocs();
  620.                     break;
  621.                 }
  622.             }
  623.             ei.cbSize          = sizeof(SHELLEXECUTEINFO);
  624.             ei.hwnd            = NULL;
  625.             ei.lpVerb          = NULL;
  626.             ei.lpFile          = szFile;
  627.             ei.lpParameters    = NULL;
  628.             ei.lpDirectory     = pStart;
  629.             ei.nShow           = nCmdShow;
  630.             ei.fMask           = 0;
  631.             if (!ShellExecuteEx(&ei))
  632.             {
  633.                 ShellMessageBox(hinstCabinet, NULL, MAKEINTRESOURCE(IDS_WINININORUN),
  634.                                 MAKEINTRESOURCE(IDS_DESKTOP),
  635.                                 MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL,
  636.                                 (LPTSTR)szFile);
  637.             }
  638.         }
  639.         pStart = pEnd+1;
  640.     }
  641. }
  642. void _DoRunEquals()
  643. {
  644.     TCHAR szBuffer[255];        // max size of load= run= lines...
  645.     if (g_fCleanBoot)
  646.         return;
  647.     /* "Load" apps before "Run"ning any. */
  648.     GetProfileString(TEXT("windows"), TEXT("Load"), TEXT(""), szBuffer, ARRAYSIZE(szBuffer));
  649.     if (*szBuffer)
  650.         BoilThatDustSpec(szBuffer, SW_SHOWMINNOACTIVE);
  651.     GetProfileString(TEXT("windows"), TEXT("Run"), TEXT(""), szBuffer, ARRAYSIZE(szBuffer));
  652.     if (*szBuffer)
  653.         BoilThatDustSpec(szBuffer, SW_SHOWNORMAL);
  654. }
  655. //---------------------------------------------------------------------------
  656. // Use IERnonce.dll to process RunOnceEx key
  657. //
  658. typedef void (WINAPI *RUNONCEEXPROCESS)(HWND, HINSTANCE, LPSTR, int);
  659. void ProcessRunOnceEx()
  660. {
  661.     HKEY hkey;
  662.     if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCEEX,
  663.                     0, KEY_QUERY_VALUE, &hkey))
  664.     {
  665.         
  666. DWORD dwNumSubKeys = 0;
  667.         RegQueryInfoKey(hkey, NULL, NULL, NULL, &dwNumSubKeys, NULL,
  668.                         NULL, NULL, NULL, NULL, NULL, NULL);
  669.         RegCloseKey(hkey);
  670.         if (dwNumSubKeys)
  671.         {
  672.             HANDLE hLib;
  673.             TCHAR szPath[MAX_PATH];
  674.             GetSystemDirectory(szPath, ARRAYSIZE(szPath));
  675.             PathAppend(szPath, TEXT("iernonce.dll"));
  676.             hLib = LoadLibrary(szPath);
  677.             if (hLib)
  678.             {
  679.                 RUNONCEEXPROCESS pfnRunOnceExProcess;
  680. #ifdef WINNT    // Hydra-Specific
  681.                 BOOL    hydraInAppInstallMode = FALSE;
  682.                 
  683.                 // See if we are on NT5, and if the terminal-services is enabled
  684.                 if (g_fRuningOnTerminalServer) 
  685.                 {
  686.                     hydraInAppInstallMode = SetTermsrvAppInstallMode(TRUE); 
  687.                 }
  688. #endif // WINNT
  689.                 pfnRunOnceExProcess = (RUNONCEEXPROCESS)GetProcAddress(hLib, "RunOnceExProcess");
  690.                 if (pfnRunOnceExProcess)
  691.                 {
  692.                     // the four param in the function is due to the function cab be called
  693.                     // from RunDLL which will path in those params.  But RunOnceExProcess ignore all
  694.                     // of them.  Therefore, I don't pass any meaningful thing here.
  695.                     //
  696.                     pfnRunOnceExProcess(NULL, NULL, NULL, 0);
  697.                 }
  698.                 FreeLibrary(hLib);
  699.                 
  700. #ifdef WINNT    // Hydra-Specific
  701.                 if (hydraInAppInstallMode)
  702.                 {
  703.                     SetTermsrvAppInstallMode(FALSE) ;
  704.                 } 
  705. #endif // WINNT
  706.             }
  707.         }
  708.     }
  709. }
  710. #define REGTIPS             REGSTR_PATH_EXPLORER TEXT("\Tips")
  711. #define SZ_REGKEY_RUNSRVWIZ TEXT("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Setup\Welcome")
  712. // BUGBUG: This can be removed as soon as the policy editor and setup are updated to
  713. // place welcome under one of the prioritized HKCURun subkeys.
  714. UINT _RunWelcome()
  715. {
  716.     HKEY hkey;
  717.     BOOL fShow = FALSE;
  718.     UINT uPeek = PEEK_NORMAL;
  719.     if (RegOpenKey(HKEY_CURRENT_USER, REGTIPS, &hkey) == ERROR_SUCCESS)
  720.     {
  721.         DWORD cbData = SIZEOF(fShow);
  722.         RegQueryValueEx(hkey, TEXT("Show"), NULL, NULL, (LPBYTE)&fShow, &cbData);
  723.         RegCloseKey(hkey);
  724.     }
  725.     if (fShow)
  726.     {
  727.         DWORD dwType;
  728.         DWORD dwData;
  729.         DWORD cbSize = sizeof(dwData);
  730.         TCHAR    szCmdLine[MAX_PATH * 2];
  731.         PROCESS_INFORMATION pi;
  732.         STARTUPINFO startup = {0};;
  733.         startup.cb = SIZEOF(startup);
  734.         startup.wShowWindow = SW_SHOWNORMAL;
  735.         if ( IsOS(OS_WIN2000PRO) || IsOS(OS_WINDOWS) )
  736.         {
  737.             // Only run welcome.exe if we are on Professional (or win9x)
  738.             GetWindowsDirectory(szCmdLine, ARRAYSIZE(szCmdLine));
  739.             PathAppend(szCmdLine, TEXT("Welcome.exe"));
  740.         }
  741.         else if ( (IsOS(OS_WIN2000SERVER) || IsOS(OS_WIN2000ADVSERVER)) &&
  742.                   IsUserAnAdmin() &&
  743.                   ((ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, SZ_REGKEY_RUNSRVWIZ, TEXT("SrvWiz"), &dwType, (LPBYTE)&dwData, &cbSize)) || (dwData != 0)))
  744.         {
  745.             // launch Configure Your Server for system administrators on Win2000 Server and Advanced Server
  746.             GetSystemDirectory(szCmdLine, ARRAYSIZE(szCmdLine));
  747.             PathAppend(szCmdLine, TEXT("mshta.exe res://srvwiz.dll/default.hta"));
  748.         }
  749.         else 
  750.         {
  751.             // If neither or the above are true don't try to run anything.
  752.             return 0;
  753.         }
  754.         if (CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &pi))
  755.         {
  756.             WaitForSingleObject(pi.hProcess, INFINITE);
  757.             CloseHandle(pi.hProcess);
  758.             CloseHandle(pi.hThread);
  759.         }
  760.     }
  761.     return uPeek;
  762. }
  763. #ifdef WINNT
  764. // On NT, run the TASKMAN= line from the registry
  765. void _AutoRunTaskMan(void)
  766. {
  767.     HKEY hkeyWinLogon;
  768.     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"),
  769.                      0, KEY_READ, &hkeyWinLogon) == ERROR_SUCCESS)
  770.     {
  771.         TCHAR szBuffer[MAX_PATH];
  772.         DWORD cbBuffer = SIZEOF(szBuffer);
  773.         if (RegQueryValueEx(hkeyWinLogon, TEXT("Taskman"), 0, NULL, (LPBYTE)szBuffer, &cbBuffer) == ERROR_SUCCESS)
  774.         {
  775.             if (szBuffer[0])
  776.             {
  777.                 PROCESS_INFORMATION pi;
  778.                 STARTUPINFO startup = {0};
  779.                 startup.cb = SIZEOF(startup);
  780.                 startup.wShowWindow = SW_SHOWNORMAL;
  781.                 if (CreateProcess(NULL, szBuffer, NULL, NULL, FALSE, 0,
  782.                                   NULL, NULL, &startup, &pi))
  783.                 {
  784.                     CloseHandle(pi.hProcess);
  785.                     CloseHandle(pi.hThread);
  786.                 }
  787.             }
  788.         }
  789.         RegCloseKey(hkeyWinLogon);
  790.     }
  791. }
  792. #else
  793. #define _AutoRunTaskMan()       // nothing on Win95
  794. #endif
  795. #ifdef DEBUG
  796. //---------------------------------------------------------------------------
  797. // Copy the exception info so we can get debug info for Raised exceptions
  798. // which don't go through the debugger.
  799. void _CopyExceptionInfo(LPEXCEPTION_POINTERS pep)
  800. {
  801.     PEXCEPTION_RECORD per;
  802.     per = pep->ExceptionRecord;
  803.     DebugMsg(DM_ERROR, TEXT("Exception %x at %#08x."), per->ExceptionCode, per->ExceptionAddress);
  804.     if (per->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
  805.     {
  806.         // If the first param is 1 then this was a write.
  807.         // If the first param is 0 then this was a read.
  808.         if (per->ExceptionInformation[0])
  809.         {
  810.             DebugMsg(DM_ERROR, TEXT("Invalid write to %#08x."), per->ExceptionInformation[1]);
  811.         }
  812.         else
  813.         {
  814.             DebugMsg(DM_ERROR, TEXT("Invalid read of %#08x."), per->ExceptionInformation[1]);
  815.         }
  816.     }
  817. }
  818. #else
  819. #define _CopyExceptionInfo(x) TRUE
  820. #endif
  821. // try to create this by sending a wm_command directly to
  822. // the desktop.
  823. BOOL MyCreateFromDesktop(HINSTANCE hInst, LPCTSTR pszCmdLine, int nCmdShow)
  824. {
  825.     NEWFOLDERINFO fi = {0};
  826.     BOOL bRet = FALSE;
  827.     fi.nShow = nCmdShow;
  828.     //  since we have browseui fill out the fi, 
  829.     //  SHExplorerParseCmdLine() does a GetCommandLine()
  830.     if (SHExplorerParseCmdLine(&fi))
  831.         bRet = SHCreateFromDesktop(&fi);
  832.     //  should we also have it cleanup after itself??
  833.     //  SHExplorerParseCmdLine() can allocate this buffer...
  834.     if (fi.uFlags & COF_PARSEPATH)
  835.         LocalFree(fi.pszPath);
  836.         
  837.     ILFree(fi.pidl);
  838.     ILFree(fi.pidlRoot);
  839.     return bRet;
  840. }
  841. BOOL g_fDragFullWindows=FALSE;
  842. int g_cxEdge=0;
  843. int g_cyEdge=0;
  844. int g_cySize=0;
  845. int g_cxTabSpace=0;
  846. int g_cyTabSpace=0;
  847. int g_cxBorder=0;
  848. int g_cyBorder=0;
  849. int g_cxPrimaryDisplay=0;
  850. int g_cyPrimaryDisplay=0;
  851. int g_cxDlgFrame=0;
  852. int g_cyDlgFrame=0;
  853. int g_cxFrame=0;
  854. int g_cyFrame=0;
  855. int g_cxMinimized=0;
  856. int g_fCleanBoot=0;
  857. int g_cxVScroll=0;
  858. int g_cyHScroll=0;
  859. void Cabinet_InitGlobalMetrics(WPARAM wParam, LPTSTR lpszSection)
  860. {
  861.     BOOL fForce = (!lpszSection || !*lpszSection);
  862.     if (fForce || wParam == SPI_SETDRAGFULLWINDOWS) {
  863.         SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &g_fDragFullWindows, 0);
  864.     }
  865.     if (fForce || !lstrcmpi(lpszSection, TEXT("WindowMetrics")) ||
  866.         wParam == SPI_SETNONCLIENTMETRICS) {
  867.         // REVIEW, before it's all over, make sure all these vars are used somewhere.
  868.         g_cxEdge = GetSystemMetrics(SM_CXEDGE);
  869.         g_cyEdge = GetSystemMetrics(SM_CYEDGE);
  870. #ifdef NASHFLAT_TASKBAR
  871.         g_cxTabSpace = (g_cxEdge * 5);
  872. #else
  873.         g_cxTabSpace = (g_cxEdge * 3) / 2;
  874. #endif
  875.         g_cyTabSpace = (g_cyEdge * 3) / 2; // cause the graphic designers really really want 3.
  876.         g_cySize = GetSystemMetrics(SM_CYSIZE);
  877.         g_cxBorder = GetSystemMetrics(SM_CXBORDER);
  878.         g_cyBorder = GetSystemMetrics(SM_CYBORDER);
  879.         g_cxVScroll = GetSystemMetrics(SM_CXVSCROLL);
  880.         g_cyHScroll = GetSystemMetrics(SM_CYHSCROLL);
  881.         g_cxDlgFrame = GetSystemMetrics(SM_CXDLGFRAME);
  882.         g_cyDlgFrame = GetSystemMetrics(SM_CYDLGFRAME);
  883.         g_cxFrame  = GetSystemMetrics(SM_CXFRAME);
  884.         g_cyFrame  = GetSystemMetrics(SM_CYFRAME);
  885.         g_cxMinimized = GetSystemMetrics(SM_CXMINIMIZED);
  886.         g_cxPrimaryDisplay = GetSystemMetrics(SM_CXSCREEN);
  887.         g_cyPrimaryDisplay = GetSystemMetrics(SM_CYSCREEN);
  888.     }
  889. }
  890. //---------------------------------------------------------------------------
  891. void _CreateAppGlobals()
  892. {
  893.     Cabinet_InitGlobalMetrics(0, NULL);
  894. #ifdef WINNT
  895.     g_bRunOnNT5 = BOOLIFY(IsOS(OS_NT5));
  896. #else
  897.     g_bRunOnMemphis = BOOLIFY(IsOS(OS_MEMPHIS));
  898. #endif
  899.     //
  900.     // Check if the mirroring APIs exist on the current
  901.     // platform.
  902.     //
  903.     g_bMirroredOS = IS_MIRRORING_ENABLED();
  904.     // make sure we got our #defines right...
  905.     ASSERT(BOOLIFY(g_bRunOnNT) == BOOLIFY(IsOS(OS_NT)));
  906.     ASSERT(BOOLIFY(g_bRunOnNT5) == BOOLIFY(IsOS(OS_NT5)));
  907.     ASSERT(BOOLIFY(g_bRunOnMemphis) == BOOLIFY(IsOS(OS_MEMPHIS)));
  908. }
  909. //
  910. //  This function checks if any of the shell windows is already created by
  911. // another instance of explorer and returns TRUE if so.
  912. //
  913. BOOL IsAnyShellWindowAlreadyPresent()
  914. {
  915.     return GetShellWindow() || FindWindow(TEXT("Proxy Desktop"), NULL);
  916. }
  917. // See if the Shell= line indicates that we are the shell
  918. BOOL ExplorerIsShell()
  919. {
  920.     TCHAR *pszPathName, szPath[MAX_PATH];
  921.     TCHAR *pszModuleName, szModulePath[MAX_PATH];
  922.     ASSERT(!IsAnyShellWindowAlreadyPresent());
  923.     GetModuleFileName(NULL, szModulePath, ARRAYSIZE(szModulePath));
  924.     pszModuleName = PathFindFileName(szModulePath);
  925.     GetPrivateProfileString(TEXT("boot"), TEXT("shell"), pszModuleName, szPath, ARRAYSIZE(szPath), TEXT("system.ini"));
  926.     PathRemoveArgs(szPath);
  927.     PathRemoveBlanks(szPath);
  928.     pszPathName = PathFindFileName(szPath);
  929.     // NB Special case shell=install.exe - assume we are the shell.
  930.     // Symantec un-installers temporarily set shell=installer.exe so
  931.     // we think we're not the shell when we are. They fail to clean up
  932.     // a bunch of links if we don't do this.
  933.     return StrCmpNI(pszPathName, pszModuleName, lstrlen(pszModuleName)) == 0 ||
  934.            lstrcmpi(pszPathName, TEXT("install.exe")) == 0;
  935. }
  936. // Returns TRUE of this is the first time the explorer is run
  937. BOOL ShouldStartDesktopAndTray()
  938. {
  939.     // We need to be careful on which window we look for.  If we look for
  940.     // our desktop window class and Progman is running we will find the
  941.     // progman window.  So Instead we should ask user for the shell window.
  942.     // We can not depend on any values being set here as this is the
  943.     // start of a new process.  This wont be called when we start new
  944.     // threads.
  945.     return !IsAnyShellWindowAlreadyPresent() && ExplorerIsShell();
  946. }
  947. void DisplayCleanBootMsg()
  948. {
  949.     TCHAR szMsg[1024];
  950.     TCHAR szTitle[80];
  951.     int ids;
  952.     int cb;
  953.     LPTSTR pszMsg = szMsg;
  954.     szMsg[0] = TEXT('');
  955.     for (ids=IDS_CLEANBOOTMSG1; ids <= IDS_CLEANBOOTMSG4 ; ids++)
  956.     {
  957.         cb = LoadString(hinstCabinet, ids, pszMsg,
  958.                 ARRAYSIZE(szMsg) - (int)(pszMsg - szMsg));
  959.         if (cb == 0)
  960.             break;
  961.         pszMsg += cb;
  962.     }
  963.     // Make sure it is NULL terminated
  964.     *pszMsg = TEXT('');
  965.     LoadString(hinstCabinet, IDS_DESKTOP, szTitle, ARRAYSIZE(szTitle));
  966.     // Now display the message.
  967.     MessageBox(NULL, szMsg, szTitle,
  968.                   MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
  969. }
  970. //---------------------------------------------------------------------------
  971. const CHAR c_szTimeChangedRunDLL[] = "rundll32 shell32.dll,Control_RunDLL timedate.cpl,,/m";
  972. void DoDaylightCheck(BOOL fStartupInit)
  973. {
  974.     DWORD changed;
  975.     DebugMsg(DM_TRACE, TEXT("c.ddc(%d): calling k32.rdi"), fStartupInit);
  976. #ifdef WINNT
  977.     changed = FALSE;
  978. #else
  979.     // Win95 base does not automatically handle timezone cutover
  980.     // we have poke it every so often...
  981.     changed = RefreshDaylightInformation(TRUE);
  982. #endif
  983.     if (changed > 0)
  984.     {
  985.         DebugMsg(DM_TRACE, TEXT("c.ddc(%d): rdi changed - %lu"), fStartupInit, changed);
  986.         // something actually changed, tell everbody
  987.         if (!fStartupInit)
  988.         {
  989.             SendMessage((HWND)-1, WM_TIMECHANGE, 0, 0);
  990.             // if the local time changed tell the user
  991.             if (changed > 1)
  992.                 WinExec(c_szTimeChangedRunDLL, SW_SHOWNORMAL);
  993.         }
  994.         else
  995.         {
  996.             // there should only be "server" processes around anyway
  997.             PostMessage((HWND)-1, WM_TIMECHANGE, 0, 0);
  998.             // if the local time changed queue a runonce to tell the user
  999.             if (changed > 1)
  1000.             {
  1001.                 HKEY runonce;
  1002.                 if (RegCreateKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCE, &runonce) ==
  1003.                     ERROR_SUCCESS)
  1004.                 {
  1005.                     RegSetValueEx(runonce, TEXT("WarnTimeChanged"), 0, REG_SZ,
  1006.                         (LPBYTE)c_szTimeChangedRunDLL, SIZEOF(c_szTimeChangedRunDLL));
  1007.                     RegCloseKey(runonce);
  1008.                 }
  1009.             }
  1010.         }
  1011.     }
  1012. }
  1013. BOOL IsExecCmd(LPCTSTR pszCmd)
  1014. {
  1015.     return *pszCmd && !StrStrI(pszCmd, TEXT("-embedding"));
  1016. }
  1017. // run the cmd line passed up from win.com
  1018. void _RunWinComCmdLine(LPCTSTR pszCmdLine, UINT nCmdShow)
  1019. {
  1020.     if (IsExecCmd(pszCmdLine))
  1021.     {
  1022.         SHELLEXECUTEINFO ei = { SIZEOF(ei), 0, NULL, NULL, pszCmdLine, NULL, NULL, nCmdShow};
  1023.         ei.lpParameters = PathGetArgs(pszCmdLine);
  1024.         if (*ei.lpParameters)
  1025.             *((LPTSTR)ei.lpParameters - 1) = 0;     // const -> non const
  1026.         ShellExecuteEx(&ei);
  1027.     }
  1028. }
  1029. // stolen from the CRT, used to shirink our code
  1030. #ifdef DEBUG // For leak detection
  1031. BOOL g_fInitTable = FALSE;
  1032. LEAKDETECTFUNCS LeakDetFunctionTable;
  1033. #endif
  1034. // ccover uses runtime libs, that in turn require main()
  1035. #ifdef CCOVER
  1036. STDAPI_(int) ModuleEntry(void);
  1037. void __cdecl main()  {
  1038.     ModuleEntry();
  1039. };
  1040. #endif
  1041. STDAPI_(int) ModuleEntry(void)
  1042. {
  1043.     int i;
  1044.     STARTUPINFOA si;
  1045.     LPTSTR pszCmdLine;
  1046. #ifdef DEBUG
  1047.     // leak detection
  1048.     if(!g_fInitTable)
  1049.     {
  1050.         if(GetLeakDetectionFunctionTable(&LeakDetFunctionTable))
  1051.             g_fInitTable = TRUE;
  1052.     }
  1053.     if(g_fInitTable)
  1054.         LeakDetFunctionTable.pfnDebugMemLeak(DML_TYPE_THREAD | DML_BEGIN, TEXT(__FILE__), __LINE__);
  1055. #endif
  1056. #ifdef WINNT    
  1057.     // Hydra-Specific
  1058.     g_fRuningOnTerminalServer = IsTerminalServicesEnabled();
  1059. #endif
  1060.     pszCmdLine = GetCommandLine();
  1061.     //
  1062.     // We don't want the "No disk in drive X:" requesters, so we set
  1063.     // the critical error mask such that calls will just silently fail
  1064.     //
  1065.     SetErrorMode(SEM_FAILCRITICALERRORS);
  1066.     if ( *pszCmdLine == TEXT('"') ) {
  1067.         /*
  1068.          * Scan, and skip over, subsequent characters until
  1069.          * another double-quote or a null is encountered.
  1070.          */
  1071.         while ( *++pszCmdLine && (*pszCmdLine
  1072.              != TEXT('"')) );
  1073.         /*
  1074.          * If we stopped on a double-quote (usual case), skip
  1075.          * over it.
  1076.          */
  1077.         if ( *pszCmdLine == TEXT('"') )
  1078.             pszCmdLine++;
  1079.     }
  1080.     else {
  1081.         while (*pszCmdLine > TEXT(' '))
  1082.             pszCmdLine++;
  1083.     }
  1084.     /*
  1085.      * Skip past any white space preceeding the second token.
  1086.      */
  1087.     while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) {
  1088.         pszCmdLine++;
  1089.     }
  1090.     si.dwFlags = 0;
  1091.     GetStartupInfoA(&si);
  1092.     i = ExplorerWinMain(GetModuleHandle(NULL), NULL, pszCmdLine,
  1093.                    si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
  1094.     // Since we now have a way for an extension to tell us when it is finished,
  1095.     // we will terminate all processes when the main thread goes away.
  1096. #ifdef DEBUG
  1097.     // we stop leak detection on main shell thread here
  1098.     // we need to change this
  1099.     if (g_fInitTable)
  1100.         LeakDetFunctionTable.pfnDebugMemLeak(DML_TYPE_THREAD | DML_END, TEXT(__FILE__), __LINE__);
  1101. #endif
  1102.     if (g_fExitExplorer)    // desktop told us not to exit
  1103.     {
  1104.         TraceMsg(DM_SHUTDOWN, "c.me: call ExitProcess");
  1105. #ifdef CCOVER
  1106.         cov_write();
  1107. #endif
  1108.         ExitProcess(i);
  1109.     }
  1110.     DebugMsg(DM_TRACE, TEXT("c.me: Cabinet main thread exiting without ExitProcess."));
  1111.     return i;
  1112. }
  1113. void _InitComctl32()
  1114. {
  1115.     INITCOMMONCONTROLSEX icce;
  1116.     // init the controls
  1117.     icce.dwICC = ICC_COOL_CLASSES | ICC_WIN95_CLASSES | ICC_PAGESCROLLER_CLASS;
  1118.     icce.dwSize = sizeof(icce);
  1119.     InitCommonControlsEx(&icce);
  1120. }
  1121. // cached "handle" to the desktop
  1122. HANDLE g_hDesktop = NULL;
  1123. extern IDeskTray* const c_pdtray;
  1124. BOOL CreateDesktopAndTray()
  1125. {
  1126.     BOOL fRet = TRUE;
  1127.     if (g_dwProfileCAP & 0x00008000)
  1128.         StartCAPAll();
  1129.     if (!v_hwndTray)
  1130.     {
  1131.         InitTrayClass(hinstCabinet);
  1132.         if (!InitTray(hinstCabinet))
  1133.             return FALSE;
  1134.     }
  1135.     ASSERT(v_hwndTray);
  1136.     if (!v_hwndDesktop)
  1137.     {
  1138.         ASSERT(!g_hDesktop);
  1139.         // cache the handle to the desktop...
  1140.         g_hDesktop = SHCreateDesktop(c_pdtray);
  1141.         if (g_hDesktop == NULL)
  1142.             fRet = FALSE;
  1143.     }
  1144.     if (g_dwProfileCAP & 0x80000000)
  1145.         StopCAPAll();
  1146.     return fRet;
  1147. }
  1148. //
  1149. //  The "Session key" is a volatile registry key unique to this session.
  1150. //  A session is a single continuous logon.  If Explorer crashes and is
  1151. //  auto-restarted, the two Explorers share the same session.  But if you
  1152. //  log off and back on, that new Explorer is a new session.
  1153. //
  1154. //  Note that Win9x doesn't support volatile registry keys, so we have to
  1155. //  fake it.  IsFirstInstanceAfterLogon() answers the question *and*
  1156. //  initializes the session key.
  1157. //
  1158. //
  1159. //  The s_SessionKeyName is the name of the session key relative to
  1160. //  REGSTR_PATH_EXPLORERSessionInfo.  On NT, this is normally the
  1161. //  Authentication ID, but we pre-initialize it to something safe so
  1162. //  we don't fault if for some reason we can't get to it.  Since
  1163. //  Win95 supports only one session at a time, it just stays at the
  1164. //  default value.
  1165. //
  1166. //  Sometimes we want to talk about the full path (SessionInfoBlahBlah)
  1167. //  and sometimes just the partial path (BlahBlah) so we wrap it inside
  1168. //  this goofy structure.
  1169. //
  1170. union SESSIONKEYNAME {
  1171.     TCHAR szPath[12+16+1];
  1172.     struct {
  1173.         TCHAR szSessionInfo[12];    // strlen("SessionInfo\")
  1174.         TCHAR szName[16+1];         // 16 = two DWORDs converted to hex
  1175.     };
  1176. } s_SessionKeyName = {
  1177.     { TEXT("SessionInfo\.Default") }
  1178. };
  1179. HKEY GetSessionKey(REGSAM samDesired)
  1180. {
  1181.     HKEY hkExp;
  1182.     HKEY hkSession = NULL;
  1183.     DWORD dwDisposition;
  1184.     LONG lRes;
  1185.     //
  1186.     // Must create this key in multiple steps, because we want
  1187.     // SessionInfo to be volatile, but REGSTR_PATH_EXPLORER to be
  1188.     // nonvolatile.
  1189.     //
  1190.     lRes = RegCreateKeyEx(HKEY_CURRENT_USER,
  1191.                           REGSTR_PATH_EXPLORER, 0,
  1192.                           NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED,
  1193.                           NULL, &hkExp, &dwDisposition);
  1194.     if (lRes == ERROR_SUCCESS)
  1195.     {
  1196.         lRes = RegCreateKeyEx(hkExp, s_SessionKeyName.szPath, 0,
  1197.                        NULL,
  1198.                        REG_OPTION_VOLATILE,
  1199.                        samDesired,
  1200.                        NULL,
  1201.                        &hkSession,
  1202.                        &dwDisposition );
  1203.         RegCloseKey(hkExp);
  1204.     }
  1205.     return hkSession;
  1206. }
  1207. // Removes the session key from the registry.
  1208. void NukeSessionKey(void)
  1209. {
  1210.     HKEY hkExp;
  1211.     LONG lRes;
  1212.     lRes = RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, &hkExp);
  1213.     if (lRes == ERROR_SUCCESS)
  1214.     {
  1215.         SHDeleteKey(hkExp, s_SessionKeyName.szPath);
  1216.         RegCloseKey(hkExp);
  1217.     }
  1218. }
  1219. BOOL IsFirstInstanceAfterLogon()
  1220. {
  1221.     BOOL fResult = FALSE;
  1222. #ifdef WINNT
  1223.     LONG lRes;
  1224.     HANDLE hToken;
  1225.     //
  1226.     //  Build the name of the session key.  We use the authentication ID
  1227.     //  which is guaranteed to be unique forever.  We can't use the
  1228.     //  Hydra session ID since that can be recycled.
  1229.     //
  1230.     if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  1231.     {
  1232.         TOKEN_STATISTICS stats;
  1233.         DWORD cbOut;
  1234.         if (GetTokenInformation(hToken, TokenStatistics, &stats, sizeof(stats), &cbOut))
  1235.         {
  1236.             HKEY hkSession;
  1237.             wsprintf(s_SessionKeyName.szName, TEXT("%08x%08x"),
  1238.                      stats.AuthenticationId.HighPart,
  1239.                      stats.AuthenticationId.LowPart);
  1240.             hkSession = GetSessionKey(KEY_WRITE);
  1241.             if (hkSession) {
  1242.                 HKEY hkStartup;
  1243.                 DWORD dwDisposition;
  1244.                 lRes = RegCreateKeyEx(hkSession, TEXT("StartupHasBeenRun"), 0,
  1245.                                NULL,
  1246.                                REG_OPTION_VOLATILE,
  1247.                                KEY_WRITE,
  1248.                                NULL,
  1249.                                &hkStartup,
  1250.                                &dwDisposition );
  1251.                 if (lRes == ERROR_SUCCESS)
  1252.                 {
  1253.                     RegCloseKey(hkStartup);
  1254.                     if (dwDisposition == REG_CREATED_NEW_KEY)
  1255.                         fResult = TRUE;
  1256.                 }
  1257.                 RegCloseKey(hkSession);
  1258.             }
  1259.         }
  1260.         CloseHandle(hToken);
  1261.     }
  1262. #else
  1263.     // on win95, we use the overloaded RegisterShellHook to thunk to the 16 bit side to do 
  1264.     // the work for us. this gets fixed when the tray shuts down properly...
  1265.     fResult = RegisterShellHook(NULL,(BOOL) 4 );
  1266.     if (fResult) {
  1267.         // Clean out the "volatile" session info since Win9x doesn't support
  1268.         // volatile regkeys.
  1269.         NukeSessionKey();
  1270.     }
  1271. #endif
  1272.     return fResult;
  1273. }
  1274. //
  1275. //  dwValue is FALSE if this is startup, TRUE if this is shutdown,
  1276. //
  1277. void WriteCleanShutdown(DWORD dwValue)
  1278. {
  1279.     RegSetValueEx(g_hkeyExplorer, TEXT("CleanShutdown"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
  1280.     // If we are shutting down for real (i.e., not fake), then clean up the
  1281.     // session key so we don't leak a bazillion volatile keys into the
  1282.     // registry on a Hydra system when people log on and off and on and off...
  1283.     if (dwValue && !g_fFakeShutdown) {
  1284.         NukeSessionKey();
  1285.     }
  1286. }
  1287. BOOL ReadCleanShutdown()
  1288. {
  1289.     DWORD dwValue = 1;  // default: it was clean
  1290.     DWORD dwSize = sizeof(dwValue);
  1291.     RegQueryValueEx(g_hkeyExplorer, TEXT("CleanShutdown"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
  1292.     return (BOOL)dwValue;
  1293. }
  1294. // APP HACK APP HACK
  1295. // installing SP3 over ie4 on NT4 trashes the RSA key.
  1296. // same code is in mainloop.c of iexplore.exe
  1297. #ifdef WINNT
  1298. #define RSA_PATH_TO_KEY    TEXT("Software\Microsoft\Cryptography\Defaults\Provider\Microsoft Base Cryptographic Provider v1.0")
  1299. #define CSD_REG_PATH       TEXT("System\CurrentControlSet\Control\Windows")
  1300. #define CSD_REG_VALUE      TEXT("CSDVersion")
  1301. // the signatures we are looking for in the regsitry so that we can patch up
  1302. #ifdef _M_IX86
  1303. static  BYTE  SP3Sig[] = {0xbd, 0x9f, 0x13, 0xc5, 0x92, 0x12, 0x2b, 0x72,
  1304.                           0x4a, 0xba, 0xb6, 0x2a, 0xf9, 0xfc, 0x54, 0x46,
  1305.                           0x6f, 0xa1, 0xb4, 0xbb, 0x43, 0xa8, 0xfe, 0xf8,
  1306.                           0xa8, 0x23, 0x7d, 0xd1, 0x85, 0x84, 0x22, 0x6e,
  1307.                           0xb4, 0x58, 0x00, 0x3e, 0x0b, 0x19, 0x83, 0x88,
  1308.                           0x6a, 0x8d, 0x64, 0x02, 0xdf, 0x5f, 0x65, 0x7e,
  1309.                           0x3b, 0x4d, 0xd4, 0x10, 0x44, 0xb9, 0x46, 0x34,
  1310.                           0xf3, 0x40, 0xf4, 0xbc, 0x9f, 0x4b, 0x82, 0x1e,
  1311.                           0xcc, 0xa7, 0xd0, 0x2d, 0x22, 0xd7, 0xb1, 0xf0,
  1312.                           0x2e, 0xcd, 0x0e, 0x21, 0x52, 0xbc, 0x3e, 0x81,
  1313.                           0xb1, 0x1a, 0x86, 0x52, 0x4d, 0x3f, 0xfb, 0xa2,
  1314.                           0x9d, 0xae, 0xc6, 0x3d, 0xaa, 0x13, 0x4d, 0x18,
  1315.                           0x7c, 0xd2, 0x28, 0xce, 0x72, 0xb1, 0x26, 0x3f,
  1316.                           0xba, 0xf8, 0xa6, 0x4b, 0x01, 0xb9, 0xa4, 0x5c,
  1317.                           0x43, 0x68, 0xd3, 0x46, 0x81, 0x00, 0x7f, 0x6a,
  1318.                           0xd7, 0xd1, 0x69, 0x51, 0x47, 0x25, 0x14, 0x40,
  1319.                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  1320. #else // other than _M_IX86
  1321. static  BYTE  SP3Sig[] = {0x8a, 0x06, 0x01, 0x6d, 0xc2, 0xb5, 0xa2, 0x66,
  1322.                           0x12, 0x1b, 0x9c, 0xe4, 0x58, 0xb1, 0xf8, 0x7d,
  1323.                           0xad, 0x17, 0xc1, 0xf9, 0x3f, 0x87, 0xe3, 0x9c,
  1324.                           0xdd, 0xeb, 0xcc, 0xa8, 0x6b, 0x62, 0xd0, 0x72,
  1325.                           0xe7, 0xf2, 0xec, 0xd6, 0xd6, 0x36, 0xab, 0x2d,
  1326.                           0x28, 0xea, 0x74, 0x07, 0x0e, 0x6c, 0x6d, 0xe1,
  1327.                           0xf8, 0x17, 0x97, 0x13, 0x8d, 0xb1, 0x8b, 0x0b,
  1328.                           0x33, 0x97, 0xc5, 0x46, 0x66, 0x96, 0xb4, 0xf7,
  1329.                           0x03, 0xc5, 0x03, 0x98, 0xf7, 0x91, 0xae, 0x9d,
  1330.                           0x00, 0x1a, 0xc6, 0x86, 0x30, 0x5c, 0xc8, 0xc7,
  1331.                           0x05, 0x47, 0xed, 0x2d, 0xc2, 0x0b, 0x61, 0x4b,
  1332.                           0xce, 0xe5, 0xb7, 0xd7, 0x27, 0x0c, 0x9e, 0x2f,
  1333.                           0xc5, 0x25, 0xe3, 0x81, 0x13, 0x9d, 0xa2, 0x67,
  1334.                           0xb2, 0x26, 0xfc, 0x99, 0x9d, 0xce, 0x0e, 0xaf,
  1335.                           0x30, 0xf3, 0x30, 0xec, 0xa3, 0x0a, 0xfe, 0x16,
  1336.                           0xb6, 0xda, 0x16, 0x90, 0x9a, 0x9a, 0x74, 0x7a,
  1337.                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  1338. #endif      // _M_IX86
  1339. void CheckForSP3RSAOverwrite( void )
  1340. {
  1341.     // check for them having installed NTSP3 over the top of IE4, it nukes
  1342.     // the RSABASE reg stuff, so we have to re-do it. (our default platform is NT + SP3, but this
  1343.     // problem doesn't occur on NT5, so ignore it.
  1344.     OSVERSIONINFO osVer;
  1345.     ZeroMemory(&osVer, sizeof(osVer));
  1346.     osVer.dwOSVersionInfoSize = sizeof(osVer);
  1347.     if( GetVersionEx((OSVERSIONINFO *)&osVer) && (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
  1348.         && (osVer.dwMajorVersion == 4))
  1349.     {
  1350.         // now check to see we are on SP3 ...
  1351.         DWORD dwValue = 0;
  1352.         DWORD dwSize = sizeof( dwValue );
  1353.         if ( ERROR_SUCCESS == SHGetValue( HKEY_LOCAL_MACHINE, CSD_REG_PATH, CSD_REG_VALUE, NULL,
  1354.              &dwValue, &dwSize) && LOWORD( dwValue ) == 0x300 )
  1355.         {
  1356.             BYTE rgbSig[136];
  1357.             dwSize = sizeof(rgbSig);
  1358.             if (ERROR_SUCCESS == SHGetValue ( HKEY_LOCAL_MACHINE, RSA_PATH_TO_KEY, TEXT("Signature"), NULL,
  1359.                 rgbSig, &dwSize))
  1360.             {
  1361.                 if ((dwSize == sizeof(SP3Sig)) &&
  1362.                     (0 == memcmp(SP3Sig, rgbSig, sizeof(SP3Sig))))
  1363.                 {
  1364.                     // need to do a DLLRegisterServer on RSABase
  1365.                     HINSTANCE hInst = LoadLibrary(TEXT("rsabase.dll"));
  1366.                     if ( hInst )
  1367.                     {
  1368.                         FARPROC pfnDllReg = GetProcAddress( hInst, "DllRegisterServer");
  1369.                         if ( pfnDllReg )
  1370.                         {
  1371.                             __try
  1372.                             {
  1373.                                 pfnDllReg();
  1374.                             }
  1375.                             __except( EXCEPTION_EXECUTE_HANDLER)
  1376.                             {
  1377.                             }
  1378.                         }
  1379.                         FreeLibrary( hInst );
  1380.                     }
  1381.                 }
  1382.             }
  1383.         }
  1384.     }
  1385. }
  1386. #else
  1387. #define CheckForSP3RSAOverwrite()
  1388. #endif
  1389. #ifdef WINNT
  1390. //
  1391. //  Synopsis:   Waits for the OLE SCM process to finish its initialization.
  1392. //              This is called before the first call to OleInitialize since
  1393. //              the SHELL runs early in the boot process.
  1394. //
  1395. //  Arguments:  None.
  1396. //
  1397. //  Returns:    S_OK - SCM is running. OK to call OleInitialize.
  1398. //              CO_E_INIT_SCM_EXEC_FAILURE - timed out waiting for SCM
  1399. //              other - create event failed
  1400. //
  1401. //  History:    26-Oct-95   Rickhi  Extracted from CheckAndStartSCM so
  1402. //                                  that only the SHELL need call it.
  1403. //
  1404. HRESULT WaitForSCMToInitialize()
  1405. {
  1406.     static BOOL s_fScmStarted = FALSE;
  1407.     HANDLE hEvent;
  1408.     SECURITY_ATTRIBUTES sa;
  1409.     SECURITY_DESCRIPTOR sd;
  1410.     SECURITY_ATTRIBUTES *psa;
  1411.     if (s_fScmStarted)
  1412.         return S_OK;
  1413.     psa =  CreateAllAccessSecurityAttributes(&sa, &sd);
  1414.     if (g_bRunOnNT5)
  1415.     {
  1416.         // on NT5 we need a global event that is shared between hydra sessions
  1417.         hEvent = CreateEvent(psa, TRUE, FALSE, SZ_SCMCREATEDEVENT_NT5);
  1418.     } 
  1419.     else
  1420.     {
  1421.         hEvent = CreateEvent(psa, TRUE, FALSE, SZ_SCMCREATEDEVENT);
  1422.     }
  1423.     if (hEvent)
  1424.     {
  1425.         // wait for the SCM to signal the event, then close the handle
  1426.         // and return a code based on the WaitEvent result.
  1427.         int rc = WaitForSingleObject(hEvent, 60000);
  1428.         CloseHandle(hEvent);
  1429.         if (rc == WAIT_OBJECT_0)
  1430.         {
  1431.             s_fScmStarted = TRUE;
  1432.             return S_OK;
  1433.         }
  1434.         else if (rc == WAIT_TIMEOUT)
  1435.         {
  1436.             return CO_E_INIT_SCM_EXEC_FAILURE;
  1437.         }
  1438.     }
  1439.     return HRESULT_FROM_WIN32(GetLastError());  // event creation failed or WFSO failed.
  1440. }
  1441. #endif // WINNT
  1442. // OleInitialize()
  1443. STDAPI OleInitializeWaitForSCM()
  1444. {
  1445. #ifdef WINNT
  1446.     HRESULT hres = WaitForSCMToInitialize();
  1447.     if (FAILED(hres))
  1448.         return hres;
  1449. #endif
  1450.     return OleInitialize(NULL);
  1451. }
  1452. // we need to figure out the fFirstShellBoot on a per-user
  1453. // basis rather than once per machine.  We want the welcome
  1454. // splash screen to come up for every new user.
  1455. BOOL IsFirstShellBoot()
  1456. {
  1457.     DWORD dwDisp;
  1458.     HKEY hkey;
  1459.     BOOL fFirstShellBoot = TRUE;  // default value
  1460.     if (RegCreateKeyEx(HKEY_CURRENT_USER, REGTIPS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
  1461.                      NULL, &hkey, &dwDisp) == ERROR_SUCCESS)
  1462.     {
  1463.         DWORD dwSize = sizeof(fFirstShellBoot);
  1464.         RegQueryValueEx(hkey, TEXT("DisplayInitialTipWindow"), NULL, NULL, (LPBYTE)&fFirstShellBoot, &dwSize);
  1465.         if (fFirstShellBoot)
  1466.         {
  1467.             // Turn off the initial tip window for future shell starts.
  1468.             BOOL bTemp = FALSE;
  1469.             RegSetValueEx(hkey, TEXT("DisplayInitialTipWindow"), 0, REG_DWORD, (LPBYTE) &bTemp, sizeof(bTemp));
  1470.         }
  1471.         RegCloseKey(hkey);
  1472.     }
  1473.     return fFirstShellBoot;
  1474. }
  1475. //
  1476. // Post a message to the MTTF window if it is present
  1477. // The MTTF tool tracks this process being up
  1478. //
  1479. #define STR_MTTF_WINDOW_CLASS TEXT("HARVEYCAT")
  1480. #define MTTF_STARTUP  40006  
  1481. #define MTTF_SHUTDOWN 40007
  1482. void PostMTTFMessage(WPARAM mttfMsg)
  1483. {
  1484.     HWND hwndVerFind = FindWindow(STR_MTTF_WINDOW_CLASS, NULL);
  1485.     if (hwndVerFind)
  1486.     {
  1487.         PostMessage(hwndVerFind, WM_COMMAND, 
  1488.                     (WPARAM) mttfMsg, 
  1489.                     (LPARAM) GetCurrentProcessId());
  1490.     }
  1491. }
  1492. int ExplorerWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR pszCmdLine, int nCmdShow)
  1493. {
  1494.     DWORD dwShellStartTime;   // used for perf times
  1495.     CcshellGetDebugFlags();
  1496.     if (g_dwProfileCAP & 0x00000001)
  1497.         StartCAP();
  1498.     hinstCabinet = hInstance;
  1499.     g_fCleanBoot = GetSystemMetrics(SM_CLEANBOOT);      // also known as "Safe Mode"
  1500.     // Run IEAK via Wininet initialization if the autoconfig url is present.
  1501.     // No need to unload wininet in this case. Also only do this first time
  1502.     // Explorer loads (GetShellWindow() returns NULL).
  1503.     if (!GetShellWindow() && !g_fCleanBoot && SHRegGetUSValue(TEXT("Software\Microsoft\Windows\Internet Settings"),
  1504.                                          TEXT("AutoConfigURL"),
  1505.                                          NULL, NULL, NULL, FALSE, NULL, 0) == ERROR_SUCCESS)
  1506.     {
  1507.         LoadLibrary(TEXT("WININET.DLL"));
  1508.     }
  1509.     // Very Important: Make sure to init dde prior to any Get/Peek/Wait().
  1510.     InitializeCriticalSection(&g_csDll);
  1511.     _InitComctl32();
  1512.     if (g_dwPrototype & 0x80000000)
  1513.     {
  1514.         // Turn off GDI batching so that paints are performed immediately
  1515.         GdiSetBatchLimit(1);
  1516.     }
  1517.     RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, &g_hkeyExplorer);
  1518.     ASSERT(g_hkeyExplorer); // Really really bad..  unable to create reg explorer key
  1519.     if (!ShouldStartDesktopAndTray())
  1520.     {
  1521.         MyCreateFromDesktop(hInstance, pszCmdLine, nCmdShow);
  1522.     }
  1523.     else
  1524.     {
  1525.         MSG msg;
  1526.         /* In case shell32 was kept in memory by a service process, tell him to
  1527.          * refresh all restrictions, before we call SHRestricted or any other
  1528.          * shell API that might depend on restrictions (SHGetSpecialFolderPath,
  1529.          * for example). Otherwise, the current user will inherit some of the
  1530.          * previous user's restrictions.
  1531.          */
  1532.         SHSettingsChanged(0, 0);
  1533.         /*
  1534.          *  BUGBUG - Win9x - we need to nuke the drives list so the incoming
  1535.          *  user doesn't inherit drive icons from the previous user
  1536.          */
  1537.         dwShellStartTime = GetTickCount();    // Compute shell startup time for perf automation
  1538.         ShellDDEInit(TRUE);        // use shdocvw shell DDE code.
  1539.         //  Specify the shutdown order of the shell process.  2 means
  1540.         //  the explorer should shutdown after everything but ntsd/windbg
  1541.         //  (level 0).  (Taskman used to use 1, but is no more.)
  1542.         SetProcessShutdownParameters(2, 0);
  1543.         _AutoRunTaskMan();
  1544.         // NB Make this the primary thread by calling peek message
  1545.         // for a message we know we're not going to get.
  1546.         // If we don't do it really soon, the notify thread can sometimes
  1547.         // become the primary thread by accident. There's a bunch of
  1548.         // special code in user to implement DDE hacks by assuming that
  1549.         // the primary thread is handling DDE.
  1550.         // Also, the PeekMsg() will cause us to set the WaitForInputIdle()
  1551.         // event so we better be ready to do all dde.
  1552.         PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE);
  1553.         // Make sure we are the first one to call the FileIconInit...
  1554.         FileIconInit(TRUE); // Tell the shell we want to play with a full deck
  1555.         g_fLogonCycle = IsFirstInstanceAfterLogon();
  1556.         g_fCleanShutdown = ReadCleanShutdown();
  1557.         if (g_fLogonCycle)
  1558.         {
  1559.             HWND hwndWait;
  1560.             // force kernel32 to update the timezone before running any apps
  1561.             DoDaylightCheck(TRUE);
  1562.             ProcessRunOnceEx();
  1563.             // We are about to do something that might take a long time so we want to display the
  1564.             // wait cursor, however we haven't created the desktop window yet.  To get around this
  1565.             // we create a temporary window which we use to display the wait cursor.
  1566.             // REVIEW: Maybe we should move this up to show the wait cursor sooner and longer?
  1567.             hwndWait = CreateRegAppsWaitWindow();
  1568.             RunRegAppsAndObjects(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCE, RRA_DELETE | RRA_WAIT);
  1569.             DestroyRegAppsWaitWindow(hwndWait);
  1570.         }
  1571.         _CreateAppGlobals();
  1572.         CheckForSP3RSAOverwrite();
  1573.         if (g_fCleanBoot)
  1574.             DisplayCleanBootMsg();  // let users know we are in safe mode
  1575.         CreateShellDirectories();   // Create the other special folders.
  1576.         // Run install stubs for the current user, mostly to propagate
  1577.         // shortcuts to apps installed by another user.
  1578.         if (!g_fCleanBoot)
  1579.             RunInstallUninstallStubs();
  1580.         OleInitializeWaitForSCM();
  1581.         if (!g_fCleanShutdown)
  1582.         {
  1583.             HWND hwndVerFind;
  1584.             IActiveDesktopP *piadp;
  1585.             //
  1586.             // Post a message to the MTTF window if it is present
  1587.             //
  1588.             // BUGBUG Get LOR to use RegisterWindowMessage()
  1589.             // 
  1590.             hwndVerFind = FindWindow(TEXT("BFG2000"), NULL);
  1591.             if (hwndVerFind)
  1592.             {
  1593.                 PostMessage(hwndVerFind, WM_COMMAND, 40005, 0);
  1594.             }
  1595.             // Put the active desktop in safe mode if we faulted previously and this is a subsequent instance
  1596.             if (SUCCEEDED(CoCreateInstance(&CLSID_ActiveDesktop, NULL, CLSCTX_INPROC, &IID_IActiveDesktopP, (void **)&piadp)))
  1597.             {
  1598.                 piadp->lpVtbl->SetSafeMode(piadp, SSM_SET | SSM_UPDATE);
  1599.                 piadp->lpVtbl->Release(piadp);
  1600.             }
  1601.         }
  1602.         PostMTTFMessage(MTTF_STARTUP);
  1603.         WriteCleanShutdown(FALSE);    // assume we will have a bad shutdown
  1604.         WinList_Init();
  1605.         // If any of the shellwindows are already present, then we want to bail out.
  1606.         //
  1607.         // NOTE: Compaq shell changes the "shell=" line during RunOnce time and
  1608.         // that will make ShouldStartDesktopAndTray() return FALSE
  1609.         if (!IsAnyShellWindowAlreadyPresent() && CreateDesktopAndTray())
  1610.         {
  1611.             _RunWinComCmdLine(pszCmdLine, nCmdShow);
  1612.             if (StopWatchMode())
  1613.             {
  1614.                 // We used to save these off into global vars, and then write them at
  1615.                 // WM_ENDSESSION, but that seems too unreliable
  1616.                 DWORD dwShellStopTime = GetTickCount();
  1617.                 StopWatch_StartTimed(SWID_STARTUP, TEXT("Shell Startup: Start"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwShellStartTime);
  1618.                 StopWatch_StopTimed(SWID_STARTUP, TEXT("Shell Startup: Stop"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwShellStopTime);
  1619.             }
  1620.             if (g_dwProfileCAP & 0x00010000)
  1621.                 StopCAP();
  1622.             // this must be whomever is the window on this thread
  1623.             SHDesktopMessageLoop(g_hDesktop);
  1624.             WriteCleanShutdown(TRUE);    // we made it out ok, record that fact
  1625.             PostMTTFMessage(MTTF_SHUTDOWN);
  1626.         }
  1627.         WinList_Terminate();    // Turn off our window list processing
  1628.         OleUninitialize();
  1629.         ShellDDEInit(FALSE);    // use shdocvw shell DDE code
  1630.     }
  1631.     DebugMsg(DM_TRACE, TEXT("c.App Exit."));
  1632.     return TRUE;
  1633. }
  1634. DWORD WINAPI RunStartupAppsThread(void *pvVoid)
  1635. {
  1636.     // Some of the items we launch during startup assume that com is initialized.  Make this
  1637.     // assumption true.
  1638.     CoInitialize(0);
  1639.     // These global flags are set once long before our thread starts and are then only
  1640.     // read so we don't need to worry about timing issues.
  1641.     if (g_fLogonCycle && !g_fCleanBoot)
  1642.     {
  1643.         // We only run these startup items if g_fLogonCycle is TRUE. This prevents
  1644.         // them from running again if the shell crashes and restarts.
  1645.         _DoRunEquals();     // Process the Load= and Run= lines...
  1646.         RunRegAppsAndObjects(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, RRA_NOUI);
  1647.         RunRegAppsAndObjects(HKEY_CURRENT_USER, REGSTR_PATH_RUN, RRA_NOUI);
  1648.         _ExecuteStartupPrograms(NULL);
  1649.     }
  1650.     // As a best guess, the CURunOnce key is executed regardless of the g_fLogonCycle
  1651.     // becuase it was once hoped that we could install newer versions of IE without
  1652.     // requiring a reboot.  They would place something in the CURunOnce key and then
  1653.     // shutdown and restart the shell to continue their setup process.  I believe this
  1654.     // idea was later abandoned but the code change is still here.  Since that could
  1655.     // some day be a useful feature I'm leaving it the same.
  1656.     RunRegAppsAndObjects(HKEY_CURRENT_USER, REGSTR_PATH_RUNONCE, RRA_DELETE|RRA_NOUI);
  1657.     // we need to run all the non-blocking items first.  Then we spend the rest of this threads life
  1658.     // runing the synchronized objects one after another.
  1659.     if (g_fLogonCycle && !g_fCleanBoot)
  1660.     {
  1661.         _RunWelcome();
  1662.         RunRegAppsAndObjects(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCE, RRA_NOUI|RRA_RUNSUBKEYS|RRA_WAIT|RRA_DELETE);
  1663.         RunRegAppsAndObjects(HKEY_CURRENT_USER,  REGSTR_PATH_RUNONCE, RRA_NOUI|RRA_RUNSUBKEYS|RRA_WAIT|RRA_DELETE);
  1664.         RunRegAppsAndObjects(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN,     RRA_NOUI|RRA_RUNSUBKEYS|RRA_WAIT);
  1665.         RunRegAppsAndObjects(HKEY_CURRENT_USER,  REGSTR_PATH_RUN,     RRA_NOUI|RRA_RUNSUBKEYS|RRA_WAIT);
  1666.     }
  1667.     CoUninitialize();
  1668.     return TRUE;
  1669. }
  1670. void RunStartupApps()
  1671. {
  1672.     DWORD dwThreadID;
  1673.     HANDLE handle;
  1674.     handle = CreateThread( NULL, 0, RunStartupAppsThread, 0, 0, &dwThreadID );
  1675.     if ( handle )
  1676.     {
  1677.         CloseHandle(handle);
  1678.     }
  1679.     else
  1680.     {
  1681.         // we couldn't create the thread so just call the thread proc directly.
  1682.         // The RunStartupAppsThread function takes a long time to complete and
  1683.         // the UI thread will be non-responsive.  We call this from the tray.
  1684.         // REVIEW: Is this really safe?
  1685.         RunStartupAppsThread(0);
  1686.     }
  1687. }