Source code of Windows XP (NT5)
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.

1086 lines
33 KiB

  1. #include "stdafx.h"
  2. #include "PageBootIni.h"
  3. #include "MSConfigState.h"
  4. #include "BootAdv.h"
  5. #ifdef _DEBUG
  6. #define new DEBUG_NEW
  7. #undef THIS_FILE
  8. static char THIS_FILE[] = __FILE__;
  9. #endif
  10. /////////////////////////////////////////////////////////////////////////////
  11. // CPageBootIni property page
  12. IMPLEMENT_DYNCREATE(CPageBootIni, CPropertyPage)
  13. CPageBootIni::CPageBootIni() : CPropertyPage(CPageBootIni::IDD)
  14. {
  15. //{{AFX_DATA_INIT(CPageBootIni)
  16. // NOTE: the ClassWizard will add member initialization here
  17. //}}AFX_DATA_INIT
  18. m_fIgnoreEdit = FALSE;
  19. m_strFileName = BOOT_INI;
  20. }
  21. CPageBootIni::~CPageBootIni()
  22. {
  23. }
  24. void CPageBootIni::DoDataExchange(CDataExchange* pDX)
  25. {
  26. CPropertyPage::DoDataExchange(pDX);
  27. //{{AFX_DATA_MAP(CPageBootIni)
  28. // NOTE: the ClassWizard will add DDX and DDV calls here
  29. //}}AFX_DATA_MAP
  30. }
  31. BEGIN_MESSAGE_MAP(CPageBootIni, CPropertyPage)
  32. //{{AFX_MSG_MAP(CPageBootIni)
  33. ON_BN_CLICKED(IDC_BOOTMOVEDOWN, OnBootMoveDown)
  34. ON_BN_CLICKED(IDC_BOOTMOVEUP, OnBootMoveUp)
  35. ON_LBN_SELCHANGE(IDC_LISTBOOTINI, OnSelChangeList)
  36. ON_BN_CLICKED(IDC_BASEVIDEO, OnClickedBase)
  37. ON_BN_CLICKED(IDC_BOOTLOG, OnClickedBootLog)
  38. ON_BN_CLICKED(IDC_NOGUIBOOT, OnClickedNoGUIBoot)
  39. ON_BN_CLICKED(IDC_SOS, OnClickedSOS)
  40. ON_BN_CLICKED(IDC_SAFEBOOT, OnClickedSafeBoot)
  41. ON_BN_CLICKED(IDC_SBDSREPAIR, OnClickedSBDSRepair)
  42. ON_BN_CLICKED(IDC_SBMINIMAL, OnClickedSBMinimal)
  43. ON_BN_CLICKED(IDC_SBMINIMALALT, OnClickedSBMinimalAlt)
  44. ON_BN_CLICKED(IDC_SBNETWORK, OnClickedSBNetwork)
  45. ON_EN_CHANGE(IDC_EDITTIMEOUT, OnChangeEditTimeOut)
  46. ON_EN_KILLFOCUS(IDC_EDITTIMEOUT, OnKillFocusEditTimeOut)
  47. ON_BN_CLICKED(IDC_BOOTADVANCED, OnClickedBootAdvanced)
  48. ON_BN_CLICKED(IDC_SETASDEFAULT, OnClickedSetAsDefault)
  49. ON_BN_CLICKED(IDC_CHECKBOOTPATHS, OnClickedCheckBootPaths)
  50. ON_WM_DESTROY()
  51. //}}AFX_MSG_MAP
  52. END_MESSAGE_MAP()
  53. /////////////////////////////////////////////////////////////////////////////
  54. // CPageBootIni message handlers
  55. //-------------------------------------------------------------------------
  56. // Initialize this page by reading the contents of the boot.ini file.
  57. //-------------------------------------------------------------------------
  58. void CPageBootIni::InitializePage()
  59. {
  60. if (LoadBootIni())
  61. {
  62. SyncControlsToIni();
  63. if (m_nMinOSIndex != -1)
  64. {
  65. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMinOSIndex, 0);
  66. SelectLine(m_nMinOSIndex);
  67. }
  68. }
  69. m_stateCurrent = CPageBase::GetAppliedTabState();
  70. }
  71. //-------------------------------------------------------------------------
  72. // Load the contents of the BOOT.INI file into our local structures.
  73. //-------------------------------------------------------------------------
  74. BOOL CPageBootIni::LoadBootIni(CString strFileName)
  75. {
  76. if (strFileName.IsEmpty())
  77. strFileName = m_strFileName;
  78. // Read the contents of the boot.ini file into a string.
  79. HANDLE h = ::CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  80. if (INVALID_HANDLE_VALUE == h)
  81. return FALSE;
  82. CString strContents;
  83. DWORD dwNumberBytesRead, dwNumberBytesToRead = ::GetFileSize(h, NULL);
  84. // The BOOT.INI file is ANSI, so we should read it and convert to Unicode.
  85. char * szBuffer = new char[dwNumberBytesToRead + 1];
  86. ::ZeroMemory((PVOID)szBuffer, dwNumberBytesToRead + 1);
  87. if (!::ReadFile(h, (LPVOID)szBuffer, dwNumberBytesToRead, &dwNumberBytesRead, NULL))
  88. *szBuffer = _T('\0');
  89. ::CloseHandle(h);
  90. // Do the conversion.
  91. USES_CONVERSION;
  92. LPTSTR szConverted = A2T(szBuffer);
  93. strContents = szConverted;
  94. delete [] szBuffer;
  95. if (dwNumberBytesToRead != dwNumberBytesRead || strContents.IsEmpty())
  96. return FALSE;
  97. // Save the original contents of the file.
  98. m_strOriginalContents = strContents;
  99. // Parse the contents of the string into an array of strings (one for each line
  100. // of the file).
  101. m_arrayIniLines.RemoveAll();
  102. m_arrayIniLines.SetSize(10, 10);
  103. CString strLine;
  104. int nIndex = 0;
  105. while (!strContents.IsEmpty())
  106. {
  107. strLine = strContents.SpanExcluding(_T("\r\n"));
  108. if (!strLine.IsEmpty())
  109. {
  110. m_arrayIniLines.SetAtGrow(nIndex, strLine);
  111. nIndex += 1;
  112. }
  113. strContents = strContents.Mid(strLine.GetLength());
  114. strContents.TrimLeft(_T("\r\n"));
  115. }
  116. // Look through the lines read from the INI file, searching for particular
  117. // ones we'll want to make a note of.
  118. m_nTimeoutIndex = m_nDefaultIndex = m_nMinOSIndex = m_nMaxOSIndex = -1;
  119. for (int i = 0; i <= m_arrayIniLines.GetUpperBound(); i++)
  120. {
  121. CString strScanLine = m_arrayIniLines[i];
  122. strScanLine.MakeLower();
  123. strScanLine.Replace(_T(" "), _T(""));
  124. if (strScanLine.Find(_T("timeout=")) != -1)
  125. m_nTimeoutIndex = i;
  126. else if (strScanLine.Find(_T("default=")) != -1)
  127. m_nDefaultIndex = i;
  128. if (m_nMinOSIndex != -1 && m_nMaxOSIndex == -1 && (strScanLine.IsEmpty() || strScanLine[0] == _T('[')))
  129. m_nMaxOSIndex = i - 1;
  130. else if (strScanLine.Find(_T("[operatingsystems]")) != -1)
  131. m_nMinOSIndex = i + 1;
  132. }
  133. if (m_nMinOSIndex != -1 && m_nMaxOSIndex == -1)
  134. m_nMaxOSIndex = i - 1;
  135. return TRUE;
  136. }
  137. //----------------------------------------------------------------------------
  138. // Update the state of the controls on this tab to match the contents of the
  139. // internal representation of the INI file.
  140. //----------------------------------------------------------------------------
  141. void CPageBootIni::SyncControlsToIni(BOOL fSyncEditField)
  142. {
  143. // We need to keep track of the extent of the strings in the list box
  144. // (to handle a horizontal scroll bar). Code from MSDN.
  145. DWORD dwExtent, dwMaxExtent = 0;
  146. TEXTMETRIC tm;
  147. HDC hDCListBox = ::GetDC(GetDlgItemHWND(IDC_LISTBOOTINI));
  148. HFONT hFontNew = (HFONT)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), WM_GETFONT, NULL, NULL);
  149. HFONT hFontOld = (HFONT)::SelectObject(hDCListBox, hFontNew);
  150. ::GetTextMetrics(hDCListBox, (LPTEXTMETRIC)&tm);
  151. CDC dc;
  152. dc.Attach(hDCListBox);
  153. for (int i = 0; i <= m_arrayIniLines.GetUpperBound(); i++)
  154. if (!m_arrayIniLines[i].IsEmpty())
  155. {
  156. CSize size = dc.GetTextExtent(m_arrayIniLines[i]);
  157. dwExtent = size.cx + tm.tmAveCharWidth;
  158. if (dwExtent > dwMaxExtent)
  159. dwMaxExtent = dwExtent;
  160. }
  161. dc.Detach();
  162. ::SelectObject(hDCListBox, hFontOld);
  163. ::ReleaseDC(GetDlgItemHWND(IDC_LISTBOOTINI), hDCListBox);
  164. // Set the extent for the list box.
  165. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETHORIZONTALEXTENT, (WPARAM)dwMaxExtent, 0);
  166. // First, add the lines from the boot ini into the list control.
  167. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_RESETCONTENT, 0, 0);
  168. for (int j = 0; j <= m_arrayIniLines.GetUpperBound(); j++)
  169. if (!m_arrayIniLines[j].IsEmpty())
  170. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)m_arrayIniLines[j]);
  171. // Set the timeout value based on the boot.ini.
  172. if (m_nTimeoutIndex != -1 && fSyncEditField)
  173. {
  174. CString strTimeout = m_arrayIniLines[m_nTimeoutIndex];
  175. strTimeout.TrimLeft(_T("timeout= "));
  176. m_fIgnoreEdit = TRUE;
  177. SetDlgItemText(IDC_EDITTIMEOUT, strTimeout);
  178. m_fIgnoreEdit = FALSE;
  179. }
  180. }
  181. //----------------------------------------------------------------------------
  182. // Update the controls based on the user's selection of a line.
  183. //----------------------------------------------------------------------------
  184. void CPageBootIni::SelectLine(int index)
  185. {
  186. if (index < m_nMinOSIndex)
  187. {
  188. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMinOSIndex, 0);
  189. SelectLine(m_nMinOSIndex);
  190. return;
  191. }
  192. if (index > m_nMaxOSIndex)
  193. {
  194. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMaxOSIndex, 0);
  195. SelectLine(m_nMaxOSIndex);
  196. return;
  197. }
  198. HWND hwndFocus = ::GetFocus();
  199. ::EnableWindow(GetDlgItemHWND(IDC_BOOTMOVEUP), (index > m_nMinOSIndex));
  200. ::EnableWindow(GetDlgItemHWND(IDC_BOOTMOVEDOWN), (index < m_nMaxOSIndex));
  201. if ((index <= m_nMinOSIndex) && hwndFocus == GetDlgItemHWND(IDC_BOOTMOVEUP))
  202. NextDlgCtrl();
  203. if ((index >= m_nMaxOSIndex) && hwndFocus == GetDlgItemHWND(IDC_BOOTMOVEDOWN))
  204. PrevDlgCtrl();
  205. CString strOS = m_arrayIniLines[index];
  206. strOS.MakeLower();
  207. CheckDlgButton(IDC_SAFEBOOT, (strOS.Find(_T("/safeboot")) != -1));
  208. CheckDlgButton(IDC_NOGUIBOOT, (strOS.Find(_T("/noguiboot")) != -1));
  209. CheckDlgButton(IDC_BOOTLOG, (strOS.Find(_T("/bootlog")) != -1));
  210. CheckDlgButton(IDC_BASEVIDEO, (strOS.Find(_T("/basevideo")) != -1));
  211. CheckDlgButton(IDC_SOS, (strOS.Find(_T("/sos")) != -1));
  212. // If the line selected isn't for Whistler, then disable the controls.
  213. // If the line is for Whistler or W2K, but it has the string "CMDCONS" in
  214. // it, we shouldn't enable the controls.
  215. BOOL fEnableControls = ((strOS.Find(_T("whistler")) != -1) || (strOS.Find(_T("windows 2000")) != -1));
  216. fEnableControls |= ((strOS.Find(_T("windowsxp")) != -1) || (strOS.Find(_T("windows xp")) != -1) || (strOS.Find(_T("windows 2002")) != -1));
  217. fEnableControls = fEnableControls && (strOS.Find(_T("cmdcons")) == -1);
  218. ::EnableWindow(GetDlgItemHWND(IDC_SAFEBOOT), fEnableControls);
  219. ::EnableWindow(GetDlgItemHWND(IDC_NOGUIBOOT), fEnableControls);
  220. ::EnableWindow(GetDlgItemHWND(IDC_BOOTLOG), fEnableControls);
  221. ::EnableWindow(GetDlgItemHWND(IDC_BASEVIDEO), fEnableControls);
  222. ::EnableWindow(GetDlgItemHWND(IDC_SOS), fEnableControls);
  223. ::EnableWindow(GetDlgItemHWND(IDC_BOOTADVANCED), fEnableControls);
  224. BOOL fSafeboot = (strOS.Find(_T("/safeboot")) != -1);
  225. ::EnableWindow(GetDlgItemHWND(IDC_SBNETWORK), fSafeboot && fEnableControls);
  226. ::EnableWindow(GetDlgItemHWND(IDC_SBDSREPAIR), fSafeboot && fEnableControls);
  227. ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMAL), fSafeboot && fEnableControls);
  228. ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMALALT), fSafeboot && fEnableControls);
  229. if (fSafeboot)
  230. {
  231. CheckDlgButton(IDC_SBNETWORK, (strOS.Find(_T("/safeboot:network")) != -1));
  232. CheckDlgButton(IDC_SBDSREPAIR, (strOS.Find(_T("/safeboot:dsrepair")) != -1));
  233. if (strOS.Find(_T("/safeboot:minimal")) != -1)
  234. {
  235. BOOL fAlternateShell = (strOS.Find(_T("/safeboot:minimal(alternateshell)")) != -1);
  236. CheckDlgButton(IDC_SBMINIMAL, !fAlternateShell);
  237. CheckDlgButton(IDC_SBMINIMALALT, fAlternateShell);
  238. }
  239. else
  240. {
  241. CheckDlgButton(IDC_SBMINIMAL, FALSE);
  242. CheckDlgButton(IDC_SBMINIMALALT, FALSE);
  243. }
  244. int iSafeboot = strOS.Find(_T("/safeboot"));
  245. if (iSafeboot != -1)
  246. {
  247. m_strSafeBoot = strOS.Mid(iSafeboot + 1);
  248. m_strSafeBoot = m_strSafeBoot.SpanExcluding(_T(" /"));
  249. m_strSafeBoot = CString(_T("/")) + m_strSafeBoot;
  250. }
  251. }
  252. // Check to see if the selected operating system is the default.
  253. // Then enable the button accordingly.
  254. BOOL fEnableDefault = FALSE;
  255. if (m_nDefaultIndex >= 0)
  256. {
  257. CString strDefault = m_arrayIniLines[m_nDefaultIndex];
  258. int iEquals = strDefault.Find(_T('='));
  259. if (iEquals != -1)
  260. {
  261. strDefault = strDefault.Mid(iEquals + 1);
  262. strDefault.MakeLower();
  263. CString strCurrent = strOS.SpanExcluding(_T("="));
  264. strDefault.TrimLeft();
  265. strCurrent.TrimRight();
  266. if (strDefault != strCurrent || index > m_nMinOSIndex)
  267. fEnableDefault = TRUE;
  268. }
  269. }
  270. ::EnableWindow(GetDlgItemHWND(IDC_SETASDEFAULT), fEnableDefault);
  271. if (!fEnableDefault && hwndFocus == GetDlgItemHWND(IDC_SETASDEFAULT))
  272. NextDlgCtrl();
  273. }
  274. //-------------------------------------------------------------------------
  275. // Add or remove the specified flag from the currently selected OS line.
  276. //-------------------------------------------------------------------------
  277. void CPageBootIni::ChangeCurrentOSFlag(BOOL fAdd, LPCTSTR szFlag)
  278. {
  279. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  280. CString strFlagPlusSpace = CString(_T(" ")) + szFlag;
  281. CString strNewLine;
  282. if (fAdd)
  283. {
  284. if (m_arrayIniLines[iSelection].Find(szFlag) != -1)
  285. {
  286. ASSERT(0 && "the flag is already there");
  287. return;
  288. }
  289. strNewLine = m_arrayIniLines[iSelection] + strFlagPlusSpace;
  290. }
  291. else
  292. {
  293. int iIndex = m_arrayIniLines[iSelection].Find(strFlagPlusSpace);
  294. if (iIndex == -1)
  295. {
  296. ASSERT(0 && "there is no flag");
  297. return;
  298. }
  299. strNewLine = m_arrayIniLines[iSelection].Left(iIndex);
  300. strNewLine += m_arrayIniLines[iSelection].Mid(iIndex + strFlagPlusSpace.GetLength());
  301. }
  302. m_arrayIniLines.SetAt(iSelection, strNewLine);
  303. UserMadeChange();
  304. SyncControlsToIni();
  305. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0);
  306. }
  307. //-------------------------------------------------------------------------
  308. // Sets the "default=" line in the boot.ini.
  309. //-------------------------------------------------------------------------
  310. void CPageBootIni::SetDefaultOS(int iIndex)
  311. {
  312. if (m_nDefaultIndex == -1)
  313. return;
  314. // Get the current string "default=xxxx". Locate the location of the
  315. // '=' so we can replace the later half of the line.
  316. CString strDefault = m_arrayIniLines[m_nDefaultIndex];
  317. int iEquals = strDefault.Find(_T('='));
  318. if (iEquals == -1)
  319. return;
  320. CString strValue = m_arrayIniLines[iIndex].SpanExcluding(_T("="));
  321. strValue.TrimRight();
  322. CString strNewDefault = strDefault.Left(iEquals + 1) + strValue;
  323. m_arrayIniLines.SetAt(m_nDefaultIndex, strNewDefault);
  324. }
  325. //-------------------------------------------------------------------------
  326. // Write new contents to the BOOT.INI file.
  327. //-------------------------------------------------------------------------
  328. BOOL CPageBootIni::SetBootIniContents(const CString & strNewContents, const CString & strAddedExtension)
  329. {
  330. // Extra safety code.
  331. if ((LPCTSTR)strNewContents == NULL || *((LPCTSTR)strNewContents) == _T('\0'))
  332. return FALSE;
  333. // To write to the BOOT.INI file, we need to set it to have normal
  334. // attributes. Save the attribute settings so we can restore them.
  335. DWORD dwWritten, dwAttribs = ::GetFileAttributes(m_strFileName);
  336. ::SetFileAttributes(m_strFileName, FILE_ATTRIBUTE_NORMAL);
  337. HANDLE h = ::CreateFile(m_strFileName, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING, 0, NULL);
  338. if (INVALID_HANDLE_VALUE == h)
  339. {
  340. ::SetFileAttributes(m_strFileName, dwAttribs);
  341. return FALSE;
  342. }
  343. // Convert the internal BOOT.INI representation (Unicode) to ANSI for writing.
  344. USES_CONVERSION;
  345. LPSTR szBuffer = T2A((LPTSTR)(LPCTSTR)strNewContents);
  346. // CreateFile with TRUNCATE_EXISTING seems to SOMETIMES not set the file length to
  347. // zero, but to overwrite the existing file with zeroes and leave the pointer at
  348. // the end of the file.
  349. ::SetFilePointer(h, 0, NULL, FILE_BEGIN);
  350. ::WriteFile(h, (void *)szBuffer, strNewContents.GetLength(), &dwWritten, NULL);
  351. ::SetEndOfFile(h);
  352. ::CloseHandle(h);
  353. ::SetFileAttributes(m_strFileName, dwAttribs);
  354. return TRUE;
  355. }
  356. //-------------------------------------------------------------------------
  357. // We need to subclass the edit control to catch the enter key, so we
  358. // can validate the data and not close MSConfig.
  359. //-------------------------------------------------------------------------
  360. CPageBootIni * pBootIniPage = NULL; // pointer to the page, so we can call member functions
  361. WNDPROC pOldBootIniEditProc = NULL; // save old wndproc when we subclass edit control
  362. LRESULT BootIniEditSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
  363. {
  364. switch (wm)
  365. {
  366. case WM_GETDLGCODE:
  367. return DLGC_WANTALLKEYS;
  368. case WM_CHAR:
  369. if (wp == VK_ESCAPE || wp == VK_RETURN)
  370. {
  371. if (pBootIniPage != NULL)
  372. {
  373. pBootIniPage->NextDlgCtrl();
  374. return 0;
  375. }
  376. }
  377. else if (wp == VK_TAB)
  378. {
  379. if (pBootIniPage != NULL)
  380. {
  381. if (::GetAsyncKeyState(VK_SHIFT) == 0)
  382. pBootIniPage->NextDlgCtrl();
  383. else
  384. pBootIniPage->PrevDlgCtrl();
  385. return 0;
  386. }
  387. }
  388. break;
  389. }
  390. if (pOldBootIniEditProc != NULL) // better not be null
  391. return CallWindowProc(pOldBootIniEditProc, hwnd, wm, wp, lp);
  392. return 0;
  393. }
  394. //-------------------------------------------------------------------------
  395. // Initialize the boot.ini page. Read in the INI file, set up internal
  396. // structures to represent the file, and update the controls to reflect
  397. // the internal structures.
  398. //-------------------------------------------------------------------------
  399. extern BOOL fBasicControls;
  400. BOOL CPageBootIni::OnInitDialog()
  401. {
  402. CPropertyPage::OnInitDialog();
  403. // Check the registry for a testing flag (which would mean we aren't
  404. // operating on the real BOOT.INI file).
  405. CRegKey regkey;
  406. if (ERROR_SUCCESS == regkey.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools\\MSConfig")))
  407. {
  408. TCHAR szBoot[MAX_PATH];
  409. DWORD dwCount = MAX_PATH;
  410. if (ERROR_SUCCESS == regkey.QueryValue(szBoot, _T("boot.ini"), &dwCount))
  411. m_strFileName = szBoot;
  412. }
  413. InitializePage();
  414. if (fBasicControls)
  415. ::ShowWindow(GetDlgItemHWND(IDC_BOOTADVANCED), SW_HIDE);
  416. // Subclass the edit control (to catch the enter key).
  417. HWND hWndEdit = GetDlgItemHWND(IDC_EDITTIMEOUT);
  418. if (hWndEdit)
  419. {
  420. pOldBootIniEditProc = (WNDPROC)::GetWindowLongPtr(hWndEdit, GWLP_WNDPROC);
  421. pBootIniPage = this;
  422. ::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)&BootIniEditSubclassProc);
  423. }
  424. m_fInitialized = TRUE;
  425. return TRUE; // return TRUE unless you set the focus to a control
  426. }
  427. //-------------------------------------------------------------------------
  428. // Called when the user clicks move up or down.
  429. //-------------------------------------------------------------------------
  430. void CPageBootIni::OnBootMoveDown()
  431. {
  432. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  433. ASSERT(iSelection >= m_nMinOSIndex && iSelection < m_nMaxOSIndex);
  434. if (iSelection >= m_nMinOSIndex && iSelection < m_nMaxOSIndex)
  435. {
  436. CString strTemp = m_arrayIniLines[iSelection + 1];
  437. m_arrayIniLines.SetAt(iSelection + 1, m_arrayIniLines[iSelection]);
  438. m_arrayIniLines.SetAt(iSelection, strTemp);
  439. UserMadeChange();
  440. SyncControlsToIni();
  441. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection + 1, 0);
  442. SelectLine(iSelection + 1);
  443. }
  444. }
  445. void CPageBootIni::OnBootMoveUp()
  446. {
  447. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  448. ASSERT(iSelection > m_nMinOSIndex && iSelection <= m_nMaxOSIndex);
  449. if (iSelection > m_nMinOSIndex && iSelection <= m_nMaxOSIndex)
  450. {
  451. CString strTemp = m_arrayIniLines[iSelection - 1];
  452. m_arrayIniLines.SetAt(iSelection - 1, m_arrayIniLines[iSelection]);
  453. m_arrayIniLines.SetAt(iSelection, strTemp);
  454. UserMadeChange();
  455. SyncControlsToIni();
  456. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection - 1, 0);
  457. SelectLine(iSelection - 1);
  458. }
  459. }
  460. //-------------------------------------------------------------------------
  461. // Called when the user clicks on a line in the list view.
  462. //-------------------------------------------------------------------------
  463. void CPageBootIni::OnSelChangeList()
  464. {
  465. SelectLine((int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0));
  466. }
  467. //-------------------------------------------------------------------------
  468. // The check boxes are handled uniformly - adding or removing a flag from
  469. // the currently selected OS line.
  470. //-------------------------------------------------------------------------
  471. void CPageBootIni::OnClickedBase()
  472. {
  473. ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_BASEVIDEO), _T("/basevideo"));
  474. }
  475. void CPageBootIni::OnClickedBootLog()
  476. {
  477. ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_BOOTLOG), _T("/bootlog"));
  478. }
  479. void CPageBootIni::OnClickedNoGUIBoot()
  480. {
  481. ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_NOGUIBOOT), _T("/noguiboot"));
  482. }
  483. void CPageBootIni::OnClickedSOS()
  484. {
  485. ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_SOS), _T("/sos"));
  486. }
  487. //-------------------------------------------------------------------------
  488. // The safeboot flag is a little more complicated, since it has an extra
  489. // portion (from the radio buttons).
  490. //-------------------------------------------------------------------------
  491. void CPageBootIni::OnClickedSafeBoot()
  492. {
  493. CString strFlag(_T("/safeboot"));
  494. if (IsDlgButtonChecked(IDC_SBNETWORK))
  495. strFlag += _T(":network");
  496. else if (IsDlgButtonChecked(IDC_SBDSREPAIR))
  497. strFlag += _T(":dsrepair");
  498. else if (IsDlgButtonChecked(IDC_SBMINIMALALT))
  499. strFlag += _T(":minimal(alternateshell)");
  500. else
  501. {
  502. strFlag += _T(":minimal");
  503. CheckDlgButton(IDC_SBMINIMAL, 1);
  504. }
  505. BOOL fSafeBoot = IsDlgButtonChecked(IDC_SAFEBOOT);
  506. ChangeCurrentOSFlag(fSafeBoot, strFlag);
  507. m_strSafeBoot = strFlag;
  508. ::EnableWindow(GetDlgItemHWND(IDC_SBNETWORK), fSafeBoot);
  509. ::EnableWindow(GetDlgItemHWND(IDC_SBDSREPAIR), fSafeBoot);
  510. ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMAL), fSafeBoot);
  511. ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMALALT), fSafeBoot);
  512. }
  513. //-------------------------------------------------------------------------
  514. // Clicking on one of the safeboot radio buttons requires a little extra
  515. // processing, to remove the existing flag and add the new one.
  516. //-------------------------------------------------------------------------
  517. void CPageBootIni::OnClickedSBDSRepair()
  518. {
  519. ChangeCurrentOSFlag(FALSE, m_strSafeBoot);
  520. m_strSafeBoot = _T("/safeboot:dsrepair");
  521. ChangeCurrentOSFlag(TRUE, m_strSafeBoot);
  522. }
  523. void CPageBootIni::OnClickedSBMinimal()
  524. {
  525. ChangeCurrentOSFlag(FALSE, m_strSafeBoot);
  526. m_strSafeBoot = _T("/safeboot:minimal");
  527. ChangeCurrentOSFlag(TRUE, m_strSafeBoot);
  528. }
  529. void CPageBootIni::OnClickedSBMinimalAlt()
  530. {
  531. ChangeCurrentOSFlag(FALSE, m_strSafeBoot);
  532. m_strSafeBoot = _T("/safeboot:minimal(alternateshell)");
  533. ChangeCurrentOSFlag(TRUE, m_strSafeBoot);
  534. }
  535. void CPageBootIni::OnClickedSBNetwork()
  536. {
  537. ChangeCurrentOSFlag(FALSE, m_strSafeBoot);
  538. m_strSafeBoot = _T("/safeboot:network");
  539. ChangeCurrentOSFlag(TRUE, m_strSafeBoot);
  540. }
  541. //-------------------------------------------------------------------------
  542. // As the user enters text in the timeout field, update the line in the
  543. // ini file list box.
  544. //-------------------------------------------------------------------------
  545. void CPageBootIni::OnChangeEditTimeOut()
  546. {
  547. if (m_fIgnoreEdit)
  548. return;
  549. if (m_nTimeoutIndex == -1)
  550. return;
  551. CString strTimeout = m_arrayIniLines[m_nTimeoutIndex];
  552. int iEquals = strTimeout.Find(_T('='));
  553. if (iEquals == -1)
  554. return;
  555. while (strTimeout[iEquals + 1] == _T(' ') && (iEquals + 1) < strTimeout.GetLength())
  556. iEquals++;
  557. TCHAR szValue[MAX_PATH];
  558. GetDlgItemText(IDC_EDITTIMEOUT, szValue, MAX_PATH);
  559. CString strNewTimeout = strTimeout.Left(iEquals + 1) + szValue;
  560. m_arrayIniLines.SetAt(m_nTimeoutIndex, strNewTimeout);
  561. UserMadeChange();
  562. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  563. SyncControlsToIni(FALSE);
  564. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0);
  565. }
  566. void CPageBootIni::OnKillFocusEditTimeOut()
  567. {
  568. TCHAR szValue[MAX_PATH];
  569. GetDlgItemText(IDC_EDITTIMEOUT, szValue, MAX_PATH);
  570. CString strNewValue(_T(""));
  571. BOOL fGiveUpFocus = FALSE;
  572. int iTimeout = _ttoi(szValue);
  573. if (iTimeout < 3 || iTimeout > 999)
  574. {
  575. CString strMessage, strCaption;
  576. strMessage.LoadString(IDS_TIMEOUTVALUE);
  577. strCaption.LoadString(IDS_APPCAPTION);
  578. MessageBox(strMessage, strCaption);
  579. if (iTimeout < 3)
  580. strNewValue = _T("3");
  581. else if (iTimeout > 999)
  582. strNewValue = _T("999");
  583. }
  584. else if (szValue[0] == _T('0'))
  585. {
  586. // Remove leading zeros.
  587. strNewValue.Format(_T("%d"), iTimeout);
  588. fGiveUpFocus = TRUE;
  589. }
  590. if (!strNewValue.IsEmpty() && m_nTimeoutIndex != -1)
  591. {
  592. CString strTimeout = m_arrayIniLines[m_nTimeoutIndex];
  593. int iEquals = strTimeout.Find(_T('='));
  594. if (iEquals != -1)
  595. {
  596. while (strTimeout[iEquals + 1] == _T(' ') && (iEquals + 1) < strTimeout.GetLength())
  597. iEquals++;
  598. CString strNewTimeout = strTimeout.Left(iEquals + 1) + strNewValue;
  599. m_arrayIniLines.SetAt(m_nTimeoutIndex, strNewTimeout);
  600. UserMadeChange();
  601. }
  602. SetDlgItemText(IDC_EDITTIMEOUT, strNewValue);
  603. ::SendMessage(GetDlgItemHWND(IDC_EDITTIMEOUT), EM_SETSEL, (WPARAM)0, (LPARAM)-1);
  604. if (!fGiveUpFocus)
  605. GotoDlgCtrl(GetDlgItem(IDC_EDITTIMEOUT));
  606. }
  607. }
  608. //-------------------------------------------------------------------------
  609. // Show the advanced options dialog box.
  610. //-------------------------------------------------------------------------
  611. void CPageBootIni::OnClickedBootAdvanced()
  612. {
  613. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  614. if (iSelection)
  615. {
  616. CString strLine(m_arrayIniLines[iSelection]);
  617. CBootIniAdvancedDlg dlg;
  618. if (dlg.ShowAdvancedOptions(strLine))
  619. {
  620. m_arrayIniLines.SetAt(iSelection, strLine);
  621. UserMadeChange();
  622. SyncControlsToIni();
  623. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0);
  624. }
  625. }
  626. }
  627. //-------------------------------------------------------------------------
  628. // If the user clicks "Set as Default", use the path information from the
  629. // currently selected line to set the new "default=" line.
  630. //-------------------------------------------------------------------------
  631. void CPageBootIni::OnClickedSetAsDefault()
  632. {
  633. if (m_fIgnoreEdit)
  634. return;
  635. // Move the currently selected line to the top of the [operating systems]
  636. // section.
  637. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  638. if (iSelection < m_nMinOSIndex || iSelection > m_nMaxOSIndex)
  639. return;
  640. while (iSelection > m_nMinOSIndex)
  641. {
  642. CString strTemp = m_arrayIniLines[iSelection - 1];
  643. m_arrayIniLines.SetAt(iSelection - 1, m_arrayIniLines[iSelection]);
  644. m_arrayIniLines.SetAt(iSelection, strTemp);
  645. iSelection -= 1;
  646. }
  647. // Get the string from the selected line. Strip off everything after the '='.
  648. SetDefaultOS(iSelection);
  649. UserMadeChange();
  650. SyncControlsToIni(FALSE);
  651. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0);
  652. SelectLine(iSelection);
  653. }
  654. //-------------------------------------------------------------------------
  655. // This attempts to programmatically check if each of the boot paths is
  656. // valid. If an invalid path is found, the user is given the opportunity
  657. // to remove it from the boot.ini file.
  658. //-------------------------------------------------------------------------
  659. void CPageBootIni::OnClickedCheckBootPaths()
  660. {
  661. BOOL fFoundBadLine = FALSE;
  662. BOOL fChangedFile = FALSE;
  663. BOOL fWinNTType, fWin9xType;
  664. CString strCaption;
  665. strCaption.LoadString(IDS_APPCAPTION);
  666. struct { LPCTSTR m_szSearch; BOOL * m_pType; } aOSType[] =
  667. {
  668. { _T("windows xp"), &fWinNTType },
  669. { _T("windowsxp"), &fWinNTType },
  670. { _T("windows nt"), &fWinNTType },
  671. { _T("whistler"), &fWinNTType },
  672. { _T("windows 2002"), &fWinNTType },
  673. { _T("windows 2000"), &fWinNTType },
  674. { _T("microsoft windows"), &fWin9xType },
  675. { NULL, NULL }
  676. };
  677. // Scan through each of the operating system lines in the boot.ini file.
  678. for (int i = m_nMinOSIndex; i <= m_nMaxOSIndex; i++)
  679. {
  680. CString strLine = m_arrayIniLines[i];
  681. strLine.MakeLower();
  682. // Try to figure out the type of the operating system line.
  683. fWinNTType = FALSE;
  684. fWin9xType = FALSE;
  685. for (int iType = 0; aOSType[iType].m_szSearch != NULL; iType++)
  686. if (strLine.Find(aOSType[iType].m_szSearch) != -1)
  687. {
  688. (*aOSType[iType].m_pType) = TRUE;
  689. break;
  690. }
  691. // Strip off the '=' and everything after it in the boot line.
  692. int iEquals = strLine.Find(_T('='));
  693. if (iEquals == -1)
  694. continue;
  695. strLine = strLine.Left(iEquals);
  696. strLine.TrimRight();
  697. if (strLine.IsEmpty())
  698. continue;
  699. // Depending on the type of the OS, we need to verify that it's
  700. // installed differently.
  701. if (fWin9xType)
  702. {
  703. // Look for the bootsect.dos file to see if this is a good drive.
  704. CString strCheck(strLine);
  705. if (strCheck.Right(1) != CString(_T("\\")))
  706. strCheck += CString(_T("\\"));
  707. strCheck += CString(_T("bootsect.dos"));
  708. if (FileExists(strCheck))
  709. continue;
  710. }
  711. else if (fWinNTType)
  712. {
  713. // If this line is for a recovery console (i.e. the line as "bootsect.dat"
  714. // in it), then look for the existence of that file.
  715. if (strLine.Find(_T("bootsect.dat")) != -1)
  716. {
  717. if (FileExists(strLine))
  718. continue;
  719. }
  720. else
  721. {
  722. // Look for the SYSTEM registry hive.
  723. CString strCheck(strLine);
  724. if (strCheck.Right(1) != CString(_T("\\")))
  725. strCheck += CString(_T("\\"));
  726. strCheck += CString(_T("system32\\config\\SYSTEM"));
  727. // Add the prefix to attempt to open an ARC path.
  728. strCheck = CString(_T("\\\\?\\GLOBALROOT\\ArcName\\")) + strCheck;
  729. if (FileExists(strCheck))
  730. continue;
  731. }
  732. }
  733. else // this is not an OS type we can check
  734. continue;
  735. // If execution falls through to here, then the line in question was an OS
  736. // we care about, and it looks like it's invalid. Give the user the opportunity
  737. // to remove it from the BOOT.INI file.
  738. CString strMessage;
  739. strMessage.Format(IDS_BADBOOTLINE, m_arrayIniLines[i]);
  740. if (IDYES == MessageBox(strMessage, strCaption, MB_YESNO | MB_ICONQUESTION))
  741. {
  742. m_arrayIniLines.RemoveAt(i);
  743. m_nMaxOSIndex -= 1;
  744. // Check to see if the line we just removed is the default
  745. // operating system.
  746. CString strDefault = m_arrayIniLines[m_nDefaultIndex];
  747. iEquals = strDefault.Find(_T('='));
  748. if (iEquals != -1)
  749. {
  750. strDefault = strDefault.Mid(iEquals + 1);
  751. strDefault.TrimLeft();
  752. if (strDefault.CompareNoCase(strLine) == 0)
  753. SetDefaultOS(m_nMinOSIndex);
  754. }
  755. i -= 1; // so we look at the next line when the for loop increments i
  756. fChangedFile = TRUE;
  757. }
  758. fFoundBadLine = TRUE;
  759. }
  760. if (!fFoundBadLine)
  761. Message(IDS_NOBADBOOTLINES);
  762. else if (fChangedFile)
  763. {
  764. UserMadeChange();
  765. SyncControlsToIni();
  766. if (m_nMinOSIndex != -1)
  767. {
  768. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMinOSIndex, 0);
  769. SelectLine(m_nMinOSIndex);
  770. }
  771. }
  772. }
  773. //-------------------------------------------------------------------------
  774. // Return the current state of the tab.
  775. //-------------------------------------------------------------------------
  776. CPageBase::TabState CPageBootIni::GetCurrentTabState()
  777. {
  778. if (!m_fInitialized)
  779. return GetAppliedTabState();
  780. return m_stateCurrent;
  781. }
  782. //-------------------------------------------------------------------------
  783. // Applying the changes for the boot.ini tab means writing out the new
  784. // file contents.
  785. //
  786. // The base class implementation is called to maintain the applied
  787. // tab state.
  788. //-------------------------------------------------------------------------
  789. BOOL CPageBootIni::OnApply()
  790. {
  791. // Build up the new contents of the boot.ini file from the
  792. // list. If there is no backup of the boot.ini file, make
  793. // one (so the original can be restored). Then write the
  794. // contents out to the file.
  795. CString strNewContents;
  796. for (int i = 0; i <= m_arrayIniLines.GetUpperBound(); i++)
  797. if (!m_arrayIniLines[i].IsEmpty())
  798. {
  799. if (m_nTimeoutIndex == i)
  800. {
  801. CString strTimeoutValue(m_arrayIniLines[i]);
  802. strTimeoutValue.TrimLeft(_T("TIMEOUTtimeout ="));
  803. int iTimeout = _ttoi(strTimeoutValue);
  804. if (iTimeout < 3 || iTimeout > 999)
  805. {
  806. if (iTimeout < 3)
  807. strTimeoutValue = _T("3");
  808. else if (iTimeout > 999)
  809. strTimeoutValue = _T("999");
  810. int iEquals = m_arrayIniLines[i].Find(_T('='));
  811. if (iEquals != -1)
  812. {
  813. CString strNewTimeout = m_arrayIniLines[i].Left(iEquals + 1) + strTimeoutValue;
  814. m_arrayIniLines.SetAt(i, strNewTimeout);
  815. }
  816. }
  817. }
  818. strNewContents += m_arrayIniLines[i] + _T("\r\n");
  819. }
  820. // If we are currently in a "NORMAL" state, then we want to make a new
  821. // backup file (overwriting an existing one, if necessary). Otherwise,
  822. // only make a backup if there isn't already one. This preserves a good
  823. // backup when the user is making incremental changes.
  824. HRESULT hr = BackupFile(m_strFileName, _T(".backup"), (GetAppliedTabState() == NORMAL));
  825. if (FAILED(hr))
  826. return FALSE;
  827. SetBootIniContents(strNewContents);
  828. CPageBase::SetAppliedState(GetCurrentTabState());
  829. m_fMadeChange = TRUE;
  830. return TRUE;
  831. }
  832. //-------------------------------------------------------------------------
  833. // Committing the changes means applying changes, then saving the current
  834. // values to the registry with the commit flag. Refill the list.
  835. //
  836. // Then call the base class implementation.
  837. //-------------------------------------------------------------------------
  838. void CPageBootIni::CommitChanges()
  839. {
  840. OnApply();
  841. m_stateCurrent = NORMAL;
  842. ::DeleteFile(GetBackupName(m_strFileName, _T(".backup")));
  843. CPageBase::CommitChanges();
  844. }
  845. //-------------------------------------------------------------------------
  846. // Set the overall state of the tab to normal or diagnostic.
  847. //-------------------------------------------------------------------------
  848. void CPageBootIni::SetNormal()
  849. {
  850. // Setting the BOOT.INI tab state to normal means that the original
  851. // BOOT.INI file contents should be restored to the UI (not actually
  852. // saved until the changes are applied). If a BOOT.INI backup file
  853. // exists, we should reload the contents of it. If it doesn't exists,
  854. // reload the contents of the real BOOT.INI.
  855. //
  856. // Note - if the state is already NORMAL, don't do anything.
  857. if (m_stateCurrent == NORMAL)
  858. return;
  859. CString strBackup = GetBackupName(m_strFileName, _T(".backup"));
  860. if (FileExists(strBackup))
  861. LoadBootIni(strBackup);
  862. else
  863. LoadBootIni();
  864. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0);
  865. SyncControlsToIni();
  866. if (iSelection)
  867. {
  868. SelectLine(iSelection);
  869. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0);
  870. }
  871. UserMadeChange();
  872. m_stateCurrent = NORMAL;
  873. }
  874. void CPageBootIni::SetDiagnostic()
  875. {
  876. // Don't do anything.
  877. }
  878. void CPageBootIni::OnDestroy()
  879. {
  880. // Undo the subclass
  881. pBootIniPage = NULL;
  882. HWND hWndEdit = GetDlgItemHWND(IDC_EDITTIMEOUT);
  883. if (pOldBootIniEditProc != NULL && hWndEdit)
  884. ::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)pOldBootIniEditProc);
  885. CPropertyPage::OnDestroy();
  886. }