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

726 lines
20 KiB

  1. //=============================================================================
  2. // MSConfig.cpp
  3. //
  4. // This contains the high level implementation of MSConfig - this class
  5. // creates all of the pages and displays a property sheet.
  6. //=============================================================================
  7. #include "stdafx.h"
  8. #include "MSConfig.h"
  9. #include <initguid.h>
  10. #include "MSConfig_i.c"
  11. #include "MSConfigState.h"
  12. #include "PageServices.h"
  13. #include "PageStartup.h"
  14. #include "PageBootIni.h"
  15. #include "PageIni.h"
  16. #include "PageGeneral.h"
  17. #include "MSConfigCtl.h"
  18. #include "AutoStartDlg.h"
  19. #ifdef _DEBUG
  20. #define new DEBUG_NEW
  21. #undef THIS_FILE
  22. static char THIS_FILE[] = __FILE__;
  23. #endif
  24. #define MSCONFIGDIR _T("%systemroot%\\pss")
  25. //-----------------------------------------------------------------------------
  26. // These global variables are pointers to each of the property pages shown
  27. // in MSConfig. They are made global so that each page can potential make
  28. // calls into other pages, to allow interaction.
  29. //-----------------------------------------------------------------------------
  30. CPageServices * ppageServices = NULL;
  31. CPageStartup * ppageStartup = NULL;
  32. CPageBootIni * ppageBootIni = NULL;
  33. CPageIni * ppageWinIni = NULL;
  34. CPageIni * ppageSystemIni = NULL;
  35. CPageGeneral * ppageGeneral = NULL;
  36. //-----------------------------------------------------------------------------
  37. // Other globals.
  38. //-----------------------------------------------------------------------------
  39. CMSConfigSheet * pMSConfigSheet = NULL; // global pointer to the property sheet
  40. /////////////////////////////////////////////////////////////////////////////
  41. // CMSConfigApp
  42. BEGIN_MESSAGE_MAP(CMSConfigApp, CWinApp)
  43. //{{AFX_MSG_MAP(CMSConfigApp)
  44. // NOTE - the ClassWizard will add and remove mapping macros here.
  45. // DO NOT EDIT what you see in these blocks of generated code!
  46. //}}AFX_MSG
  47. ON_COMMAND(ID_HELP, CWinApp::OnHelp)
  48. END_MESSAGE_MAP()
  49. //-----------------------------------------------------------------------------
  50. // Constructor. Nothing important here.
  51. //-----------------------------------------------------------------------------
  52. CMSConfigApp::CMSConfigApp()
  53. {
  54. }
  55. CMSConfigApp theApp;
  56. //-----------------------------------------------------------------------------
  57. // InitInstance is where we create the property sheet and show it (assuming
  58. // there isn't a command line flag to do otherwise).
  59. //-----------------------------------------------------------------------------
  60. BOOL fBasicControls = FALSE; // hide any advanced controls if true
  61. BOOL CMSConfigApp::InitInstance()
  62. {
  63. if (!InitATL())
  64. return FALSE;
  65. AfxEnableControlContainer();
  66. CCommandLineInfo cmdInfo;
  67. ParseCommandLine(cmdInfo);
  68. if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
  69. {
  70. return TRUE;
  71. }
  72. // If this is not the first instance, exit. The call to FirstInstance
  73. // will activate the previous instance.
  74. if (!FirstInstance())
  75. return FALSE;
  76. // Standard initialization
  77. // If you are not using these features and wish to reduce the size
  78. // of your final executable, you should remove from the following
  79. // the specific initialization routines you do not need.
  80. #ifdef _AFXDLL
  81. Enable3dControls(); // Call this when using MFC in a shared DLL
  82. #else
  83. Enable3dControlsStatic(); // Call this when linking to MFC statically
  84. #endif
  85. // Process the command line to see if one of the following flags have been set:
  86. //
  87. // /n (where n is a number) startup showing the nth tab
  88. // /basic hide advanced features
  89. // /commit n make changes from tab number n permanent
  90. // /auto show the automatic launch dialog
  91. int nInitialTab = 0;
  92. int nCommitTab = 0;
  93. BOOL fShowAutoDialog = FALSE;
  94. CString strCommandLine(m_lpCmdLine);
  95. CString strFlag, strTemp;
  96. strCommandLine.MakeLower();
  97. while (!strCommandLine.IsEmpty())
  98. {
  99. // Get the next flag from the command line (starting at a / or -,
  100. // and containing the text to the end of the string or the next
  101. // instance of a / or -).
  102. int iFlag = strCommandLine.FindOneOf(_T("/-"));
  103. if (iFlag == -1)
  104. break;
  105. strFlag = strCommandLine.Mid(iFlag + 1);
  106. strFlag = strFlag.SpanExcluding(_T("/-"));
  107. strCommandLine = strCommandLine.Mid(iFlag + 1 + strFlag.GetLength());
  108. strFlag.TrimRight();
  109. // Check for the /auto flag.
  110. if (strFlag.Find(_T("auto")) == 0)
  111. fShowAutoDialog = TRUE;
  112. // Check for the "/basic" flag.
  113. if (strFlag.Compare(_T("basic")) == 0)
  114. {
  115. fBasicControls = TRUE;
  116. continue;
  117. }
  118. // Check for the "/commit n" flag.
  119. if (strFlag.Left(6) == CString(_T("commit")))
  120. {
  121. // Find out which tab number to commit. Skip all of the
  122. // non-numeric characters.
  123. strTemp = strFlag.SpanExcluding(_T("0123456789"));
  124. if (strTemp.GetLength() < strFlag.GetLength())
  125. {
  126. strFlag = strFlag.Mid(strTemp.GetLength());
  127. if (!strFlag.IsEmpty())
  128. {
  129. TCHAR c = strFlag[0];
  130. if (_istdigit(c))
  131. nCommitTab = _ttoi((LPCTSTR)strFlag);
  132. }
  133. }
  134. continue;
  135. }
  136. // Finally, check for the "/n" flag, where n is the number of
  137. // the tab to initially display.
  138. if (strFlag.GetLength() == 1)
  139. {
  140. TCHAR c = strFlag[0];
  141. if (_istdigit(c))
  142. nInitialTab = _ttoi((LPCTSTR)strFlag);
  143. }
  144. }
  145. // Show the automatic launch dialog. The user may make settings in this
  146. // dialog that will keep MSConfig from automatically launching.
  147. if (fShowAutoDialog)
  148. {
  149. CAutoStartDlg dlg;
  150. dlg.DoModal();
  151. if (dlg.m_checkDontShow)
  152. {
  153. SetAutoRun(FALSE);
  154. return FALSE;
  155. }
  156. }
  157. // Check to see if the user is going to be able to make changes using MSConfig
  158. // (if not an admin, probably not). If the user doesn't have the necessary
  159. // privileges, don't run. Bug 475796.
  160. BOOL fModifyServices = FALSE, fModifyRegistry = FALSE;
  161. // Check to see if the user will be able to modify services.
  162. SC_HANDLE sch = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  163. if (sch != NULL)
  164. {
  165. fModifyServices = TRUE;
  166. ::CloseServiceHandle(sch);
  167. }
  168. // Check to see if the user can modify the registry.
  169. HKEY hkey;
  170. if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools"), 0, KEY_WRITE, &hkey))
  171. {
  172. fModifyRegistry = TRUE;
  173. ::RegCloseKey(hkey);
  174. }
  175. // If the user can't do both actions, exit now.
  176. if (!fModifyServices || !fModifyRegistry)
  177. {
  178. CString strText, strCaption;
  179. strCaption.LoadString(IDS_DIALOGCAPTION);
  180. strText.LoadString(IDS_NOPERMISSIONS);
  181. ::MessageBox(NULL, strText, strCaption, MB_OK | MB_ICONSTOP);
  182. return FALSE;
  183. }
  184. // This will load all the pages.
  185. BOOL fNeedsReboot = FALSE;
  186. InitializePages();
  187. // If the command line specifies to commit a change, we won't
  188. // show the dialog.
  189. if (nCommitTab > 1) // ignore zero (no flag) and one (general tab)
  190. {
  191. CPageBase * pPage = NULL;
  192. CString strTabCaption;
  193. if (NULL == ppageBootIni && nCommitTab >= 4)
  194. nCommitTab += 1; // adjust tab number if there is no BOOT.INI tab
  195. switch (nCommitTab)
  196. {
  197. case 2:
  198. pPage = dynamic_cast<CPageBase *>(ppageSystemIni);
  199. strTabCaption.LoadString(IDS_SYSTEMINI_CAPTION);
  200. break;
  201. case 3:
  202. pPage = dynamic_cast<CPageBase *>(ppageWinIni);
  203. strTabCaption.LoadString(IDS_WININI_CAPTION);
  204. break;
  205. case 4:
  206. pPage = dynamic_cast<CPageBase *>(ppageBootIni);
  207. strTabCaption.LoadString(IDS_BOOTINI_CAPTION);
  208. break;
  209. case 5:
  210. pPage = dynamic_cast<CPageBase *>(ppageServices);
  211. strTabCaption.LoadString(IDS_SERVICES_CAPTION);
  212. break;
  213. case 6:
  214. pPage = dynamic_cast<CPageBase *>(ppageStartup);
  215. strTabCaption.LoadString(IDS_STARTUP_CAPTION);
  216. break;
  217. }
  218. if (pPage)
  219. {
  220. CString strText, strCaption;
  221. strCaption.LoadString(IDS_DIALOGCAPTION);
  222. strText.Format(IDS_COMMITMESSAGE, strTabCaption);
  223. if (IDYES == ::MessageBox(NULL, strText, strCaption, MB_YESNO))
  224. pPage->CommitChanges();
  225. }
  226. }
  227. else
  228. fNeedsReboot = ShowPropertySheet(nInitialTab);
  229. CleanupPages();
  230. if (fNeedsReboot)
  231. Reboot();
  232. // Since the dialog has been closed, return FALSE so that we exit the
  233. // application, rather than start the application's message pump.
  234. return FALSE;
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Create all of the property pages. This function also contains the logic to
  238. // exclude property pages under certain circumstances (for example, if there
  239. // is no BOOT.INI file, don't create that page). TBD.
  240. //-----------------------------------------------------------------------------
  241. void CMSConfigApp::InitializePages()
  242. {
  243. // The boot.ini tab shouldn't be added if the file doesn't exist (for
  244. // instance, on Win64).
  245. CString strBootINI(_T("c:\\boot.ini"));
  246. // Check the registry for a testing flag (which would mean we aren't
  247. // operating on the real BOOT.INI file).
  248. CRegKey regkey;
  249. if (ERROR_SUCCESS == regkey.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools\\MSConfig")))
  250. {
  251. TCHAR szBoot[MAX_PATH];
  252. DWORD dwCount = MAX_PATH;
  253. if (ERROR_SUCCESS == regkey.QueryValue(szBoot, _T("boot.ini"), &dwCount))
  254. strBootINI = szBoot;
  255. }
  256. if (FileExists(strBootINI))
  257. ppageBootIni = new CPageBootIni;
  258. else
  259. ppageBootIni = NULL;
  260. ppageServices = new CPageServices;
  261. ppageStartup = new CPageStartup;
  262. ppageWinIni = new CPageIni;
  263. ppageSystemIni = new CPageIni;
  264. ppageGeneral = new CPageGeneral;
  265. ppageWinIni->SetTabInfo(_T("win.ini"));
  266. ppageSystemIni->SetTabInfo(_T("system.ini"));
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Show the MSConfig property sheet. This function returns whether or not
  270. // the computer should be rebooted.
  271. //-----------------------------------------------------------------------------
  272. BOOL CMSConfigApp::ShowPropertySheet(int nInitialTab)
  273. {
  274. CMSConfigSheet sheet(IDS_DIALOGCAPTION, NULL, (nInitialTab > 0) ? nInitialTab - 1 : 0);
  275. // Add each of the pages to the property sheet.
  276. if (ppageGeneral) sheet.AddPage(ppageGeneral);
  277. if (ppageSystemIni) sheet.AddPage(ppageSystemIni);
  278. if (ppageWinIni) sheet.AddPage(ppageWinIni);
  279. if (ppageBootIni) sheet.AddPage(ppageBootIni);
  280. if (ppageServices) sheet.AddPage(ppageServices);
  281. if (ppageStartup) sheet.AddPage(ppageStartup);
  282. // Show the property sheet.
  283. pMSConfigSheet = &sheet;
  284. INT_PTR iReturn = sheet.DoModal();
  285. pMSConfigSheet = NULL;
  286. // Possibly set MSConfig to automatically run on boot, and
  287. // check to see if we need to restart.
  288. BOOL fRunMSConfigOnBoot = FALSE;
  289. BOOL fNeedToRestart = FALSE;
  290. CPageBase * apPages[5] =
  291. {
  292. ppageSystemIni,
  293. ppageWinIni,
  294. ppageBootIni,
  295. ppageServices,
  296. ppageStartup
  297. };
  298. for (int nPage = 0; nPage < 5; nPage++)
  299. if (apPages[nPage])
  300. {
  301. fRunMSConfigOnBoot |= (CPageBase::NORMAL != apPages[nPage]->GetAppliedTabState());
  302. fNeedToRestart |= apPages[nPage]->HasAppliedChanges();
  303. if (fRunMSConfigOnBoot && fNeedToRestart)
  304. break;
  305. }
  306. // If the user didn't click CANCEL, or the user applied a change, then
  307. // we should set whether or not MSConfig needs to automatically run on boot.
  308. if (fNeedToRestart || iReturn != IDCANCEL)
  309. SetAutoRun(fRunMSConfigOnBoot);
  310. return (fNeedToRestart);
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Cleanup the global property pages.
  314. //-----------------------------------------------------------------------------
  315. void CMSConfigApp::CleanupPages()
  316. {
  317. if (ppageGeneral) delete ppageGeneral;
  318. if (ppageSystemIni) delete ppageSystemIni;
  319. if (ppageWinIni) delete ppageWinIni;
  320. if (ppageBootIni) delete ppageBootIni;
  321. if (ppageServices) delete ppageServices;
  322. if (ppageStartup) delete ppageStartup;
  323. }
  324. //-------------------------------------------------------------------------
  325. // This function will set the appropriate registry key to make msconfig run
  326. // on system start.
  327. //-------------------------------------------------------------------------
  328. void CMSConfigApp::SetAutoRun(BOOL fAutoRun)
  329. {
  330. LPCTSTR szRegKey = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
  331. LPCTSTR szRegVal = _T("MSConfig");
  332. CRegKey regkey;
  333. if (ERROR_SUCCESS != regkey.Open(HKEY_LOCAL_MACHINE, szRegKey))
  334. return;
  335. if (fAutoRun)
  336. {
  337. TCHAR szModulePath[MAX_PATH + 1];
  338. DWORD dwLength = ::GetModuleFileName(::GetModuleHandle(NULL), szModulePath, MAX_PATH);
  339. if (dwLength == 0)
  340. return;
  341. szModulePath[dwLength] = _T('\0');
  342. if (!FileExists(szModulePath))
  343. return;
  344. CString strNewVal = CString(_T("\"")) + CString(szModulePath) + CString(_T("\" ")) + CString(COMMANDLINE_AUTO);
  345. regkey.SetValue(strNewVal, szRegVal);
  346. }
  347. else
  348. regkey.DeleteValue(szRegVal);
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Not much explanation needed here. The user is given an option to not
  352. // restart the system.
  353. //-----------------------------------------------------------------------------
  354. void CMSConfigApp::Reboot()
  355. {
  356. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
  357. if (hProcess)
  358. {
  359. HANDLE hToken;
  360. if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
  361. {
  362. TOKEN_PRIVILEGES tp;
  363. tp.PrivilegeCount = 1;
  364. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  365. if (LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tp.Privileges[0].Luid) &&
  366. AdjustTokenPrivileges(hToken, FALSE, &tp, NULL, NULL, NULL) &&
  367. (GetLastError() == ERROR_SUCCESS))
  368. {
  369. CRebootDlg dlg;
  370. if (dlg.DoModal() == IDOK)
  371. ExitWindowsEx(EWX_REBOOT, 0);
  372. }
  373. else
  374. Message(IDS_USERSHOULDRESTART);
  375. }
  376. CloseHandle(hProcess);
  377. }
  378. }
  379. CMSConfigModule _Module;
  380. BEGIN_OBJECT_MAP(ObjectMap)
  381. END_OBJECT_MAP()
  382. LONG CMSConfigModule::Unlock()
  383. {
  384. AfxOleUnlockApp();
  385. return 0;
  386. }
  387. LONG CMSConfigModule::Lock()
  388. {
  389. AfxOleLockApp();
  390. return 1;
  391. }
  392. LPCTSTR CMSConfigModule::FindOneOf(LPCTSTR p1, LPCTSTR p2)
  393. {
  394. while (*p1 != NULL)
  395. {
  396. LPCTSTR p = p2;
  397. while (*p != NULL)
  398. {
  399. if (*p1 == *p)
  400. return CharNext(p1);
  401. p = CharNext(p);
  402. }
  403. p1++;
  404. }
  405. return NULL;
  406. }
  407. int CMSConfigApp::ExitInstance()
  408. {
  409. if (m_bATLInited)
  410. {
  411. _Module.RevokeClassObjects();
  412. _Module.Term();
  413. CoUninitialize();
  414. }
  415. return CWinApp::ExitInstance();
  416. }
  417. BOOL CMSConfigApp::InitATL()
  418. {
  419. m_bATLInited = TRUE;
  420. #if _WIN32_WINNT >= 0x0400
  421. HRESULT hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  422. #else
  423. HRESULT hRes = CoInitialize(NULL);
  424. #endif
  425. if (FAILED(hRes))
  426. {
  427. m_bATLInited = FALSE;
  428. return FALSE;
  429. }
  430. _Module.Init(ObjectMap, AfxGetInstanceHandle());
  431. _Module.dwThreadID = GetCurrentThreadId();
  432. LPTSTR lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
  433. TCHAR szTokens[] = _T("-/");
  434. BOOL bRun = TRUE;
  435. LPCTSTR lpszToken = _Module.FindOneOf(lpCmdLine, szTokens);
  436. while (lpszToken != NULL)
  437. {
  438. if (lstrcmpi(lpszToken, _T("UnregServer"))==0)
  439. {
  440. _Module.UpdateRegistryFromResource(IDR_MSCONFIG, FALSE);
  441. _Module.UnregisterServer(TRUE); //TRUE means typelib is unreg'd
  442. bRun = FALSE;
  443. break;
  444. }
  445. if (lstrcmpi(lpszToken, _T("RegServer"))==0)
  446. {
  447. _Module.UpdateRegistryFromResource(IDR_MSCONFIG, TRUE);
  448. _Module.RegisterServer(TRUE);
  449. bRun = FALSE;
  450. break;
  451. }
  452. lpszToken = _Module.FindOneOf(lpszToken, szTokens);
  453. }
  454. if (!bRun)
  455. {
  456. m_bATLInited = FALSE;
  457. _Module.Term();
  458. CoUninitialize();
  459. return FALSE;
  460. }
  461. hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
  462. REGCLS_MULTIPLEUSE);
  463. if (FAILED(hRes))
  464. {
  465. m_bATLInited = FALSE;
  466. CoUninitialize();
  467. return FALSE;
  468. }
  469. return TRUE;
  470. }
  471. //=============================================================================
  472. // Implement the utility functions described in msconfigstate.h
  473. //=============================================================================
  474. void Message(LPCTSTR szMessage, HWND hwndParent)
  475. {
  476. CString strCaption;
  477. strCaption.LoadString(IDS_APPCAPTION);
  478. if (hwndParent != NULL || pMSConfigSheet == NULL)
  479. ::MessageBox(hwndParent, szMessage, strCaption, MB_OK);
  480. else
  481. ::MessageBox(pMSConfigSheet->GetSafeHwnd(), szMessage, strCaption, MB_OK);
  482. }
  483. void Message(UINT uiMessage, HWND hwndParent)
  484. {
  485. CString strMessage;
  486. strMessage.LoadString(uiMessage);
  487. Message((LPCTSTR)strMessage, hwndParent);
  488. }
  489. HKEY GetRegKey(LPCTSTR szSubKey)
  490. {
  491. LPCTSTR szMSConfigKey = _T("SOFTWARE\\Microsoft\\Shared Tools\\MSConfig");
  492. CString strKey(szMSConfigKey);
  493. HKEY hkey = NULL;
  494. // Try to open the base MSConfig key. If it succeeds, and there is no
  495. // subkey to open, return the HKEY. Otherwise, we need to create the
  496. // base key.
  497. if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMSConfigKey, 0, KEY_ALL_ACCESS, &hkey))
  498. {
  499. if (szSubKey == NULL)
  500. return hkey;
  501. ::RegCloseKey(hkey);
  502. }
  503. else
  504. {
  505. // Create the MSConfig key (and close it).
  506. HKEY hkeyBase;
  507. if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools"), 0, KEY_ALL_ACCESS, &hkeyBase))
  508. {
  509. if (ERROR_SUCCESS == RegCreateKeyEx(hkeyBase, _T("MSConfig"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
  510. ::RegCloseKey(hkey);
  511. ::RegCloseKey(hkeyBase);
  512. }
  513. }
  514. if (szSubKey)
  515. strKey += CString(_T("\\")) + CString(szSubKey);
  516. if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKey, 0, KEY_ALL_ACCESS, &hkey))
  517. {
  518. // If we couldn't open the subkey, then we should try to create it.
  519. if (szSubKey)
  520. {
  521. HKEY hkeyBase;
  522. if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, szMSConfigKey, 0, KEY_ALL_ACCESS, &hkeyBase))
  523. {
  524. if (ERROR_SUCCESS != RegCreateKeyEx(hkeyBase, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
  525. hkey = NULL;
  526. ::RegCloseKey(hkeyBase);
  527. }
  528. }
  529. }
  530. return hkey;
  531. }
  532. HRESULT BackupFile(LPCTSTR szFilename, const CString & strAddedExtension, BOOL fOverwrite)
  533. {
  534. CString strFrom(szFilename);
  535. CString strTo(GetBackupName(szFilename, strAddedExtension));
  536. if (!fOverwrite && FileExists(strTo))
  537. return S_FALSE;
  538. ::SetFileAttributes(strTo, FILE_ATTRIBUTE_NORMAL);
  539. if (::CopyFile(strFrom, strTo, FALSE))
  540. {
  541. ::SetFileAttributes(strTo, FILE_ATTRIBUTE_NORMAL);
  542. return S_OK;
  543. }
  544. return E_FAIL;
  545. }
  546. CString strBackupDir; // global string containing the path of the backup directory
  547. const CString GetBackupName(LPCTSTR szFilename, const CString & strAddedExtension)
  548. {
  549. // There should be a directory for MSConfig files. Make sure it exists
  550. // (create it if it doesn't).
  551. if (strBackupDir.IsEmpty())
  552. {
  553. TCHAR szMSConfigDir[MAX_PATH];
  554. if (MAX_PATH > ::ExpandEnvironmentStrings(MSCONFIGDIR, szMSConfigDir, MAX_PATH))
  555. {
  556. strBackupDir = szMSConfigDir;
  557. if (!FileExists(strBackupDir))
  558. ::CreateDirectory(strBackupDir, NULL);
  559. }
  560. }
  561. CString strFrom(szFilename);
  562. int i = strFrom.ReverseFind(_T('\\'));
  563. CString strFile(strFrom.Mid(i + 1));
  564. CString strTo(strBackupDir + _T("\\") + strFile + strAddedExtension);
  565. return strTo;
  566. }
  567. HRESULT RestoreFile(LPCTSTR szFilename, const CString & strAddedExtension, BOOL fOverwrite)
  568. {
  569. CString strTo(szFilename);
  570. int i = strTo.ReverseFind(_T('\\'));
  571. CString strFile(strTo.Mid(i + 1));
  572. CString strFrom(strBackupDir + _T("\\") + strFile + strAddedExtension);
  573. if (!fOverwrite && FileExists(strTo))
  574. return S_FALSE;
  575. DWORD dwAttributes = ::GetFileAttributes(strTo);
  576. ::SetFileAttributes(strTo, FILE_ATTRIBUTE_NORMAL);
  577. if (::CopyFile(strFrom, strTo, FALSE))
  578. {
  579. ::SetFileAttributes(strTo, dwAttributes);
  580. return S_OK;
  581. }
  582. return E_FAIL;
  583. }