windows-nt/Source/XPSP1/NT/admin/pchealth/sysinfo/msconfig/exe/msconfig.cpp

723 lines
19 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//=============================================================================
// MSConfig.cpp
//
// This contains the high level implementation of MSConfig - this class
// creates all of the pages and displays a property sheet.
//=============================================================================
#include "stdafx.h"
#include "MSConfig.h"
#include <initguid.h>
#include "MSConfig_i.c"
#include "MSConfigState.h"
#include "PageServices.h"
#include "PageStartup.h"
#include "PageBootIni.h"
#include "PageIni.h"
#include "PageGeneral.h"
#include "MSConfigCtl.h"
#include "AutoStartDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define MSCONFIGDIR _T("%systemroot%\\pss")
//-----------------------------------------------------------------------------
// These global variables are pointers to each of the property pages shown
// in MSConfig. They are made global so that each page can potential make
// calls into other pages, to allow interaction.
//-----------------------------------------------------------------------------
CPageServices * ppageServices = NULL;
CPageStartup * ppageStartup = NULL;
CPageBootIni * ppageBootIni = NULL;
CPageIni * ppageWinIni = NULL;
CPageIni * ppageSystemIni = NULL;
CPageGeneral * ppageGeneral = NULL;
//-----------------------------------------------------------------------------
// Other globals.
//-----------------------------------------------------------------------------
CMSConfigSheet * pMSConfigSheet = NULL; // global pointer to the property sheet
/////////////////////////////////////////////////////////////////////////////
// CMSConfigApp
BEGIN_MESSAGE_MAP(CMSConfigApp, CWinApp)
//{{AFX_MSG_MAP(CMSConfigApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Constructor. Nothing important here.
//-----------------------------------------------------------------------------
CMSConfigApp::CMSConfigApp()
{
}
CMSConfigApp theApp;
//-----------------------------------------------------------------------------
// InitInstance is where we create the property sheet and show it (assuming
// there isn't a command line flag to do otherwise).
//-----------------------------------------------------------------------------
BOOL fBasicControls = FALSE; // hide any advanced controls if true
BOOL CMSConfigApp::InitInstance()
{
if (!InitATL())
return FALSE;
AfxEnableControlContainer();
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
{
return TRUE;
}
// If this is not the first instance, exit. The call to FirstInstance
// will activate the previous instance.
if (!FirstInstance())
return FALSE;
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
// Process the command line to see if one of the following flags have been set:
//
// /n (where n is a number) startup showing the nth tab
// /basic hide advanced features
// /commit n make changes from tab number n permanent
// /auto show the automatic launch dialog
int nInitialTab = 0;
int nCommitTab = 0;
BOOL fShowAutoDialog = FALSE;
CString strCommandLine(m_lpCmdLine);
CString strFlag, strTemp;
strCommandLine.MakeLower();
while (!strCommandLine.IsEmpty())
{
// Get the next flag from the command line (starting at a / or -,
// and containing the text to the end of the string or the next
// instance of a / or -).
int iFlag = strCommandLine.FindOneOf(_T("/-"));
if (iFlag == -1)
break;
strFlag = strCommandLine.Mid(iFlag + 1);
strFlag = strFlag.SpanExcluding(_T("/-"));
strCommandLine = strCommandLine.Mid(iFlag + 1 + strFlag.GetLength());
strFlag.TrimRight();
// Check for the /auto flag.
if (strFlag.Find(_T("auto")) == 0)
fShowAutoDialog = TRUE;
// Check for the "/basic" flag.
if (strFlag.Compare(_T("basic")) == 0)
{
fBasicControls = TRUE;
continue;
}
// Check for the "/commit n" flag.
if (strFlag.Left(6) == CString(_T("commit")))
{
// Find out which tab number to commit. Skip all of the
// non-numeric characters.
strTemp = strFlag.SpanExcluding(_T("0123456789"));
if (strTemp.GetLength() < strFlag.GetLength())
{
strFlag = strFlag.Mid(strTemp.GetLength());
if (!strFlag.IsEmpty())
{
TCHAR c = strFlag[0];
if (_istdigit(c))
nCommitTab = _ttoi((LPCTSTR)strFlag);
}
}
continue;
}
// Finally, check for the "/n" flag, where n is the number of
// the tab to initially display.
if (strFlag.GetLength() == 1)
{
TCHAR c = strFlag[0];
if (_istdigit(c))
nInitialTab = _ttoi((LPCTSTR)strFlag);
}
}
// Show the automatic launch dialog. The user may make settings in this
// dialog that will keep MSConfig from automatically launching.
if (fShowAutoDialog)
{
CAutoStartDlg dlg;
dlg.DoModal();
if (dlg.m_checkDontShow)
{
SetAutoRun(FALSE);
return FALSE;
}
}
// Check to see if the user is going to be able to make changes using MSConfig
// (if not an admin, probably not). If the user doesn't have the necessary
// privileges, don't run. Bug 475796.
BOOL fModifyServices = FALSE, fModifyRegistry = FALSE;
// Check to see if the user will be able to modify services.
SC_HANDLE sch = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (sch != NULL)
{
fModifyServices = TRUE;
::CloseServiceHandle(sch);
}
// Check to see if the user can modify the registry.
HKEY hkey;
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools"), 0, KEY_WRITE, &hkey))
{
fModifyRegistry = TRUE;
::RegCloseKey(hkey);
}
// If the user can't do both actions, exit now.
if (!fModifyServices || !fModifyRegistry)
{
CString strText, strCaption;
strCaption.LoadString(IDS_DIALOGCAPTION);
strText.LoadString(IDS_SERVICEACCESSDENIED);
::MessageBox(NULL, strText, strCaption, MB_OK | MB_ICONSTOP);
return FALSE;
}
// This will load all the pages.
BOOL fNeedsReboot = FALSE;
InitializePages();
// If the command line specifies to commit a change, we won't
// show the dialog.
if (nCommitTab > 1) // ignore zero (no flag) and one (general tab)
{
CPageBase * pPage = NULL;
CString strTabCaption;
if (NULL == ppageBootIni && nCommitTab >= 4)
nCommitTab += 1; // adjust tab number if there is no BOOT.INI tab
switch (nCommitTab)
{
case 2:
pPage = dynamic_cast<CPageBase *>(ppageSystemIni);
strTabCaption.LoadString(IDS_SYSTEMINI_CAPTION);
break;
case 3:
pPage = dynamic_cast<CPageBase *>(ppageWinIni);
strTabCaption.LoadString(IDS_WININI_CAPTION);
break;
case 4:
pPage = dynamic_cast<CPageBase *>(ppageBootIni);
strTabCaption.LoadString(IDS_BOOTINI_CAPTION);
break;
case 5:
pPage = dynamic_cast<CPageBase *>(ppageServices);
strTabCaption.LoadString(IDS_SERVICES_CAPTION);
break;
case 6:
pPage = dynamic_cast<CPageBase *>(ppageStartup);
strTabCaption.LoadString(IDS_STARTUP_CAPTION);
break;
}
if (pPage)
{
CString strText, strCaption;
strCaption.LoadString(IDS_DIALOGCAPTION);
strText.Format(IDS_COMMITMESSAGE, strTabCaption);
if (IDYES == ::MessageBox(NULL, strText, strCaption, MB_YESNO))
pPage->CommitChanges();
}
}
else
fNeedsReboot = ShowPropertySheet(nInitialTab);
CleanupPages();
if (fNeedsReboot)
Reboot();
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
//-----------------------------------------------------------------------------
// Create all of the property pages. This function also contains the logic to
// exclude property pages under certain circumstances (for example, if there
// is no BOOT.INI file, don't create that page). TBD.
//-----------------------------------------------------------------------------
void CMSConfigApp::InitializePages()
{
// The boot.ini tab shouldn't be added if the file doesn't exist (for
// instance, on Win64).
CString strBootINI(_T("c:\\boot.ini"));
// Check the registry for a testing flag (which would mean we aren't
// operating on the real BOOT.INI file).
CRegKey regkey;
if (ERROR_SUCCESS == regkey.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools\\MSConfig")))
{
TCHAR szBoot[MAX_PATH];
DWORD dwCount = MAX_PATH;
if (ERROR_SUCCESS == regkey.QueryValue(szBoot, _T("boot.ini"), &dwCount))
strBootINI = szBoot;
}
if (FileExists(strBootINI))
ppageBootIni = new CPageBootIni;
else
ppageBootIni = NULL;
ppageServices = new CPageServices;
ppageStartup = new CPageStartup;
ppageWinIni = new CPageIni;
ppageSystemIni = new CPageIni;
ppageGeneral = new CPageGeneral;
ppageWinIni->SetTabInfo(_T("win.ini"));
ppageSystemIni->SetTabInfo(_T("system.ini"));
}
//-----------------------------------------------------------------------------
// Show the MSConfig property sheet. This function returns whether or not
// the computer should be rebooted.
//-----------------------------------------------------------------------------
BOOL CMSConfigApp::ShowPropertySheet(int nInitialTab)
{
CMSConfigSheet sheet(IDS_DIALOGCAPTION, NULL, (nInitialTab > 0) ? nInitialTab - 1 : 0);
// Add each of the pages to the property sheet.
if (ppageGeneral) sheet.AddPage(ppageGeneral);
if (ppageSystemIni) sheet.AddPage(ppageSystemIni);
if (ppageWinIni) sheet.AddPage(ppageWinIni);
if (ppageBootIni) sheet.AddPage(ppageBootIni);
if (ppageServices) sheet.AddPage(ppageServices);
if (ppageStartup) sheet.AddPage(ppageStartup);
// Show the property sheet.
pMSConfigSheet = &sheet;
INT_PTR iReturn = sheet.DoModal();
pMSConfigSheet = NULL;
// Possibly set MSConfig to automatically run on boot, and
// check to see if we need to restart.
BOOL fRunMSConfigOnBoot = FALSE;
BOOL fNeedToRestart = FALSE;
CPageBase * apPages[5] =
{
ppageSystemIni,
ppageWinIni,
ppageBootIni,
ppageServices,
ppageStartup
};
for (int nPage = 0; nPage < 5; nPage++)
if (apPages[nPage])
{
fRunMSConfigOnBoot |= (CPageBase::NORMAL != apPages[nPage]->GetAppliedTabState());
fNeedToRestart |= apPages[nPage]->HasAppliedChanges();
if (fRunMSConfigOnBoot && fNeedToRestart)
break;
}
// If the user didn't click CANCEL, or the user applied a change, then
// we should set whether or not MSConfig needs to automatically run on boot.
if (fNeedToRestart || iReturn != IDCANCEL)
SetAutoRun(fRunMSConfigOnBoot);
return (fNeedToRestart);
}
//-----------------------------------------------------------------------------
// Cleanup the global property pages.
//-----------------------------------------------------------------------------
void CMSConfigApp::CleanupPages()
{
if (ppageGeneral) delete ppageGeneral;
if (ppageSystemIni) delete ppageSystemIni;
if (ppageWinIni) delete ppageWinIni;
if (ppageBootIni) delete ppageBootIni;
if (ppageServices) delete ppageServices;
if (ppageStartup) delete ppageStartup;
}
//-------------------------------------------------------------------------
// This function will set the appropriate registry key to make msconfig run
// on system start.
//-------------------------------------------------------------------------
void CMSConfigApp::SetAutoRun(BOOL fAutoRun)
{
LPCTSTR szRegKey = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
LPCTSTR szRegVal = _T("MSConfig");
CRegKey regkey;
if (ERROR_SUCCESS != regkey.Open(HKEY_LOCAL_MACHINE, szRegKey))
return;
if (fAutoRun)
{
TCHAR szModulePath[MAX_PATH];
if (::GetModuleFileName(::GetModuleHandle(NULL), szModulePath, MAX_PATH) == 0)
return;
if (!FileExists(szModulePath))
return;
CString strNewVal = CString(szModulePath) + CString(_T(" ")) + CString(COMMANDLINE_AUTO);
regkey.SetValue(strNewVal, szRegVal);
}
else
regkey.DeleteValue(szRegVal);
}
//-----------------------------------------------------------------------------
// Not much explanation needed here. The user is given an option to not
// restart the system.
//-----------------------------------------------------------------------------
void CMSConfigApp::Reboot()
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
if (hProcess)
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tp.Privileges[0].Luid) && AdjustTokenPrivileges(hToken, FALSE, &tp, NULL, NULL, NULL))
{
CRebootDlg dlg;
if (dlg.DoModal() == IDOK)
ExitWindowsEx(EWX_REBOOT, 0);
}
else
Message(IDS_USERSHOULDRESTART);
}
CloseHandle(hProcess);
}
}
CMSConfigModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()
LONG CMSConfigModule::Unlock()
{
AfxOleUnlockApp();
return 0;
}
LONG CMSConfigModule::Lock()
{
AfxOleLockApp();
return 1;
}
LPCTSTR CMSConfigModule::FindOneOf(LPCTSTR p1, LPCTSTR p2)
{
while (*p1 != NULL)
{
LPCTSTR p = p2;
while (*p != NULL)
{
if (*p1 == *p)
return CharNext(p1);
p = CharNext(p);
}
p1++;
}
return NULL;
}
int CMSConfigApp::ExitInstance()
{
if (m_bATLInited)
{
_Module.RevokeClassObjects();
_Module.Term();
CoUninitialize();
}
return CWinApp::ExitInstance();
}
BOOL CMSConfigApp::InitATL()
{
m_bATLInited = TRUE;
#if _WIN32_WINNT >= 0x0400
HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
#else
HRESULT hRes = CoInitialize(NULL);
#endif
if (FAILED(hRes))
{
m_bATLInited = FALSE;
return FALSE;
}
_Module.Init(ObjectMap, AfxGetInstanceHandle());
_Module.dwThreadID = GetCurrentThreadId();
LPTSTR lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
TCHAR szTokens[] = _T("-/");
BOOL bRun = TRUE;
LPCTSTR lpszToken = _Module.FindOneOf(lpCmdLine, szTokens);
while (lpszToken != NULL)
{
if (lstrcmpi(lpszToken, _T("UnregServer"))==0)
{
_Module.UpdateRegistryFromResource(IDR_MSCONFIG, FALSE);
_Module.UnregisterServer(TRUE); //TRUE means typelib is unreg'd
bRun = FALSE;
break;
}
if (lstrcmpi(lpszToken, _T("RegServer"))==0)
{
_Module.UpdateRegistryFromResource(IDR_MSCONFIG, TRUE);
_Module.RegisterServer(TRUE);
bRun = FALSE;
break;
}
lpszToken = _Module.FindOneOf(lpszToken, szTokens);
}
if (!bRun)
{
m_bATLInited = FALSE;
_Module.Term();
CoUninitialize();
return FALSE;
}
hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE);
if (FAILED(hRes))
{
m_bATLInited = FALSE;
CoUninitialize();
return FALSE;
}
return TRUE;
}
//=============================================================================
// Implement the utility functions described in msconfigstate.h
//=============================================================================
void Message(LPCTSTR szMessage, HWND hwndParent)
{
CString strCaption;
strCaption.LoadString(IDS_APPCAPTION);
if (hwndParent != NULL || pMSConfigSheet == NULL)
::MessageBox(hwndParent, szMessage, strCaption, MB_OK);
else
::MessageBox(pMSConfigSheet->GetSafeHwnd(), szMessage, strCaption, MB_OK);
}
void Message(UINT uiMessage, HWND hwndParent)
{
CString strMessage;
strMessage.LoadString(uiMessage);
Message((LPCTSTR)strMessage, hwndParent);
}
HKEY GetRegKey(LPCTSTR szSubKey)
{
LPCTSTR szMSConfigKey = _T("SOFTWARE\\Microsoft\\Shared Tools\\MSConfig");
CString strKey(szMSConfigKey);
HKEY hkey = NULL;
// Try to open the base MSConfig key. If it succeeds, and there is no
// subkey to open, return the HKEY. Otherwise, we need to create the
// base key.
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMSConfigKey, 0, KEY_ALL_ACCESS, &hkey))
{
if (szSubKey == NULL)
return hkey;
::RegCloseKey(hkey);
}
else
{
// Create the MSConfig key (and close it).
HKEY hkeyBase;
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools"), 0, KEY_ALL_ACCESS, &hkeyBase))
{
if (ERROR_SUCCESS == RegCreateKeyEx(hkeyBase, _T("MSConfig"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
::RegCloseKey(hkey);
::RegCloseKey(hkeyBase);
}
}
if (szSubKey)
strKey += CString(_T("\\")) + CString(szSubKey);
if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKey, 0, KEY_ALL_ACCESS, &hkey))
{
// If we couldn't open the subkey, then we should try to create it.
if (szSubKey)
{
HKEY hkeyBase;
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMSConfigKey, 0, KEY_ALL_ACCESS, &hkeyBase))
{
if (ERROR_SUCCESS != RegCreateKeyEx(hkeyBase, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
hkey = NULL;
::RegCloseKey(hkeyBase);
}
}
}
return hkey;
}
HRESULT BackupFile(LPCTSTR szFilename, const CString & strAddedExtension, BOOL fOverwrite)
{
CString strFrom(szFilename);
CString strTo(GetBackupName(szFilename, strAddedExtension));
if (!fOverwrite && FileExists(strTo))
return S_FALSE;
::SetFileAttributes(strTo, FILE_ATTRIBUTE_NORMAL);
if (::CopyFile(strFrom, strTo, FALSE))
{
::SetFileAttributes(strTo, FILE_ATTRIBUTE_NORMAL);
return S_OK;
}
return E_FAIL;
}
CString strBackupDir; // global string containing the path of the backup directory
const CString GetBackupName(LPCTSTR szFilename, const CString & strAddedExtension)
{
// There should be a directory for MSConfig files. Make sure it exists
// (create it if it doesn't).
if (strBackupDir.IsEmpty())
{
TCHAR szMSConfigDir[MAX_PATH];
if (MAX_PATH > ::ExpandEnvironmentStrings(MSCONFIGDIR, szMSConfigDir, MAX_PATH))
{
strBackupDir = szMSConfigDir;
if (!FileExists(strBackupDir))
::CreateDirectory(strBackupDir, NULL);
}
}
CString strFrom(szFilename);
int i = strFrom.ReverseFind(_T('\\'));
CString strFile(strFrom.Mid(i + 1));
CString strTo(strBackupDir + _T("\\") + strFile + strAddedExtension);
return strTo;
}
HRESULT RestoreFile(LPCTSTR szFilename, const CString & strAddedExtension, BOOL fOverwrite)
{
CString strTo(szFilename);
int i = strTo.ReverseFind(_T('\\'));
CString strFile(strTo.Mid(i + 1));
CString strFrom(strBackupDir + _T("\\") + strFile + strAddedExtension);
if (!fOverwrite && FileExists(strTo))
return S_FALSE;
DWORD dwAttributes = ::GetFileAttributes(strTo);
::SetFileAttributes(strTo, FILE_ATTRIBUTE_NORMAL);
if (::CopyFile(strFrom, strTo, FALSE))
{
::SetFileAttributes(strTo, dwAttributes);
return S_OK;
}
return E_FAIL;
}