|
|
//=============================================================================
// 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_NOPERMISSIONS); ::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 + 1]; DWORD dwLength = ::GetModuleFileName(::GetModuleHandle(NULL), szModulePath, MAX_PATH); if (dwLength == 0) return; szModulePath[dwLength] = _T('\0');
if (!FileExists(szModulePath)) return;
CString strNewVal = CString(_T("\"")) + 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) && (GetLastError() == ERROR_SUCCESS)) { 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; }
|