1966 lines
60 KiB
C++
1966 lines
60 KiB
C++
|
#include "cabinet.h"
|
||
|
#include "rcids.h"
|
||
|
|
||
|
#include <regstr.h>
|
||
|
#include "startmnu.h"
|
||
|
#include <shdguid.h> // for IID_IShellService
|
||
|
#include <shlguid.h>
|
||
|
#include <desktray.h>
|
||
|
#include <wininet.h>
|
||
|
#include <trayp.h>
|
||
|
#include "tray.h"
|
||
|
#include "util.h"
|
||
|
#include "atlstuff.h"
|
||
|
|
||
|
#include <runonce.c> // shared runonce processing code
|
||
|
|
||
|
// global so that it is shared between TS sessions
|
||
|
#define SZ_SCMCREATEDEVENT_NT5 TEXT("Global\\ScmCreatedEvent")
|
||
|
#define SZ_WINDOWMETRICS TEXT("Control Panel\\Desktop\\WindowMetrics")
|
||
|
#define SZ_APPLIEDDPI TEXT("AppliedDPI")
|
||
|
#define SZ_CONTROLPANEL TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel")
|
||
|
#define SZ_ORIGINALDPI TEXT("OriginalDPI")
|
||
|
|
||
|
// exports from shdocvw.dll
|
||
|
STDAPI_(void) RunInstallUninstallStubs(void);
|
||
|
|
||
|
int ExplorerWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR pszCmdLine, int nCmdShow);
|
||
|
|
||
|
#ifdef FEATURE_STARTPAGE
|
||
|
#include <DUIThread.h>
|
||
|
void CreateStartPage(HINSTANCE hInstance, HANDLE hDesktop);
|
||
|
BOOL Tray_ShowStartPageEnabled();
|
||
|
#endif
|
||
|
BOOL _ShouldFixResolution(void);
|
||
|
|
||
|
#ifdef PERF_ENABLESETMARK
|
||
|
#include <wmistr.h>
|
||
|
#include <ntwmi.h> // PWMI_SET_MARK_INFORMATION is defined in ntwmi.h
|
||
|
#include <wmiumkm.h>
|
||
|
#define NTPERF
|
||
|
#include <ntperf.h>
|
||
|
|
||
|
void DoSetMark(LPCSTR pszMark, ULONG cbSz)
|
||
|
{
|
||
|
PWMI_SET_MARK_INFORMATION MarkInfo;
|
||
|
HANDLE hTemp;
|
||
|
ULONG cbBufferSize;
|
||
|
ULONG cbReturnSize;
|
||
|
|
||
|
cbBufferSize = FIELD_OFFSET(WMI_SET_MARK_INFORMATION, Mark) + cbSz;
|
||
|
|
||
|
MarkInfo = (PWMI_SET_MARK_INFORMATION) LocalAlloc(LPTR, cbBufferSize);
|
||
|
|
||
|
// Failed to init, no big deal
|
||
|
if (MarkInfo == NULL)
|
||
|
return;
|
||
|
|
||
|
BYTE *pMarkBuffer = (BYTE *) (&MarkInfo->Mark[0]);
|
||
|
|
||
|
memcpy(pMarkBuffer, pszMark, cbSz);
|
||
|
|
||
|
// WMI_SET_MARK_WITH_FLUSH will flush the working set when setting the mark
|
||
|
MarkInfo->Flag = PerformanceMmInfoMark;
|
||
|
|
||
|
hTemp = CreateFile(WMIDataDeviceName,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL |
|
||
|
FILE_FLAG_OVERLAPPED,
|
||
|
NULL);
|
||
|
|
||
|
if (hTemp != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
// here's the piece that actually puts the mark in the buffer
|
||
|
BOOL fIoctlSuccess = DeviceIoControl(hTemp,
|
||
|
IOCTL_WMI_SET_MARK,
|
||
|
MarkInfo,
|
||
|
cbBufferSize,
|
||
|
NULL,
|
||
|
0,
|
||
|
&cbReturnSize,
|
||
|
NULL);
|
||
|
|
||
|
CloseHandle(hTemp);
|
||
|
}
|
||
|
LocalFree(MarkInfo);
|
||
|
}
|
||
|
#endif // PERF_ENABLESETMARK
|
||
|
|
||
|
|
||
|
//Do not change this stock5.lib use this as a BOOL not a bit.
|
||
|
BOOL g_bMirroredOS = FALSE;
|
||
|
|
||
|
HINSTANCE hinstCabinet = 0;
|
||
|
|
||
|
CRITICAL_SECTION g_csDll = { 0 };
|
||
|
|
||
|
HKEY g_hkeyExplorer = NULL;
|
||
|
|
||
|
#define MAGIC_FAULT_TIME (1000 * 60 * 5)
|
||
|
#define MAGIC_FAULT_LIMIT (2)
|
||
|
BOOL g_fLogonCycle = FALSE;
|
||
|
BOOL g_fCleanShutdown = TRUE;
|
||
|
BOOL g_fExitExplorer = TRUE; // set to FALSE on WM_ENDSESSION shutdown case
|
||
|
BOOL g_fEndSession = FALSE; // set to TRUE if we rx a WM_ENDSESSION during RunOnce etc
|
||
|
BOOL g_fFakeShutdown = FALSE; // set to TRUE if we do Ctrl+Alt+Shift+Cancel shutdown
|
||
|
|
||
|
DWORD g_dwStopWatchMode; // to minimize impact of perf logging on retail
|
||
|
|
||
|
|
||
|
|
||
|
// helper function to check to see if a given regkey is has any subkeys
|
||
|
BOOL SHKeyHasSubkeys(HKEY hk, LPCTSTR pszSubKey)
|
||
|
{
|
||
|
HKEY hkSub;
|
||
|
BOOL bHasSubKeys = FALSE;
|
||
|
|
||
|
// need to open this with KEY_QUERY_VALUE or else RegQueryInfoKey will fail
|
||
|
if (RegOpenKeyEx(hk,
|
||
|
pszSubKey,
|
||
|
0,
|
||
|
(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE),
|
||
|
&hkSub) == ERROR_SUCCESS)
|
||
|
{
|
||
|
DWORD dwSubKeys;
|
||
|
|
||
|
if (RegQueryInfoKey(hkSub, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
|
||
|
{
|
||
|
bHasSubKeys = (dwSubKeys != 0);
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hkSub);
|
||
|
}
|
||
|
|
||
|
return bHasSubKeys;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef _WIN64
|
||
|
// helper function to check to see if a given regkey is has values (ignores the default value)
|
||
|
BOOL SHKeyHasValues(HKEY hk, LPCTSTR pszSubKey)
|
||
|
{
|
||
|
HKEY hkSub;
|
||
|
BOOL bHasValues = FALSE;
|
||
|
|
||
|
if (RegOpenKeyEx(hk,
|
||
|
pszSubKey,
|
||
|
0,
|
||
|
KEY_QUERY_VALUE,
|
||
|
&hkSub) == ERROR_SUCCESS)
|
||
|
{
|
||
|
DWORD dwValues;
|
||
|
DWORD dwSubKeys;
|
||
|
|
||
|
if (RegQueryInfoKey(hkSub, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, &dwValues, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
|
||
|
{
|
||
|
bHasValues = (dwValues != 0);
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hkSub);
|
||
|
}
|
||
|
|
||
|
return bHasValues;
|
||
|
}
|
||
|
#endif // _WIN64
|
||
|
|
||
|
|
||
|
void CreateShellDirectories()
|
||
|
{
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
|
||
|
// Create the shell directories if they don't exist
|
||
|
SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, TRUE);
|
||
|
SHGetSpecialFolderPath(NULL, szPath, CSIDL_PROGRAMS, TRUE);
|
||
|
SHGetSpecialFolderPath(NULL, szPath, CSIDL_STARTMENU, TRUE);
|
||
|
SHGetSpecialFolderPath(NULL, szPath, CSIDL_STARTUP, TRUE);
|
||
|
SHGetSpecialFolderPath(NULL, szPath, CSIDL_RECENT, TRUE);
|
||
|
SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE);
|
||
|
}
|
||
|
|
||
|
// returns:
|
||
|
// TRUE if the user wants to abort the startup sequence
|
||
|
// FALSE keep going
|
||
|
//
|
||
|
// note: this is a switch, once on it will return TRUE to all
|
||
|
// calls so these keys don't need to be pressed the whole time
|
||
|
BOOL AbortStartup()
|
||
|
{
|
||
|
static BOOL bAborted = FALSE; // static so it sticks!
|
||
|
|
||
|
if (bAborted)
|
||
|
{
|
||
|
return TRUE; // don't do funky startup stuff
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bAborted = (g_fCleanBoot || ((GetKeyState(VK_CONTROL) < 0) || (GetKeyState(VK_SHIFT) < 0)));
|
||
|
return bAborted;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL ExecStartupEnumProc(IShellFolder *psf, LPITEMIDLIST pidlItem)
|
||
|
{
|
||
|
IContextMenu *pcm;
|
||
|
HRESULT hr = psf->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlItem, IID_PPV_ARG_NULL(IContextMenu, &pcm));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
HMENU hmenu = CreatePopupMenu();
|
||
|
if (hmenu)
|
||
|
{
|
||
|
pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, CMF_DEFAULTONLY);
|
||
|
INT idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
|
||
|
if (idCmd)
|
||
|
{
|
||
|
CMINVOKECOMMANDINFOEX ici = {0};
|
||
|
|
||
|
ici.cbSize = sizeof(ici);
|
||
|
ici.fMask = CMIC_MASK_FLAG_NO_UI;
|
||
|
ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CONTEXTMENU_IDCMD_FIRST);
|
||
|
ici.nShow = SW_NORMAL;
|
||
|
|
||
|
if (FAILED(pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici)))
|
||
|
{
|
||
|
c_tray.LogFailedStartupApp();
|
||
|
}
|
||
|
}
|
||
|
DestroyMenu(hmenu);
|
||
|
}
|
||
|
pcm->Release();
|
||
|
}
|
||
|
|
||
|
return !AbortStartup();
|
||
|
}
|
||
|
|
||
|
typedef BOOL (*PFNENUMFOLDERCALLBACK)(IShellFolder *psf, LPITEMIDLIST pidlItem);
|
||
|
|
||
|
void EnumFolder(LPITEMIDLIST pidlFolder, DWORD grfFlags, PFNENUMFOLDERCALLBACK pfn)
|
||
|
{
|
||
|
IShellFolder *psf;
|
||
|
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlFolder, &psf))))
|
||
|
{
|
||
|
IEnumIDList *penum;
|
||
|
if (S_OK == psf->EnumObjects(NULL, grfFlags, &penum))
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
ULONG celt;
|
||
|
while (S_OK == penum->Next(1, &pidl, &celt))
|
||
|
{
|
||
|
BOOL bRet = pfn(psf, pidl);
|
||
|
|
||
|
SHFree(pidl);
|
||
|
|
||
|
if (!bRet)
|
||
|
break;
|
||
|
}
|
||
|
penum->Release();
|
||
|
}
|
||
|
psf->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const UINT c_rgStartupFolders[] = {
|
||
|
CSIDL_COMMON_STARTUP,
|
||
|
CSIDL_COMMON_ALTSTARTUP, // non-localized "Common StartUp" group if exists.
|
||
|
CSIDL_STARTUP,
|
||
|
CSIDL_ALTSTARTUP // non-localized "StartUp" group if exists.
|
||
|
};
|
||
|
|
||
|
void _ExecuteStartupPrograms()
|
||
|
{
|
||
|
if (!AbortStartup())
|
||
|
{
|
||
|
for (int i = 0; i < ARRAYSIZE(c_rgStartupFolders); i++)
|
||
|
{
|
||
|
LPITEMIDLIST pidlStartup = SHCloneSpecialIDList(NULL, c_rgStartupFolders[i], FALSE);
|
||
|
if (pidlStartup)
|
||
|
{
|
||
|
EnumFolder(pidlStartup, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, ExecStartupEnumProc);
|
||
|
ILFree(pidlStartup);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// helper function for parsing the run= stuff
|
||
|
BOOL ExecuteOldEqualsLine(LPTSTR pszCmdLine, int nCmdShow)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
BOOL bFinished = FALSE;
|
||
|
TCHAR szWindowsDir[MAX_PATH];
|
||
|
|
||
|
// Load and Run lines are done relative to windows directory.
|
||
|
GetWindowsDirectory(szWindowsDir, ARRAYSIZE(szWindowsDir));
|
||
|
|
||
|
while (!bFinished && !AbortStartup())
|
||
|
{
|
||
|
LPTSTR pEnd = pszCmdLine;
|
||
|
|
||
|
// NOTE: I am guessing from the code below that you can have multiple entries seperated
|
||
|
// by a ' ' or a ',' and we will exec all of them.
|
||
|
while ((*pEnd) && (*pEnd != TEXT(' ')) && (*pEnd != TEXT(',')))
|
||
|
{
|
||
|
pEnd = (LPTSTR)CharNext(pEnd);
|
||
|
}
|
||
|
|
||
|
if (*pEnd == 0)
|
||
|
{
|
||
|
bFinished = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pEnd = 0;
|
||
|
}
|
||
|
|
||
|
if (lstrlen(pszCmdLine) != 0)
|
||
|
{
|
||
|
SHELLEXECUTEINFO ei = {0};
|
||
|
|
||
|
ei.cbSize = sizeof(ei);
|
||
|
ei.lpFile = pszCmdLine;
|
||
|
ei.lpDirectory = szWindowsDir;
|
||
|
ei.nShow = nCmdShow;
|
||
|
|
||
|
if (!ShellExecuteEx(&ei))
|
||
|
{
|
||
|
ShellMessageBox(hinstCabinet,
|
||
|
NULL,
|
||
|
MAKEINTRESOURCE(IDS_WINININORUN),
|
||
|
MAKEINTRESOURCE(IDS_DESKTOP),
|
||
|
MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL,
|
||
|
pszCmdLine);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pszCmdLine = pEnd + 1;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
// we check for the old "load=" and "run=" from the [Windows] section of the win.ini, which
|
||
|
// is mapped nowadays to HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
|
||
|
BOOL _ProcessOldRunAndLoadEquals()
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
// don't do the run= section if we are in safemode
|
||
|
if (!g_fCleanBoot)
|
||
|
{
|
||
|
HKEY hk;
|
||
|
|
||
|
if (RegOpenKeyEx(HKEY_CURRENT_USER,
|
||
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"),
|
||
|
0,
|
||
|
KEY_QUERY_VALUE,
|
||
|
&hk) == ERROR_SUCCESS)
|
||
|
{
|
||
|
DWORD dwType;
|
||
|
DWORD cbData;
|
||
|
TCHAR szBuffer[255]; // max size of load= & run= lines...
|
||
|
|
||
|
// "Load" apps before "Run"ning any.
|
||
|
cbData = sizeof(szBuffer);
|
||
|
if ((SHGetValue(hk, NULL, TEXT("Load"), &dwType, (void*)szBuffer, &cbData) == ERROR_SUCCESS) &&
|
||
|
(dwType == REG_SZ))
|
||
|
{
|
||
|
// we want load= to be hidden, so SW_SHOWMINNOACTIVE is needed
|
||
|
if (ExecuteOldEqualsLine(szBuffer, SW_SHOWMINNOACTIVE))
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cbData = sizeof(szBuffer);
|
||
|
if ((SHGetValue(hk, NULL, TEXT("Run"), &dwType, (void*)szBuffer, &cbData) == ERROR_SUCCESS) &&
|
||
|
(dwType == REG_SZ))
|
||
|
{
|
||
|
if (ExecuteOldEqualsLine(szBuffer, SW_SHOWNORMAL))
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
// Use IERnonce.dll to process RunOnceEx key
|
||
|
//
|
||
|
typedef void (WINAPI *RUNONCEEXPROCESS)(HWND, HINSTANCE, LPSTR, int);
|
||
|
|
||
|
BOOL _ProcessRunOnceEx()
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
if (SHKeyHasSubkeys(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCEEX))
|
||
|
{
|
||
|
PROCESS_INFORMATION pi = {0};
|
||
|
TCHAR szArgString[MAX_PATH];
|
||
|
TCHAR szRunDll32[MAX_PATH];
|
||
|
BOOL fInTSInstallMode = FALSE;
|
||
|
|
||
|
// See if we are in "Applications Server" mode, if so we need to trigger install mode
|
||
|
if (IsOS(OS_TERMINALSERVER))
|
||
|
{
|
||
|
fInTSInstallMode = SHSetTermsrvAppInstallMode(TRUE);
|
||
|
}
|
||
|
|
||
|
// we used to call LoadLibrary("IERNONCE.DLL") and do all of the processing in-proc. Since
|
||
|
// ierunonce.dll in turn calls LoadLibrary on whatever is in the registry and those setup dll's
|
||
|
// can leak handles, we do this all out-of-proc now.
|
||
|
|
||
|
GetSystemDirectory(szArgString, ARRAYSIZE(szArgString));
|
||
|
PathAppend(szArgString, TEXT("iernonce.dll"));
|
||
|
PathQuoteSpaces(szArgString);
|
||
|
StrCatBuff(szArgString, TEXT(",RunOnceExProcess"), ARRAYSIZE(szArgString));
|
||
|
|
||
|
GetSystemDirectory(szRunDll32, ARRAYSIZE(szRunDll32));
|
||
|
PathAppend(szRunDll32, TEXT("rundll32.exe"));
|
||
|
|
||
|
if (CreateProcessWithArgs(szRunDll32, szArgString, NULL, &pi))
|
||
|
{
|
||
|
SHProcessMessagesUntilEvent(NULL, pi.hProcess, INFINITE);
|
||
|
|
||
|
CloseHandle(pi.hProcess);
|
||
|
CloseHandle(pi.hThread);
|
||
|
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
|
||
|
if (fInTSInstallMode)
|
||
|
{
|
||
|
SHSetTermsrvAppInstallMode(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef _WIN64
|
||
|
//
|
||
|
// check and see if we need to do 32-bit RunOnceEx processing for wow64
|
||
|
//
|
||
|
if (SHKeyHasSubkeys(HKEY_LOCAL_MACHINE, TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnceEx")))
|
||
|
{
|
||
|
TCHAR szWow64Path[MAX_PATH];
|
||
|
|
||
|
if (ExpandEnvironmentStrings(TEXT("%SystemRoot%\\SysWOW64"), szWow64Path, ARRAYSIZE(szWow64Path)))
|
||
|
{
|
||
|
TCHAR sz32BitRunOnce[MAX_PATH];
|
||
|
PROCESS_INFORMATION pi = {0};
|
||
|
|
||
|
wnsprintf(sz32BitRunOnce, ARRAYSIZE(sz32BitRunOnce), TEXT("%s\\runonce.exe"), szWow64Path);
|
||
|
|
||
|
if (CreateProcessWithArgs(sz32BitRunOnce, TEXT("/RunOnceEx6432"), szWow64Path, &pi))
|
||
|
{
|
||
|
// have to wait for the ruonceex processing before we can return
|
||
|
SHProcessMessagesUntilEvent(NULL, pi.hProcess, INFINITE);
|
||
|
CloseHandle(pi.hProcess);
|
||
|
CloseHandle(pi.hThread);
|
||
|
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // _WIN64
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL _ProcessRunOnce()
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
if (!SHRestricted(REST_NOLOCALMACHINERUNONCE))
|
||
|
{
|
||
|
bRet = Cabinet_EnumRegApps(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUNONCE, RRA_DELETE | RRA_WAIT, ExecuteRegAppEnumProc, 0);
|
||
|
|
||
|
#ifdef _WIN64
|
||
|
//
|
||
|
// check and see if we need to do 32-bit RunOnce processing for wow64
|
||
|
//
|
||
|
// NOTE: we do not support per-user (HKCU) 6432 runonce
|
||
|
//
|
||
|
if (SHKeyHasValues(HKEY_LOCAL_MACHINE, TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")))
|
||
|
{
|
||
|
TCHAR szWow64Path[MAX_PATH];
|
||
|
|
||
|
if (ExpandEnvironmentStrings(TEXT("%SystemRoot%\\SysWOW64"), szWow64Path, ARRAYSIZE(szWow64Path)))
|
||
|
{
|
||
|
TCHAR sz32BitRunOnce[MAX_PATH];
|
||
|
PROCESS_INFORMATION pi = {0};
|
||
|
|
||
|
wnsprintf(sz32BitRunOnce, ARRAYSIZE(sz32BitRunOnce), TEXT("%s\\runonce.exe"), szWow64Path);
|
||
|
|
||
|
// NOTE: since the 32-bit and 64-bit registries are different, we don't wait since it should not affect us
|
||
|
if (CreateProcessWithArgs(sz32BitRunOnce, TEXT("/RunOnce6432"), szWow64Path, &pi))
|
||
|
{
|
||
|
CloseHandle(pi.hProcess);
|
||
|
CloseHandle(pi.hThread);
|
||
|
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // _WIN64
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define REGTIPS REGSTR_PATH_EXPLORER TEXT("\\Tips")
|
||
|
#define SZ_REGKEY_SRVWIZ_ROOT TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\srvWiz")
|
||
|
#define SZ_REGVAL_SRVWIZ_RUN_ALWAYS TEXT("CYSMustRun")
|
||
|
|
||
|
// Srvwiz is the Configure Your Server Wizard that runs on srv and ads skus
|
||
|
|
||
|
bool _ShouldStartCys()
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// Only run on srv or ads sku
|
||
|
if (!IsOS(OS_SERVER) && !IsOS(OS_ADVSERVER))
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!IsUserAnAdmin())
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
DWORD dwType = 0;
|
||
|
DWORD dwData = 0;
|
||
|
DWORD cbSize = sizeof(dwData);
|
||
|
|
||
|
// if the must-run value is present and non-zero, then we need to
|
||
|
// start the wizard.
|
||
|
|
||
|
if (
|
||
|
SHGetValue(
|
||
|
HKEY_LOCAL_MACHINE,
|
||
|
SZ_REGKEY_SRVWIZ_ROOT,
|
||
|
SZ_REGVAL_SRVWIZ_RUN_ALWAYS,
|
||
|
&dwType,
|
||
|
reinterpret_cast<BYTE*>(&dwData),
|
||
|
&cbSize) == ERROR_SUCCESS)
|
||
|
{
|
||
|
if (dwData)
|
||
|
{
|
||
|
result = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the user's preference is present and zero, then don't show
|
||
|
// the wizard, else continue with other tests
|
||
|
|
||
|
if (
|
||
|
!SHGetValue(
|
||
|
HKEY_CURRENT_USER,
|
||
|
REGTIPS,
|
||
|
TEXT("Show"),
|
||
|
NULL,
|
||
|
reinterpret_cast<BYTE*>(&dwData),
|
||
|
&cbSize))
|
||
|
{
|
||
|
if (!dwData)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the user's preference is absent or non-zero, then we need to
|
||
|
// start the wizard.
|
||
|
|
||
|
dwData = 0;
|
||
|
|
||
|
if (
|
||
|
SHGetValue(
|
||
|
HKEY_CURRENT_USER,
|
||
|
SZ_REGKEY_SRVWIZ_ROOT,
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
reinterpret_cast<BYTE*>(&dwData),
|
||
|
&cbSize) != ERROR_SUCCESS)
|
||
|
{
|
||
|
result = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (dwData)
|
||
|
{
|
||
|
result = true;
|
||
|
}
|
||
|
}
|
||
|
while (0);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void _RunWelcome()
|
||
|
{
|
||
|
if (_ShouldStartCys())
|
||
|
{
|
||
|
// NTRAID #94718: The SHGetValue above should be replaced with an SHRestricted call. The above is a highly non-standard
|
||
|
// place for this "policy" to live plus it doesn't allow for per machine and per user settings.
|
||
|
|
||
|
TCHAR szCmdLine[MAX_PATH];
|
||
|
PROCESS_INFORMATION pi;
|
||
|
|
||
|
// launch Configure Your Server for system administrators on Win2000 Server and Advanced Server
|
||
|
GetSystemDirectory(szCmdLine, ARRAYSIZE(szCmdLine));
|
||
|
PathAppend(szCmdLine, TEXT("cys.exe"));
|
||
|
|
||
|
if (CreateProcessWithArgs(szCmdLine, TEXT("/explorer"), NULL, &pi))
|
||
|
{
|
||
|
// OLE created a secret window for us, so we can't use
|
||
|
// WaitForSingleObject or we will deadlock
|
||
|
SHWaitForSendMessageThread(pi.hProcess, INFINITE);
|
||
|
CloseHandle(pi.hProcess);
|
||
|
CloseHandle(pi.hThread);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Once that's all done, see if the Start Menu needs to auto-open.
|
||
|
// Don't auto-open if we are going to offer to fix the user's screen
|
||
|
// resolution, though, because that causes us to cover up the screen
|
||
|
// resolution fix wizard! The screen resolution fix wizard will post
|
||
|
// this message when the user has finished fixing the screen.
|
||
|
//
|
||
|
// Also, don't auto-open if we are on Tablet. Tablet's OOBE will post
|
||
|
// us the Welcome Finished message when it's ready.
|
||
|
//
|
||
|
if (!_ShouldFixResolution() &&
|
||
|
!IsOS(OS_TABLETPC))
|
||
|
{
|
||
|
PostMessage(v_hwndTray, RegisterWindowMessage(TEXT("Welcome Finished")), 0, 0);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// On NT, run the TASKMAN= line from the registry
|
||
|
void _AutoRunTaskMan(void)
|
||
|
{
|
||
|
HKEY hkeyWinLogon;
|
||
|
|
||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
|
||
|
0, KEY_READ, &hkeyWinLogon) == ERROR_SUCCESS)
|
||
|
{
|
||
|
TCHAR szBuffer[MAX_PATH];
|
||
|
DWORD cbBuffer = sizeof(szBuffer);
|
||
|
if (RegQueryValueEx(hkeyWinLogon, TEXT("Taskman"), 0, NULL, (LPBYTE)szBuffer, &cbBuffer) == ERROR_SUCCESS)
|
||
|
{
|
||
|
if (szBuffer[0])
|
||
|
{
|
||
|
PROCESS_INFORMATION pi;
|
||
|
STARTUPINFO startup = {0};
|
||
|
startup.cb = sizeof(startup);
|
||
|
startup.wShowWindow = SW_SHOWNORMAL;
|
||
|
|
||
|
if (CreateProcess(NULL, szBuffer, NULL, NULL, FALSE, 0,
|
||
|
NULL, NULL, &startup, &pi))
|
||
|
{
|
||
|
CloseHandle(pi.hProcess);
|
||
|
CloseHandle(pi.hThread);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
RegCloseKey(hkeyWinLogon);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// try to create this by sending a wm_command directly to
|
||
|
// the desktop.
|
||
|
BOOL MyCreateFromDesktop(HINSTANCE hInst, LPCTSTR pszCmdLine, int nCmdShow)
|
||
|
{
|
||
|
NEWFOLDERINFO fi = {0};
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
fi.nShow = nCmdShow;
|
||
|
|
||
|
// since we have browseui fill out the fi,
|
||
|
// SHExplorerParseCmdLine() does a GetCommandLine()
|
||
|
if (SHExplorerParseCmdLine(&fi))
|
||
|
bRet = SHCreateFromDesktop(&fi);
|
||
|
|
||
|
// should we also have it cleanup after itself??
|
||
|
|
||
|
// SHExplorerParseCmdLine() can allocate this buffer...
|
||
|
if (fi.uFlags & COF_PARSEPATH)
|
||
|
LocalFree(fi.pszPath);
|
||
|
|
||
|
ILFree(fi.pidl);
|
||
|
ILFree(fi.pidlRoot);
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL g_fDragFullWindows=FALSE;
|
||
|
int g_cxEdge=0;
|
||
|
int g_cyEdge=0;
|
||
|
int g_cySize=0;
|
||
|
int g_cxTabSpace=0;
|
||
|
int g_cyTabSpace=0;
|
||
|
int g_cxBorder=0;
|
||
|
int g_cyBorder=0;
|
||
|
int g_cxPrimaryDisplay=0;
|
||
|
int g_cyPrimaryDisplay=0;
|
||
|
int g_cxDlgFrame=0;
|
||
|
int g_cyDlgFrame=0;
|
||
|
int g_cxFrame=0;
|
||
|
int g_cyFrame=0;
|
||
|
|
||
|
int g_cxMinimized=0;
|
||
|
int g_fCleanBoot=0;
|
||
|
int g_cxVScroll=0;
|
||
|
int g_cyHScroll=0;
|
||
|
UINT g_uDoubleClick=0;
|
||
|
|
||
|
void Cabinet_InitGlobalMetrics(WPARAM wParam, LPTSTR lpszSection)
|
||
|
{
|
||
|
BOOL fForce = (!lpszSection || !*lpszSection);
|
||
|
|
||
|
if (fForce || wParam == SPI_SETDRAGFULLWINDOWS)
|
||
|
{
|
||
|
SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &g_fDragFullWindows, 0);
|
||
|
}
|
||
|
|
||
|
if (fForce || !lstrcmpi(lpszSection, TEXT("WindowMetrics")) ||
|
||
|
wParam == SPI_SETNONCLIENTMETRICS)
|
||
|
{
|
||
|
g_cxEdge = GetSystemMetrics(SM_CXEDGE);
|
||
|
g_cyEdge = GetSystemMetrics(SM_CYEDGE);
|
||
|
g_cxTabSpace = (g_cxEdge * 3) / 2;
|
||
|
g_cyTabSpace = (g_cyEdge * 3) / 2; // cause the graphic designers really really want 3.
|
||
|
g_cySize = GetSystemMetrics(SM_CYSIZE);
|
||
|
g_cxBorder = GetSystemMetrics(SM_CXBORDER);
|
||
|
g_cyBorder = GetSystemMetrics(SM_CYBORDER);
|
||
|
g_cxVScroll = GetSystemMetrics(SM_CXVSCROLL);
|
||
|
g_cyHScroll = GetSystemMetrics(SM_CYHSCROLL);
|
||
|
g_cxDlgFrame = GetSystemMetrics(SM_CXDLGFRAME);
|
||
|
g_cyDlgFrame = GetSystemMetrics(SM_CYDLGFRAME);
|
||
|
g_cxFrame = GetSystemMetrics(SM_CXFRAME);
|
||
|
g_cyFrame = GetSystemMetrics(SM_CYFRAME);
|
||
|
g_cxMinimized = GetSystemMetrics(SM_CXMINIMIZED);
|
||
|
g_cxPrimaryDisplay = GetSystemMetrics(SM_CXSCREEN);
|
||
|
g_cyPrimaryDisplay = GetSystemMetrics(SM_CYSCREEN);
|
||
|
}
|
||
|
|
||
|
if (fForce || wParam == SPI_SETDOUBLECLICKTIME)
|
||
|
{
|
||
|
g_uDoubleClick = GetDoubleClickTime();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
|
||
|
void _CreateAppGlobals()
|
||
|
{
|
||
|
g_fCleanBoot = GetSystemMetrics(SM_CLEANBOOT); // also known as "Safe Mode"
|
||
|
|
||
|
Cabinet_InitGlobalMetrics(0, NULL);
|
||
|
|
||
|
//
|
||
|
// Check if the mirroring APIs exist on the current
|
||
|
// platform.
|
||
|
//
|
||
|
g_bMirroredOS = IS_MIRRORING_ENABLED();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This function checks if any of the shell windows is already created by
|
||
|
// another instance of explorer and returns TRUE if so.
|
||
|
//
|
||
|
|
||
|
BOOL IsAnyShellWindowAlreadyPresent()
|
||
|
{
|
||
|
return GetShellWindow() || FindWindow(TEXT("Proxy Desktop"), NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
// See if the Shell= line indicates that we are the shell
|
||
|
|
||
|
BOOL ExplorerIsShell()
|
||
|
{
|
||
|
TCHAR *pszPathName, szPath[MAX_PATH];
|
||
|
TCHAR *pszModuleName, szModulePath[MAX_PATH];
|
||
|
|
||
|
ASSERT(!IsAnyShellWindowAlreadyPresent());
|
||
|
|
||
|
GetModuleFileName(NULL, szModulePath, ARRAYSIZE(szModulePath));
|
||
|
pszModuleName = PathFindFileName(szModulePath);
|
||
|
|
||
|
GetPrivateProfileString(TEXT("boot"), TEXT("shell"), pszModuleName, szPath, ARRAYSIZE(szPath), TEXT("system.ini"));
|
||
|
|
||
|
PathRemoveArgs(szPath);
|
||
|
PathRemoveBlanks(szPath);
|
||
|
pszPathName = PathFindFileName(szPath);
|
||
|
|
||
|
// NB Special case shell=install.exe - assume we are the shell.
|
||
|
// Symantec un-installers temporarily set shell=installer.exe so
|
||
|
// we think we're not the shell when we are. They fail to clean up
|
||
|
// a bunch of links if we don't do this.
|
||
|
|
||
|
return StrCmpNI(pszPathName, pszModuleName, lstrlen(pszModuleName)) == 0 ||
|
||
|
lstrcmpi(pszPathName, TEXT("install.exe")) == 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Returns TRUE of this is the first time the explorer is run
|
||
|
|
||
|
BOOL ShouldStartDesktopAndTray()
|
||
|
{
|
||
|
// We need to be careful on which window we look for. If we look for
|
||
|
// our desktop window class and Progman is running we will find the
|
||
|
// progman window. So Instead we should ask user for the shell window.
|
||
|
|
||
|
// We can not depend on any values being set here as this is the
|
||
|
// start of a new process. This wont be called when we start new
|
||
|
// threads.
|
||
|
return !IsAnyShellWindowAlreadyPresent() && ExplorerIsShell();
|
||
|
}
|
||
|
|
||
|
void DisplayCleanBootMsg()
|
||
|
{
|
||
|
// On server sku's or anytime on ia64, just show a message with
|
||
|
// an OK button for safe boot
|
||
|
UINT uiMessageBoxFlags = MB_ICONEXCLAMATION | MB_SYSTEMMODAL | MB_OK;
|
||
|
UINT uiMessage = IDS_CLEANBOOTMSG;
|
||
|
|
||
|
#ifndef _WIN64
|
||
|
if (!IsOS(OS_ANYSERVER))
|
||
|
{
|
||
|
// On x86 per and pro, also offer an option to start system restore
|
||
|
uiMessageBoxFlags = MB_ICONEXCLAMATION | MB_SYSTEMMODAL | MB_YESNO;
|
||
|
uiMessage = IDS_CLEANBOOTMSGRESTORE;
|
||
|
}
|
||
|
#endif // !_WIN64
|
||
|
|
||
|
WCHAR szTitle[80];
|
||
|
WCHAR szMessage[1024];
|
||
|
|
||
|
LoadString(hinstCabinet, IDS_DESKTOP, szTitle, ARRAYSIZE(szTitle));
|
||
|
LoadString(hinstCabinet, uiMessage, szMessage, ARRAYSIZE(szMessage));
|
||
|
|
||
|
// on IA64 the msgbox will always return IDOK, so this "if" will always fail.
|
||
|
if (IDNO == MessageBox(NULL, szMessage, szTitle, uiMessageBoxFlags))
|
||
|
{
|
||
|
TCHAR szPath[MAX_PATH];
|
||
|
ExpandEnvironmentStrings(TEXT("%SystemRoot%\\system32\\restore\\rstrui.exe"), szPath, ARRAYSIZE(szPath));
|
||
|
PROCESS_INFORMATION pi;
|
||
|
STARTUPINFO si = {0};
|
||
|
if (CreateProcess(szPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
||
|
{
|
||
|
CloseHandle(pi.hThread);
|
||
|
CloseHandle(pi.hProcess);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL IsExecCmd(LPCTSTR pszCmd)
|
||
|
{
|
||
|
return *pszCmd && !StrStrI(pszCmd, TEXT("-embedding"));
|
||
|
}
|
||
|
|
||
|
// run the cmd line passed up from win.com
|
||
|
|
||
|
void _RunWinComCmdLine(LPCTSTR pszCmdLine, UINT nCmdShow)
|
||
|
{
|
||
|
if (IsExecCmd(pszCmdLine))
|
||
|
{
|
||
|
SHELLEXECUTEINFO ei = { sizeof(ei), 0, NULL, NULL, pszCmdLine, NULL, NULL, nCmdShow};
|
||
|
|
||
|
ei.lpParameters = PathGetArgs(pszCmdLine);
|
||
|
if (*ei.lpParameters)
|
||
|
*((LPTSTR)ei.lpParameters - 1) = 0; // const -> non const
|
||
|
|
||
|
ShellExecuteEx(&ei);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// stolen from the CRT, used to shirink our code
|
||
|
LPTSTR _SkipCmdLineCrap(LPTSTR pszCmdLine)
|
||
|
{
|
||
|
if (*pszCmdLine == TEXT('\"'))
|
||
|
{
|
||
|
//
|
||
|
// Scan, and skip over, subsequent characters until
|
||
|
// another double-quote or a null is encountered.
|
||
|
//
|
||
|
while (*++pszCmdLine && (*pszCmdLine != TEXT('\"')))
|
||
|
;
|
||
|
|
||
|
//
|
||
|
// If we stopped on a double-quote (usual case), skip
|
||
|
// over it.
|
||
|
//
|
||
|
if (*pszCmdLine == TEXT('\"'))
|
||
|
pszCmdLine++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
while (*pszCmdLine > TEXT(' '))
|
||
|
pszCmdLine++;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Skip past any white space preceeding the second token.
|
||
|
//
|
||
|
while (*pszCmdLine && (*pszCmdLine <= TEXT(' ')))
|
||
|
pszCmdLine++;
|
||
|
|
||
|
return pszCmdLine;
|
||
|
}
|
||
|
|
||
|
STDAPI_(int) ModuleEntry()
|
||
|
{
|
||
|
PERFSETMARK("ExplorerStartup");
|
||
|
|
||
|
DoInitialization();
|
||
|
|
||
|
// We don't want the "No disk in drive X:" requesters, so we set
|
||
|
// the critical error mask such that calls will just silently fail
|
||
|
|
||
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||
|
|
||
|
LPTSTR pszCmdLine = GetCommandLine();
|
||
|
pszCmdLine = _SkipCmdLineCrap(pszCmdLine);
|
||
|
|
||
|
STARTUPINFO si = {0};
|
||
|
si.cb = sizeof(si);
|
||
|
GetStartupInfo(&si);
|
||
|
|
||
|
int nCmdShow = si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT;
|
||
|
int iRet = ExplorerWinMain(GetModuleHandle(NULL), NULL, pszCmdLine, nCmdShow);
|
||
|
|
||
|
DoCleanup();
|
||
|
|
||
|
// Since we now have a way for an extension to tell us when it is finished,
|
||
|
// we will terminate all processes when the main thread goes away.
|
||
|
|
||
|
if (g_fExitExplorer) // desktop told us not to exit
|
||
|
ExitProcess(iRet);
|
||
|
|
||
|
return iRet;
|
||
|
}
|
||
|
|
||
|
HANDLE CreateDesktopAndTray()
|
||
|
{
|
||
|
HANDLE hDesktop = NULL;
|
||
|
|
||
|
if (g_dwProfileCAP & 0x00008000)
|
||
|
StartCAPAll();
|
||
|
|
||
|
if (v_hwndTray || c_tray.Init())
|
||
|
{
|
||
|
ASSERT(v_hwndTray);
|
||
|
|
||
|
if (!v_hwndDesktop)
|
||
|
{
|
||
|
// cache the handle to the desktop...
|
||
|
hDesktop = SHCreateDesktop(c_tray.GetDeskTray());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (g_dwProfileCAP & 0x80000000)
|
||
|
StopCAPAll();
|
||
|
|
||
|
return hDesktop;
|
||
|
}
|
||
|
|
||
|
// Removes the session key from the registry.
|
||
|
void NukeSessionKey(void)
|
||
|
{
|
||
|
HKEY hkDummy;
|
||
|
SHCreateSessionKey(0xFFFFFFFF, &hkDummy);
|
||
|
}
|
||
|
|
||
|
BOOL IsFirstInstanceAfterLogon()
|
||
|
{
|
||
|
BOOL fResult = FALSE;
|
||
|
|
||
|
HKEY hkSession;
|
||
|
HRESULT hr = SHCreateSessionKey(KEY_WRITE, &hkSession);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
HKEY hkStartup;
|
||
|
DWORD dwDisposition;
|
||
|
LONG lRes;
|
||
|
lRes = RegCreateKeyEx(hkSession, TEXT("StartupHasBeenRun"), 0,
|
||
|
NULL,
|
||
|
REG_OPTION_VOLATILE,
|
||
|
KEY_WRITE,
|
||
|
NULL,
|
||
|
&hkStartup,
|
||
|
&dwDisposition);
|
||
|
if (lRes == ERROR_SUCCESS)
|
||
|
{
|
||
|
RegCloseKey(hkStartup);
|
||
|
if (dwDisposition == REG_CREATED_NEW_KEY)
|
||
|
fResult = TRUE;
|
||
|
}
|
||
|
RegCloseKey(hkSession);
|
||
|
}
|
||
|
return fResult;
|
||
|
}
|
||
|
|
||
|
DWORD ReadFaultCount()
|
||
|
{
|
||
|
DWORD dwValue = 0;
|
||
|
DWORD dwSize = sizeof(dwValue);
|
||
|
|
||
|
RegQueryValueEx(g_hkeyExplorer, TEXT("FaultCount"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
|
||
|
return dwValue;
|
||
|
}
|
||
|
|
||
|
void WriteFaultCount(DWORD dwValue)
|
||
|
{
|
||
|
RegSetValueEx(g_hkeyExplorer, TEXT("FaultCount"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
|
||
|
// If we are clearing the fault count or this is the first fault, clear or set the fault time.
|
||
|
if (!dwValue || (dwValue == 1))
|
||
|
{
|
||
|
if (dwValue)
|
||
|
dwValue = GetTickCount();
|
||
|
RegSetValueEx(g_hkeyExplorer, TEXT("FaultTime"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This function assumes it is only called when a fault has occured previously...
|
||
|
BOOL ShouldDisplaySafeMode()
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
SHELLSTATE ss;
|
||
|
|
||
|
SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE);
|
||
|
|
||
|
if (ss.fDesktopHTML)
|
||
|
{
|
||
|
if (ReadFaultCount() >= MAGIC_FAULT_LIMIT)
|
||
|
{
|
||
|
DWORD dwValue = 0;
|
||
|
DWORD dwSize = sizeof(dwValue);
|
||
|
|
||
|
RegQueryValueEx(g_hkeyExplorer, TEXT("FaultTime"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
|
||
|
fRet = ((GetTickCount() - dwValue) < MAGIC_FAULT_TIME);
|
||
|
// We had enough faults but they weren't over a sufficiently short period of time. Reset the fault
|
||
|
// count to 1 so that we start counting from this fault now.
|
||
|
if (!fRet)
|
||
|
WriteFaultCount(1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We don't care about faults that occured if AD is off.
|
||
|
WriteFaultCount(0);
|
||
|
}
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// dwValue is FALSE if this is startup, TRUE if this is shutdown,
|
||
|
//
|
||
|
void WriteCleanShutdown(DWORD dwValue)
|
||
|
{
|
||
|
RegSetValueEx(g_hkeyExplorer, TEXT("CleanShutdown"), 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
|
||
|
|
||
|
// If we are shutting down for real (i.e., not fake), then clean up the
|
||
|
// session key so we don't leak a bazillion volatile keys into the
|
||
|
// registry on a TS system when people log on and off and on and off...
|
||
|
if (dwValue && !g_fFakeShutdown)
|
||
|
{
|
||
|
NukeSessionKey();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL ReadCleanShutdown()
|
||
|
{
|
||
|
DWORD dwValue = 1; // default: it was clean
|
||
|
DWORD dwSize = sizeof(dwValue);
|
||
|
|
||
|
RegQueryValueEx(g_hkeyExplorer, TEXT("CleanShutdown"), NULL, NULL, (LPBYTE)&dwValue, &dwSize);
|
||
|
return (BOOL)dwValue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Synopsis: Waits for the OLE SCM process to finish its initialization.
|
||
|
// This is called before the first call to OleInitialize since
|
||
|
// the SHELL runs early in the boot process.
|
||
|
//
|
||
|
// Arguments: None.
|
||
|
//
|
||
|
// Returns: S_OK - SCM is running. OK to call OleInitialize.
|
||
|
// CO_E_INIT_SCM_EXEC_FAILURE - timed out waiting for SCM
|
||
|
// other - create event failed
|
||
|
//
|
||
|
// History: 26-Oct-95 Rickhi Extracted from CheckAndStartSCM so
|
||
|
// that only the SHELL need call it.
|
||
|
//
|
||
|
HRESULT WaitForSCMToInitialize()
|
||
|
{
|
||
|
static BOOL s_fScmStarted = FALSE;
|
||
|
|
||
|
if (s_fScmStarted)
|
||
|
return S_OK;
|
||
|
|
||
|
SECURITY_ATTRIBUTES *psa = CreateAllAccessSecurityAttributes(NULL, NULL, NULL);
|
||
|
|
||
|
// on NT5 we need a global event that is shared between TS sessions
|
||
|
HANDLE hEvent = CreateEvent(psa, TRUE, FALSE, SZ_SCMCREATEDEVENT_NT5);
|
||
|
|
||
|
if (!hEvent && GetLastError() == ERROR_ACCESS_DENIED)
|
||
|
{
|
||
|
//
|
||
|
// Win2K OLE32 has tightened security such that if this object
|
||
|
// already exists, we aren't allowed to open it with EVENT_ALL_ACCESS
|
||
|
// (CreateEvent fails with ERROR_ACCESS_DENIED in this case).
|
||
|
// Fall back by calling OpenEvent requesting SYNCHRONIZE access.
|
||
|
//
|
||
|
hEvent = OpenEvent(SYNCHRONIZE, FALSE, SZ_SCMCREATEDEVENT_NT5);
|
||
|
}
|
||
|
|
||
|
if (hEvent)
|
||
|
{
|
||
|
// wait for the SCM to signal the event, then close the handle
|
||
|
// and return a code based on the WaitEvent result.
|
||
|
int rc = WaitForSingleObject(hEvent, 60000);
|
||
|
|
||
|
CloseHandle(hEvent);
|
||
|
|
||
|
if (rc == WAIT_OBJECT_0)
|
||
|
{
|
||
|
s_fScmStarted = TRUE;
|
||
|
return S_OK;
|
||
|
}
|
||
|
else if (rc == WAIT_TIMEOUT)
|
||
|
{
|
||
|
return CO_E_INIT_SCM_EXEC_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
return HRESULT_FROM_WIN32(GetLastError()); // event creation failed or WFSO failed.
|
||
|
}
|
||
|
|
||
|
STDAPI OleInitializeWaitForSCM()
|
||
|
{
|
||
|
HRESULT hr = WaitForSCMToInitialize();
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SHCoInitialize(); // make sure we get no OLE1 DDE crap
|
||
|
OleInitialize(NULL);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// we need to figure out the fFirstShellBoot on a per-user
|
||
|
// basis rather than once per machine. We want the welcome
|
||
|
// splash screen to come up for every new user.
|
||
|
|
||
|
BOOL IsFirstShellBoot()
|
||
|
{
|
||
|
DWORD dwDisp;
|
||
|
HKEY hkey;
|
||
|
BOOL fFirstShellBoot = TRUE; // default value
|
||
|
|
||
|
if (RegCreateKeyEx(HKEY_CURRENT_USER, REGTIPS, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
|
||
|
NULL, &hkey, &dwDisp) == ERROR_SUCCESS)
|
||
|
{
|
||
|
DWORD dwSize = sizeof(fFirstShellBoot);
|
||
|
|
||
|
RegQueryValueEx(hkey, TEXT("DisplayInitialTipWindow"), NULL, NULL, (LPBYTE)&fFirstShellBoot, &dwSize);
|
||
|
|
||
|
if (fFirstShellBoot)
|
||
|
{
|
||
|
// Turn off the initial tip window for future shell starts.
|
||
|
BOOL bTemp = FALSE;
|
||
|
RegSetValueEx(hkey, TEXT("DisplayInitialTipWindow"), 0, REG_DWORD, (LPBYTE) &bTemp, sizeof(bTemp));
|
||
|
}
|
||
|
RegCloseKey(hkey);
|
||
|
}
|
||
|
return fFirstShellBoot;
|
||
|
}
|
||
|
|
||
|
// the following locale fixes (for NT5 378948) are dependent on desk.cpl changes
|
||
|
// Since Millennium does not ship updated desk.cpl, we don't want to do this on Millennium
|
||
|
//
|
||
|
// Given the Locale ID, this returns the corresponding charset
|
||
|
//
|
||
|
UINT GetCharsetFromLCID(LCID lcid)
|
||
|
{
|
||
|
TCHAR szData[6+1]; // 6 chars are max allowed for this lctype
|
||
|
UINT uiRet;
|
||
|
if (GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, szData, ARRAYSIZE(szData)) > 0)
|
||
|
{
|
||
|
UINT uiCp = (UINT)StrToInt(szData);
|
||
|
CHARSETINFO csinfo;
|
||
|
|
||
|
TranslateCharsetInfo(IntToPtr_(DWORD *, uiCp), &csinfo, TCI_SRCCODEPAGE);
|
||
|
uiRet = csinfo.ciCharset;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// at worst non penalty for charset
|
||
|
uiRet = DEFAULT_CHARSET;
|
||
|
}
|
||
|
|
||
|
return uiRet;
|
||
|
}
|
||
|
|
||
|
// In case of system locale change, the only way to update UI fonts is opening
|
||
|
// Desktop->Properties->Appearance.
|
||
|
// If the end user never open it the UI fonts are never changed.
|
||
|
// So compare the charset from system locale with the UI fonts charset then
|
||
|
// call desk.cpl if those are different.
|
||
|
|
||
|
#define MAX_CHARSETS 4
|
||
|
typedef HRESULT (STDAPICALLTYPE *LPUPDATECHARSETCHANGES)();
|
||
|
|
||
|
void CheckDefaultUIFonts()
|
||
|
{
|
||
|
UINT uiCharsets[MAX_CHARSETS];
|
||
|
DWORD dwSize = sizeof(UINT) * MAX_CHARSETS;
|
||
|
DWORD dwError;
|
||
|
|
||
|
dwError = SHGetValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Appearance"), TEXT("RecentFourCharsets"), NULL, (void *)uiCharsets, &dwSize);
|
||
|
|
||
|
if (dwError != ERROR_SUCCESS || uiCharsets[0] != GetCharsetFromLCID(GetSystemDefaultLCID()))
|
||
|
{
|
||
|
HINSTANCE hInst;
|
||
|
LPUPDATECHARSETCHANGES pfnUpdateCharsetChanges;
|
||
|
|
||
|
if (hInst = LoadLibrary(TEXT("desk.cpl")))
|
||
|
{
|
||
|
// Call desk.cpl to change the UI fonts in case of
|
||
|
// system locale change.
|
||
|
if (pfnUpdateCharsetChanges = (LPUPDATECHARSETCHANGES)(GetProcAddress(hInst, "UpdateCharsetChanges")))
|
||
|
{
|
||
|
(*pfnUpdateCharsetChanges)();
|
||
|
}
|
||
|
FreeLibrary(hInst);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This function calls an desk.cpl function to update the UI fonts to use the new DPI value.
|
||
|
// UpdateUIfonts() in desk.cpl checks to see if the DPI value has changed. If not, it returns
|
||
|
// immediately; If the dpi value has changed, it changes the size of all the UI fonts to reflect
|
||
|
// the dpi change and then returns.
|
||
|
//
|
||
|
typedef HRESULT (WINAPI *LPUPDATEUIFONTS)(int, int);
|
||
|
void ChangeUIfontsToNewDPI()
|
||
|
{
|
||
|
int iNewDPI, iOldDPI;
|
||
|
|
||
|
//Get the current system DPI.
|
||
|
HDC hdc = GetDC(NULL);
|
||
|
iNewDPI = GetDeviceCaps(hdc, LOGPIXELSY);
|
||
|
ReleaseDC(NULL, hdc);
|
||
|
|
||
|
DWORD dwSize = sizeof(iOldDPI);
|
||
|
//Get the last saved DPI value for the current user.
|
||
|
if (SHGetValue(HKEY_CURRENT_USER, SZ_WINDOWMETRICS, SZ_APPLIEDDPI, NULL, (void *)&iOldDPI, &dwSize) != ERROR_SUCCESS)
|
||
|
{
|
||
|
//"AppliedDPI" for the current user is missing.
|
||
|
// Now, see if the "OriginalDPI" value exists under HKLM
|
||
|
dwSize = sizeof(iOldDPI);
|
||
|
if (SHGetValue(HKEY_LOCAL_MACHINE, SZ_CONTROLPANEL, SZ_ORIGINALDPI, NULL, (void *)&iOldDPI, &dwSize) != ERROR_SUCCESS)
|
||
|
{
|
||
|
//If "OriginalDPI" value is also missing, that means that nobody has changed DPI.
|
||
|
// Old and New are one and the same!!!
|
||
|
iOldDPI = iNewDPI;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (iNewDPI != iOldDPI) //Has the dpi value changed?
|
||
|
{
|
||
|
HINSTANCE hInst = LoadLibrary(TEXT("desk.cpl"));
|
||
|
|
||
|
if (hInst)
|
||
|
{
|
||
|
LPUPDATEUIFONTS pfnUpdateUIfonts;
|
||
|
//Call desk.cpl to update the UI fonts to reflect the DPI change.
|
||
|
if (pfnUpdateUIfonts = (LPUPDATEUIFONTS)(GetProcAddress(hInst, "UpdateUIfontsDueToDPIchange")))
|
||
|
{
|
||
|
(*pfnUpdateUIfonts)(iOldDPI, iNewDPI);
|
||
|
}
|
||
|
FreeLibrary(hInst);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#define SZ_EXPLORERMUTEX TEXT("ExplorerIsShellMutex")
|
||
|
|
||
|
CComModule _Module;
|
||
|
BEGIN_OBJECT_MAP(ObjectMap)
|
||
|
// add your OBJECT_ENTRY's here
|
||
|
END_OBJECT_MAP()
|
||
|
|
||
|
typedef BOOL (*PFNICOMCTL32)(LPINITCOMMONCONTROLSEX);
|
||
|
void _InitComctl32()
|
||
|
{
|
||
|
HMODULE hmod = LoadLibrary(TEXT("comctl32.dll"));
|
||
|
if (hmod)
|
||
|
{
|
||
|
PFNICOMCTL32 pfn = (PFNICOMCTL32)GetProcAddress(hmod, "InitCommonControlsEx");
|
||
|
if (pfn)
|
||
|
{
|
||
|
INITCOMMONCONTROLSEX icce;
|
||
|
icce.dwICC = 0x00003FFF;
|
||
|
icce.dwSize = sizeof(icce);
|
||
|
pfn(&icce);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL _ShouldFixResolution(void)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
#ifndef _WIN64 // This feature is not supported on 64-bit machine
|
||
|
|
||
|
if (!IsOS(OS_ANYSERVER))
|
||
|
{
|
||
|
DISPLAY_DEVICE dd;
|
||
|
ZeroMemory(&dd, sizeof(DISPLAY_DEVICE));
|
||
|
dd.cb = sizeof(DISPLAY_DEVICE);
|
||
|
|
||
|
if (SHRegGetBoolUSValue(REGSTR_PATH_EXPLORER TEXT("\\DontShowMeThisDialogAgain"), TEXT("ScreenCheck"), FALSE, TRUE))
|
||
|
{
|
||
|
// Don't fix SafeMode or Terminal Clients
|
||
|
if ((GetSystemMetrics(SM_CLEANBOOT) == 0) && (GetSystemMetrics(SM_REMOTESESSION) == FALSE))
|
||
|
{
|
||
|
fRet = TRUE;
|
||
|
for (DWORD dwMon = 0; EnumDisplayDevices(NULL, dwMon, &dd, 0); dwMon++)
|
||
|
{
|
||
|
if (!(dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
|
||
|
{
|
||
|
DEVMODE dm = {0};
|
||
|
dm.dmSize = sizeof(DEVMODE);
|
||
|
|
||
|
if (EnumDisplaySettingsEx(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm, 0))
|
||
|
{
|
||
|
if ((dm.dmFields & DM_POSITION) &&
|
||
|
((dm.dmPelsWidth >= 600) &&
|
||
|
(dm.dmPelsHeight >= 600) &&
|
||
|
(dm.dmBitsPerPel >= 15)))
|
||
|
{
|
||
|
fRet = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // _WIN64
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL _ShouldOfferTour(void)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
#ifndef _WIN64 // This feature is not supported on 64-bit machine
|
||
|
|
||
|
// we don't allow guest to get offered tour b/c guest's registry is wiped every time she logs out,
|
||
|
// so she would get tour offered every single she logged in.
|
||
|
if (!IsOS(OS_ANYSERVER) && !IsOS(OS_EMBEDDED) && !(SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS)))
|
||
|
{
|
||
|
DWORD dwCount;
|
||
|
DWORD cbCount = sizeof(DWORD);
|
||
|
|
||
|
// 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.
|
||
|
if (ERROR_SUCCESS != SHRegGetUSValue(REGSTR_PATH_SETUP TEXT("\\Applets\\Tour"), TEXT("RunCount"), NULL, &dwCount, &cbCount, FALSE, NULL, 0))
|
||
|
{
|
||
|
dwCount = 3;
|
||
|
}
|
||
|
|
||
|
if (dwCount)
|
||
|
{
|
||
|
HUSKEY hkey1;
|
||
|
if (ERROR_SUCCESS == SHRegCreateUSKey(REGSTR_PATH_SETUP TEXT("\\Applets"), KEY_WRITE, NULL, &hkey1, SHREGSET_HKCU))
|
||
|
{
|
||
|
HUSKEY hkey2;
|
||
|
if (ERROR_SUCCESS == SHRegCreateUSKey(TEXT("Tour"), KEY_WRITE, hkey1, &hkey2, SHREGSET_HKCU))
|
||
|
{
|
||
|
if (ERROR_SUCCESS == SHRegWriteUSValue(hkey2, TEXT("RunCount"), REG_DWORD, &(--dwCount), cbCount, SHREGSET_FORCE_HKCU))
|
||
|
{
|
||
|
fRet = TRUE;
|
||
|
}
|
||
|
SHRegCloseUSKey(hkey2);
|
||
|
}
|
||
|
SHRegCloseUSKey(hkey1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // _WIN64
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
typedef BOOL (*CHECKFUNCTION)(void);
|
||
|
|
||
|
void _ConditionalBalloonLaunch(CHECKFUNCTION pCheckFct, SHELLREMINDER* psr)
|
||
|
{
|
||
|
if (pCheckFct())
|
||
|
{
|
||
|
IShellReminderManager* psrm;
|
||
|
HRESULT hr = CoCreateInstance(CLSID_PostBootReminder, NULL, CLSCTX_INPROC_SERVER,
|
||
|
IID_PPV_ARG(IShellReminderManager, &psrm));
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
psrm->Add(psr);
|
||
|
psrm->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void _CheckScreenResolution(void)
|
||
|
{
|
||
|
WCHAR szTitle[256];
|
||
|
WCHAR szText[512];
|
||
|
SHELLREMINDER sr = {0};
|
||
|
|
||
|
LoadString(hinstCabinet, IDS_FIXSCREENRES_TITLE, szTitle, ARRAYSIZE(szTitle));
|
||
|
LoadString(hinstCabinet, IDS_FIXSCREENRES_TEXT, szText, ARRAYSIZE(szText));
|
||
|
|
||
|
sr.cbSize = sizeof (sr);
|
||
|
sr.pszName = L"Microsoft.FixScreenResolution";
|
||
|
sr.pszTitle = szTitle;
|
||
|
sr.pszText = szText;
|
||
|
sr.pszIconResource = L"explorer.exe,9";
|
||
|
sr.dwTypeFlags = NIIF_INFO;
|
||
|
sr.pclsid = (GUID*)&CLSID_ScreenResFixer; // Try to run the Screen Resolution Fixing code over in ThemeUI
|
||
|
sr.pszShellExecute = L"desk.cpl"; // Open the Display Control Panel as a backup
|
||
|
|
||
|
_ConditionalBalloonLaunch(_ShouldFixResolution, &sr);
|
||
|
}
|
||
|
|
||
|
|
||
|
void _OfferTour(void)
|
||
|
{
|
||
|
WCHAR szTitle[256];
|
||
|
WCHAR szText[512];
|
||
|
SHELLREMINDER sr = {0};
|
||
|
|
||
|
LoadString(hinstCabinet, IDS_OFFERTOUR_TITLE, szTitle, ARRAYSIZE(szTitle));
|
||
|
LoadString(hinstCabinet, IDS_OFFERTOUR_TEXT, szText, ARRAYSIZE(szText));
|
||
|
|
||
|
sr.cbSize = sizeof (sr);
|
||
|
sr.pszName = L"Microsoft.OfferTour";
|
||
|
sr.pszTitle = szTitle;
|
||
|
sr.pszText = szText;
|
||
|
sr.pszIconResource = L"tourstart.exe,0";
|
||
|
sr.dwTypeFlags = NIIF_INFO;
|
||
|
sr.pszShellExecute = L"tourstart.exe";
|
||
|
sr.dwShowTime = 60000;
|
||
|
|
||
|
_ConditionalBalloonLaunch(_ShouldOfferTour, &sr);
|
||
|
}
|
||
|
|
||
|
|
||
|
void _FixWordMailRegKey(void)
|
||
|
{
|
||
|
// If we don't have permissions, fine this is just correction code
|
||
|
HKEY hkey;
|
||
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Applications", 0, KEY_ALL_ACCESS, &hkey))
|
||
|
{
|
||
|
HKEY hkeyTemp;
|
||
|
if (ERROR_SUCCESS != RegOpenKeyEx(hkey, L"WINWORD.EXE", 0, KEY_ALL_ACCESS, &hkeyTemp))
|
||
|
{
|
||
|
HKEY hkeyWinWord;
|
||
|
DWORD dwResult;
|
||
|
if (ERROR_SUCCESS == RegCreateKeyEx(hkey, L"WINWORD.EXE", 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyWinWord, &dwResult))
|
||
|
{
|
||
|
HKEY hkeyTBExcept;
|
||
|
if (ERROR_SUCCESS == RegCreateKeyEx(hkeyWinWord, L"TaskbarExceptionsIcons", 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyTBExcept, &dwResult))
|
||
|
{
|
||
|
HKEY hkeyIcon;
|
||
|
if (ERROR_SUCCESS == RegCreateKeyEx(hkeyTBExcept, L"explorer.exe,16", 0, L"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyIcon, &dwResult))
|
||
|
{
|
||
|
LPCTSTR szTemp = L"%ProgramFiles%\\Microsoft Office\\Office10\\OUTLOOK.EXE";
|
||
|
DWORD cb = sizeof(szTemp);
|
||
|
RegSetValue(hkeyIcon, NULL, REG_SZ, szTemp, cb);
|
||
|
RegCloseKey(hkeyIcon);
|
||
|
}
|
||
|
RegCloseKey(hkeyTBExcept);
|
||
|
}
|
||
|
RegCloseKey(hkeyWinWord);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RegCloseKey(hkeyTemp);
|
||
|
}
|
||
|
RegCloseKey(hkey);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CheckUpdateShortcuts()
|
||
|
{
|
||
|
HKEY hKeyInitiallyClear;
|
||
|
|
||
|
// Check to see if during an update we made any notes of shortcuts of interest that were not present.
|
||
|
// If so, call shmgrate.exe to cleanup for us. This is to handle the case of a user deleting a shortcut
|
||
|
// to say Internet Explorer and then having our service pack process recreate them. Apparently this is
|
||
|
// frowned upon.
|
||
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
|
||
|
TEXT("Software\\Microsoft\\Active Setup\\Installed Components\\InitiallyClear"),
|
||
|
0,
|
||
|
KEY_QUERY_VALUE,
|
||
|
&hKeyInitiallyClear))
|
||
|
{
|
||
|
RegCloseKey(hKeyInitiallyClear);
|
||
|
|
||
|
TCHAR szExe[MAX_PATH];
|
||
|
|
||
|
if (GetSystemDirectory(szExe, ARRAYSIZE(szExe)))
|
||
|
{
|
||
|
PathAppend(szExe, TEXT("shmgrate.exe"));
|
||
|
|
||
|
ShellExecute(NULL, NULL, szExe, TEXT("OCInstallCleanupInitiallyClear"), NULL, SW_SHOWNORMAL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int ExplorerWinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPTSTR pszCmdLine, int nCmdShow)
|
||
|
{
|
||
|
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
||
|
|
||
|
SHFusionInitializeFromModule(hInstance);
|
||
|
|
||
|
CcshellGetDebugFlags();
|
||
|
|
||
|
g_dwStopWatchMode = StopWatchMode();
|
||
|
|
||
|
if (g_dwProfileCAP & 0x00000001)
|
||
|
StartCAP();
|
||
|
|
||
|
hinstCabinet = hInstance;
|
||
|
|
||
|
if (SUCCEEDED(_Module.Init(ObjectMap, hInstance)))
|
||
|
{
|
||
|
_CreateAppGlobals();
|
||
|
|
||
|
// Run IEAK via Wininet initialization if the autoconfig url is present.
|
||
|
// No need to unload wininet in this case. Also only do this first time
|
||
|
// Explorer loads (GetShellWindow() returns NULL).
|
||
|
if (!GetShellWindow() && !g_fCleanBoot && SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\Internet Settings"),
|
||
|
TEXT("AutoConfigURL"),
|
||
|
NULL, NULL, NULL, FALSE, NULL, 0) == ERROR_SUCCESS)
|
||
|
{
|
||
|
LoadLibrary(TEXT("WININET.DLL"));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Very Important: Make sure to init dde prior to any Get/Peek/Wait().
|
||
|
InitializeCriticalSection(&g_csDll);
|
||
|
|
||
|
// Need to initialize the version 5 common controls for AppCompat. Apps sucked into explorer's
|
||
|
// process expect the shell to register controls for them.
|
||
|
_InitComctl32();
|
||
|
|
||
|
#ifdef FULL_DEBUG
|
||
|
// Turn off GDI batching so that paints are performed immediately
|
||
|
GdiSetBatchLimit(1);
|
||
|
#endif
|
||
|
|
||
|
RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, &g_hkeyExplorer);
|
||
|
if (g_hkeyExplorer == NULL)
|
||
|
{
|
||
|
TraceMsg(TF_ERROR, "ExplorerWinMain: unable to create reg explorer key");
|
||
|
}
|
||
|
|
||
|
HANDLE hMutex = NULL;
|
||
|
|
||
|
BOOL fExplorerIsShell = ShouldStartDesktopAndTray();
|
||
|
if (fExplorerIsShell)
|
||
|
{
|
||
|
// Grab the mutex and do the check again. We do it this
|
||
|
// way so that we don't bother with the mutex for the common
|
||
|
// case of opening a browser window.
|
||
|
|
||
|
hMutex = CreateMutex(NULL, FALSE, SZ_EXPLORERMUTEX);
|
||
|
if (hMutex)
|
||
|
{
|
||
|
WaitForSingleObject(hMutex, INFINITE);
|
||
|
}
|
||
|
|
||
|
fExplorerIsShell = ShouldStartDesktopAndTray();
|
||
|
}
|
||
|
|
||
|
if (!fExplorerIsShell)
|
||
|
{
|
||
|
// We're not going to be the shell, relinquish the mutex
|
||
|
if (hMutex)
|
||
|
ReleaseMutex(hMutex);
|
||
|
|
||
|
// we purposely do NOT want to init OLE or COM in this case since we are delegating the creation work
|
||
|
// to an existing explorer and we want to keep from loading lots of extra dlls that would slow us down.
|
||
|
MyCreateFromDesktop(hInstance, pszCmdLine, nCmdShow);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MSG msg;
|
||
|
|
||
|
DWORD dwShellStartTime = GetTickCount(); // Compute shell startup time for perf automation
|
||
|
|
||
|
ShellDDEInit(TRUE); // use shdocvw shell DDE code.
|
||
|
|
||
|
// Specify the shutdown order of the shell process. 2 means
|
||
|
// the explorer should shutdown after everything but ntsd/windbg
|
||
|
// (level 0). (Taskman used to use 1, but is no more.)
|
||
|
|
||
|
SetProcessShutdownParameters(2, 0);
|
||
|
|
||
|
_AutoRunTaskMan();
|
||
|
|
||
|
// NB Make this the primary thread by calling peek message
|
||
|
// for a message we know we're not going to get.
|
||
|
// If we don't do it really soon, the notify thread can sometimes
|
||
|
// become the primary thread by accident. There's a bunch of
|
||
|
// special code in user to implement DDE hacks by assuming that
|
||
|
// the primary thread is handling DDE.
|
||
|
// Also, the PeekMsg() will cause us to set the WaitForInputIdle()
|
||
|
// event so we better be ready to do all dde.
|
||
|
|
||
|
PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE);
|
||
|
|
||
|
// We do this here, since FileIconInit will call SHCoInitialize anyway
|
||
|
HRESULT hrInit = OleInitializeWaitForSCM();
|
||
|
|
||
|
// Make sure we are the first one to call the FileIconInit...
|
||
|
FileIconInit(TRUE); // Tell the shell we want to play with a full deck
|
||
|
|
||
|
g_fLogonCycle = IsFirstInstanceAfterLogon();
|
||
|
g_fCleanShutdown = ReadCleanShutdown();
|
||
|
|
||
|
CheckDefaultUIFonts();
|
||
|
ChangeUIfontsToNewDPI(); //Check dpi values and update the fonts if needed.
|
||
|
|
||
|
if (g_fLogonCycle)
|
||
|
{
|
||
|
_ProcessRunOnceEx();
|
||
|
|
||
|
_ProcessRunOnce();
|
||
|
}
|
||
|
|
||
|
if (g_fCleanBoot)
|
||
|
{
|
||
|
// let users know we are in safe mode
|
||
|
DisplayCleanBootMsg();
|
||
|
}
|
||
|
|
||
|
// Create the other special folders.
|
||
|
CreateShellDirectories();
|
||
|
|
||
|
// Run install stubs for the current user, mostly to propagate
|
||
|
// shortcuts to apps installed by another user.
|
||
|
if (!g_fCleanBoot)
|
||
|
{
|
||
|
HANDLE hCanRegister = CreateEvent(NULL, TRUE, TRUE, TEXT("_fCanRegisterWithShellService"));
|
||
|
|
||
|
RunInstallUninstallStubs();
|
||
|
|
||
|
CheckUpdateShortcuts();
|
||
|
|
||
|
if (hCanRegister)
|
||
|
{
|
||
|
CloseHandle(hCanRegister);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!g_fCleanShutdown)
|
||
|
{
|
||
|
IActiveDesktopP *piadp;
|
||
|
DWORD dwFaultCount;
|
||
|
|
||
|
// Increment and store away fault count
|
||
|
dwFaultCount = ReadFaultCount();
|
||
|
WriteFaultCount(++dwFaultCount);
|
||
|
|
||
|
// Put the active desktop in safe mode if we faulted 3 times previously and this is a subsequent instance
|
||
|
|
||
|
if (ShouldDisplaySafeMode() && SUCCEEDED(CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC, IID_PPV_ARG(IActiveDesktopP, &piadp))))
|
||
|
{
|
||
|
piadp->SetSafeMode(SSM_SET | SSM_UPDATE);
|
||
|
piadp->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
WriteCleanShutdown(FALSE); // assume we will have a bad shutdown
|
||
|
|
||
|
WinList_Init();
|
||
|
|
||
|
// If any of the shellwindows are already present, then we want to bail out.
|
||
|
//
|
||
|
// NOTE: Compaq shell changes the "shell=" line during RunOnce time and
|
||
|
// that will make ShouldStartDesktopAndTray() return FALSE
|
||
|
|
||
|
HANDLE hDesktop = NULL;
|
||
|
|
||
|
if (!IsAnyShellWindowAlreadyPresent())
|
||
|
{
|
||
|
hDesktop = CreateDesktopAndTray();
|
||
|
}
|
||
|
|
||
|
// Now that we've had a chance to create the desktop, release the mutex
|
||
|
if (hMutex)
|
||
|
{
|
||
|
ReleaseMutex(hMutex);
|
||
|
}
|
||
|
|
||
|
if (hDesktop)
|
||
|
{
|
||
|
// Enable display of balloons in the tray...
|
||
|
PostMessage(v_hwndTray, TM_SHOWTRAYBALLOON, TRUE, 0);
|
||
|
|
||
|
_CheckScreenResolution();
|
||
|
|
||
|
_OfferTour();
|
||
|
|
||
|
_FixWordMailRegKey();
|
||
|
|
||
|
_RunWinComCmdLine(pszCmdLine, nCmdShow);
|
||
|
|
||
|
if (g_dwStopWatchMode)
|
||
|
{
|
||
|
// We used to save these off into global vars, and then write them at
|
||
|
// WM_ENDSESSION, but that seems too unreliable
|
||
|
DWORD dwShellStopTime = GetTickCount();
|
||
|
StopWatch_StartTimed(SWID_STARTUP, TEXT("Shell Startup: Start"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwShellStartTime);
|
||
|
StopWatch_StopTimed(SWID_STARTUP, TEXT("Shell Startup: Stop"), SPMODE_SHELL | SPMODE_DEBUGOUT, dwShellStopTime);
|
||
|
}
|
||
|
|
||
|
#ifdef FEATURE_STARTPAGE
|
||
|
BOOL fIsStartPage = Tray_ShowStartPageEnabled();
|
||
|
if (fIsStartPage)
|
||
|
CreateStartPage(hInstance, hDesktop);
|
||
|
#endif
|
||
|
|
||
|
if (g_dwProfileCAP & 0x00010000)
|
||
|
StopCAP();
|
||
|
|
||
|
PERFSETMARK("ExplorerStartMsgLoop");
|
||
|
|
||
|
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
||
|
|
||
|
// this must be whomever is the window on this thread
|
||
|
SHDesktopMessageLoop(hDesktop);
|
||
|
|
||
|
#ifdef FEATURE_STARTPAGE
|
||
|
if (fIsStartPage)
|
||
|
{
|
||
|
// DirectUI uninit thread
|
||
|
DirectUI::UnInitThread();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
WriteCleanShutdown(TRUE); // we made it out ok, record that fact
|
||
|
WriteFaultCount(0); // clear our count of faults, we are exiting normally
|
||
|
}
|
||
|
|
||
|
WinList_Terminate(); // Turn off our window list processing
|
||
|
OleUninitialize();
|
||
|
SHCoUninitialize(hrInit);
|
||
|
|
||
|
ShellDDEInit(FALSE); // use shdocvw shell DDE code
|
||
|
}
|
||
|
|
||
|
_Module.Term();
|
||
|
}
|
||
|
|
||
|
SHFusionUninitialize();
|
||
|
DebugMsg(DM_TRACE, TEXT("c.App Exit."));
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#ifdef _WIN64
|
||
|
//
|
||
|
// The purpose of this function is to spawn rundll32.exe if we have 32-bit stuff in
|
||
|
// HKLM\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run that needs to be executed.
|
||
|
//
|
||
|
BOOL _ProcessRun6432()
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
if (!SHRestricted(REST_NOLOCALMACHINERUN))
|
||
|
{
|
||
|
if (SHKeyHasValues(HKEY_LOCAL_MACHINE, TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")))
|
||
|
{
|
||
|
TCHAR szWow64Path[MAX_PATH];
|
||
|
|
||
|
if (ExpandEnvironmentStrings(TEXT("%SystemRoot%\\SysWOW64"), szWow64Path, ARRAYSIZE(szWow64Path)))
|
||
|
{
|
||
|
TCHAR sz32BitRunOnce[MAX_PATH];
|
||
|
PROCESS_INFORMATION pi = {0};
|
||
|
|
||
|
wnsprintf(sz32BitRunOnce, ARRAYSIZE(sz32BitRunOnce), TEXT("%s\\runonce.exe"), szWow64Path);
|
||
|
|
||
|
if (CreateProcessWithArgs(sz32BitRunOnce, TEXT("/Run6432"), szWow64Path, &pi))
|
||
|
{
|
||
|
CloseHandle(pi.hProcess);
|
||
|
CloseHandle(pi.hThread);
|
||
|
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
#endif // _WIN64
|
||
|
|
||
|
|
||
|
STDAPI_(BOOL) Startup_ExecuteRegAppEnumProc(LPCTSTR szSubkey, LPCTSTR szCmdLine, RRA_FLAGS fFlags, LPARAM lParam)
|
||
|
{
|
||
|
BOOL bRet = ExecuteRegAppEnumProc(szSubkey, szCmdLine, fFlags, lParam);
|
||
|
|
||
|
if (!bRet && !(fFlags & RRA_DELETE))
|
||
|
{
|
||
|
c_tray.LogFailedStartupApp();
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
RESTRICTIONS rest;
|
||
|
HKEY hKey;
|
||
|
const TCHAR* psz;
|
||
|
DWORD dwRRAFlags;
|
||
|
}
|
||
|
STARTUPGROUP;
|
||
|
|
||
|
BOOL _RunStartupGroup(const STARTUPGROUP* pGroup, int cGroup)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
// make sure SHRestricted is working ok
|
||
|
ASSERT(!SHRestricted(REST_NONE));
|
||
|
|
||
|
for (int i = 0; i < cGroup; i++)
|
||
|
{
|
||
|
if (!SHRestricted(pGroup[i].rest))
|
||
|
{
|
||
|
bRet = Cabinet_EnumRegApps(pGroup[i].hKey, pGroup[i].psz, pGroup[i].dwRRAFlags, Startup_ExecuteRegAppEnumProc, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL _ProcessRun()
|
||
|
{
|
||
|
static const STARTUPGROUP s_RunTasks [] =
|
||
|
{
|
||
|
{ REST_NONE, HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN_POLICY, RRA_NOUI }, // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run
|
||
|
{ REST_NOLOCALMACHINERUN, HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, RRA_NOUI }, // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
|
||
|
{ REST_NONE, HKEY_CURRENT_USER, REGSTR_PATH_RUN_POLICY, RRA_NOUI }, // HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\Run
|
||
|
{ REST_NOCURRENTUSERRUN, HKEY_CURRENT_USER, REGSTR_PATH_RUN, RRA_NOUI }, // HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
|
||
|
};
|
||
|
|
||
|
BOOL bRet = _RunStartupGroup(s_RunTasks, ARRAYSIZE(s_RunTasks));
|
||
|
|
||
|
#ifdef _WIN64
|
||
|
// see if we need to launch any 32-bit apps under wow64
|
||
|
_ProcessRun6432();
|
||
|
#endif
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL _ProcessPerUserRunOnce()
|
||
|
{
|
||
|
static const STARTUPGROUP s_PerUserRunOnceTasks [] =
|
||
|
{
|
||
|
{ REST_NOCURRENTUSERRUNONCE, HKEY_CURRENT_USER, REGSTR_PATH_RUNONCE, RRA_DELETE | RRA_NOUI }, // HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce
|
||
|
};
|
||
|
|
||
|
return _RunStartupGroup(s_PerUserRunOnceTasks, ARRAYSIZE(s_PerUserRunOnceTasks));
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD WINAPI RunStartupAppsThread(void *pv)
|
||
|
{
|
||
|
// Some of the items we launch during startup assume that com is initialized. Make this
|
||
|
// assumption true.
|
||
|
HRESULT hrInit = SHCoInitialize();
|
||
|
|
||
|
// These global flags are set once long before our thread starts and are then only
|
||
|
// read so we don't need to worry about timing issues.
|
||
|
if (g_fLogonCycle && !g_fCleanBoot)
|
||
|
{
|
||
|
// We only run these startup items if g_fLogonCycle is TRUE. This prevents
|
||
|
// them from running again if the shell crashes and restarts.
|
||
|
|
||
|
_ProcessOldRunAndLoadEquals();
|
||
|
_ProcessRun();
|
||
|
_ExecuteStartupPrograms();
|
||
|
}
|
||
|
|
||
|
// As a best guess, the HKCU RunOnce key is executed regardless of the g_fLogonCycle
|
||
|
// becuase it was once hoped that we could install newer versions of IE without
|
||
|
// requiring a reboot. They would place something in the CU\RunOnce key and then
|
||
|
// shutdown and restart the shell to continue their setup process. I believe this
|
||
|
// idea was later abandoned but the code change is still here. Since that could
|
||
|
// some day be a useful feature I'm leaving it the same.
|
||
|
_ProcessPerUserRunOnce();
|
||
|
|
||
|
// we need to run all the non-blocking items first. Then we spend the rest of this threads life
|
||
|
// runing the synchronized objects one after another.
|
||
|
if (g_fLogonCycle && !g_fCleanBoot)
|
||
|
{
|
||
|
_RunWelcome();
|
||
|
}
|
||
|
|
||
|
PostMessage(v_hwndTray, TM_STARTUPAPPSLAUNCHED, 0, 0);
|
||
|
|
||
|
SHCoUninitialize(hrInit);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
void RunStartupApps()
|
||
|
{
|
||
|
DWORD dwThreadID;
|
||
|
HANDLE handle = CreateThread(NULL, 0, RunStartupAppsThread, 0, 0, &dwThreadID);
|
||
|
if (handle)
|
||
|
{
|
||
|
CloseHandle(handle);
|
||
|
}
|
||
|
}
|