#include "stdafx.h" #include "PageBootIni.h" #include "MSConfigState.h" #include "BootAdv.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CPageBootIni property page IMPLEMENT_DYNCREATE(CPageBootIni, CPropertyPage) CPageBootIni::CPageBootIni() : CPropertyPage(CPageBootIni::IDD) { //{{AFX_DATA_INIT(CPageBootIni) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_fIgnoreEdit = FALSE; m_strFileName = BOOT_INI; m_fModified = FALSE; } CPageBootIni::~CPageBootIni() { } void CPageBootIni::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CPageBootIni) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CPageBootIni, CPropertyPage) //{{AFX_MSG_MAP(CPageBootIni) ON_BN_CLICKED(IDC_BOOTMOVEDOWN, OnBootMoveDown) ON_BN_CLICKED(IDC_BOOTMOVEUP, OnBootMoveUp) ON_LBN_SELCHANGE(IDC_LISTBOOTINI, OnSelChangeList) ON_BN_CLICKED(IDC_BASEVIDEO, OnClickedBase) ON_BN_CLICKED(IDC_BOOTLOG, OnClickedBootLog) ON_BN_CLICKED(IDC_NOGUIBOOT, OnClickedNoGUIBoot) ON_BN_CLICKED(IDC_SOS, OnClickedSOS) ON_BN_CLICKED(IDC_SAFEBOOT, OnClickedSafeBoot) ON_BN_CLICKED(IDC_SBDSREPAIR, OnClickedSBDSRepair) ON_BN_CLICKED(IDC_SBMINIMAL, OnClickedSBMinimal) ON_BN_CLICKED(IDC_SBMINIMALALT, OnClickedSBMinimalAlt) ON_BN_CLICKED(IDC_SBNETWORK, OnClickedSBNetwork) ON_EN_CHANGE(IDC_EDITTIMEOUT, OnChangeEditTimeOut) ON_EN_KILLFOCUS(IDC_EDITTIMEOUT, OnKillFocusEditTimeOut) ON_BN_CLICKED(IDC_BOOTADVANCED, OnClickedBootAdvanced) ON_BN_CLICKED(IDC_SETASDEFAULT, OnClickedSetAsDefault) ON_BN_CLICKED(IDC_CHECKBOOTPATHS, OnClickedCheckBootPaths) ON_WM_DESTROY() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CPageBootIni message handlers //------------------------------------------------------------------------- // Initialize this page by reading the contents of the boot.ini file. //------------------------------------------------------------------------- void CPageBootIni::InitializePage() { if (LoadBootIni() && m_nMinOSIndex != -1) { SyncControlsToIni(); if (m_nMinOSIndex != -1) { ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMinOSIndex, 0); SelectLine(m_nMinOSIndex); } } else { // Failed to load the boot.ini file (or it was empty). Disable all controls. ::EnableWindow(GetDlgItemHWND(IDC_BOOTMOVEUP), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_BOOTMOVEDOWN), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SAFEBOOT), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_NOGUIBOOT), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_BOOTLOG), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_BASEVIDEO), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SOS), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_BOOTADVANCED), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SBNETWORK), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SBDSREPAIR), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMAL), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMALALT), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_SETASDEFAULT), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_CHECKBOOTPATHS), FALSE); ::EnableWindow(GetDlgItemHWND(IDC_EDITTIMEOUT), FALSE); } m_stateCurrent = CPageBase::GetAppliedTabState(); } //------------------------------------------------------------------------- // Load the contents of the BOOT.INI file into our local structures. //------------------------------------------------------------------------- BOOL CPageBootIni::LoadBootIni(CString strFileName) { if (strFileName.IsEmpty()) strFileName = m_strFileName; // Read the contents of the boot.ini file into a string. HANDLE h = ::CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == h) return FALSE; CString strContents; DWORD dwNumberBytesRead, dwNumberBytesToRead = ::GetFileSize(h, NULL); // The BOOT.INI file is ANSI, so we should read it and convert to Unicode. char * szBuffer = new char[dwNumberBytesToRead + 1]; ::ZeroMemory((PVOID)szBuffer, dwNumberBytesToRead + 1); if (!::ReadFile(h, (LPVOID)szBuffer, dwNumberBytesToRead, &dwNumberBytesRead, NULL)) *szBuffer = _T('\0'); ::CloseHandle(h); // Do the conversion. USES_CONVERSION; LPTSTR szConverted = A2T(szBuffer); strContents = szConverted; delete [] szBuffer; if (dwNumberBytesToRead != dwNumberBytesRead || strContents.IsEmpty()) return FALSE; // Save the original contents of the file. m_strOriginalContents = strContents; // Parse the contents of the string into an array of strings (one for each line // of the file). m_arrayIniLines.RemoveAll(); m_arrayIniLines.SetSize(10, 10); CString strLine; int nIndex = 0; while (!strContents.IsEmpty()) { strLine = strContents.SpanExcluding(_T("\r\n")); if (!strLine.IsEmpty()) { m_arrayIniLines.SetAtGrow(nIndex, strLine); nIndex += 1; } strContents = strContents.Mid(strLine.GetLength()); strContents.TrimLeft(_T("\r\n")); } // Look through the lines read from the INI file, searching for particular // ones we'll want to make a note of. m_nTimeoutIndex = m_nDefaultIndex = m_nMinOSIndex = m_nMaxOSIndex = -1; for (int i = 0; i <= m_arrayIniLines.GetUpperBound(); i++) { CString strScanLine = m_arrayIniLines[i]; strScanLine.MakeLower(); strScanLine.Replace(_T(" "), _T("")); if (strScanLine.Find(_T("timeout=")) != -1) m_nTimeoutIndex = i; else if (strScanLine.Find(_T("default=")) != -1) m_nDefaultIndex = i; if (m_nMinOSIndex != -1 && m_nMaxOSIndex == -1 && (strScanLine.IsEmpty() || strScanLine[0] == _T('['))) m_nMaxOSIndex = i - 1; else if (strScanLine.Find(_T("[operatingsystems]")) != -1) m_nMinOSIndex = i + 1; } if (m_nMinOSIndex != -1 && m_nMaxOSIndex == -1) m_nMaxOSIndex = i - 1; return TRUE; } //---------------------------------------------------------------------------- // Update the state of the controls on this tab to match the contents of the // internal representation of the INI file. //---------------------------------------------------------------------------- void CPageBootIni::SyncControlsToIni(BOOL fSyncEditField) { // We need to keep track of the extent of the strings in the list box // (to handle a horizontal scroll bar). Code from MSDN. DWORD dwExtent, dwMaxExtent = 0; TEXTMETRIC tm; HDC hDCListBox = ::GetDC(GetDlgItemHWND(IDC_LISTBOOTINI)); HFONT hFontNew = (HFONT)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), WM_GETFONT, NULL, NULL); HFONT hFontOld = (HFONT)::SelectObject(hDCListBox, hFontNew); ::GetTextMetrics(hDCListBox, (LPTEXTMETRIC)&tm); CDC dc; dc.Attach(hDCListBox); for (int i = 0; i <= m_arrayIniLines.GetUpperBound(); i++) if (!m_arrayIniLines[i].IsEmpty()) { CSize size = dc.GetTextExtent(m_arrayIniLines[i]); dwExtent = size.cx + tm.tmAveCharWidth; if (dwExtent > dwMaxExtent) dwMaxExtent = dwExtent; } dc.Detach(); ::SelectObject(hDCListBox, hFontOld); ::ReleaseDC(GetDlgItemHWND(IDC_LISTBOOTINI), hDCListBox); // Set the extent for the list box. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETHORIZONTALEXTENT, (WPARAM)dwMaxExtent, 0); // First, add the lines from the boot ini into the list control. ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_RESETCONTENT, 0, 0); for (int j = 0; j <= m_arrayIniLines.GetUpperBound(); j++) if (!m_arrayIniLines[j].IsEmpty()) ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)m_arrayIniLines[j]); // Set the timeout value based on the boot.ini. if (m_nTimeoutIndex != -1 && fSyncEditField) { CString strTimeout = m_arrayIniLines[m_nTimeoutIndex]; strTimeout.TrimLeft(_T("timeout= ")); m_fIgnoreEdit = TRUE; SetDlgItemText(IDC_EDITTIMEOUT, strTimeout); m_fIgnoreEdit = FALSE; } } //---------------------------------------------------------------------------- // Update the controls based on the user's selection of a line. //---------------------------------------------------------------------------- void CPageBootIni::SelectLine(int index) { if (index < m_nMinOSIndex) { ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMinOSIndex, 0); SelectLine(m_nMinOSIndex); return; } if (index > m_nMaxOSIndex) { ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMaxOSIndex, 0); SelectLine(m_nMaxOSIndex); return; } HWND hwndFocus = ::GetFocus(); ::EnableWindow(GetDlgItemHWND(IDC_BOOTMOVEUP), (index > m_nMinOSIndex)); ::EnableWindow(GetDlgItemHWND(IDC_BOOTMOVEDOWN), (index < m_nMaxOSIndex)); if ((index <= m_nMinOSIndex) && hwndFocus == GetDlgItemHWND(IDC_BOOTMOVEUP)) NextDlgCtrl(); if ((index >= m_nMaxOSIndex) && hwndFocus == GetDlgItemHWND(IDC_BOOTMOVEDOWN)) PrevDlgCtrl(); CString strOS = m_arrayIniLines[index]; strOS.MakeLower(); CheckDlgButton(IDC_SAFEBOOT, (strOS.Find(_T("/safeboot")) != -1)); CheckDlgButton(IDC_NOGUIBOOT, (strOS.Find(_T("/noguiboot")) != -1)); CheckDlgButton(IDC_BOOTLOG, (strOS.Find(_T("/bootlog")) != -1)); CheckDlgButton(IDC_BASEVIDEO, (strOS.Find(_T("/basevideo")) != -1)); CheckDlgButton(IDC_SOS, (strOS.Find(_T("/sos")) != -1)); // If the line selected isn't for Whistler, then disable the controls. // If the line is for Whistler or W2K, but it has the string "CMDCONS" in // it, we shouldn't enable the controls. BOOL fEnableControls = ((strOS.Find(_T("whistler")) != -1) || (strOS.Find(_T("windows 2000")) != -1)); fEnableControls |= ((strOS.Find(_T("windowsxp")) != -1) || (strOS.Find(_T("windows xp")) != -1)); fEnableControls |= (strOS.Find(_T("windows server 2003")) != -1); fEnableControls = fEnableControls && (strOS.Find(_T("cmdcons")) == -1); ::EnableWindow(GetDlgItemHWND(IDC_SAFEBOOT), fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_NOGUIBOOT), fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_BOOTLOG), fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_BASEVIDEO), fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_SOS), fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_BOOTADVANCED), fEnableControls); BOOL fSafeboot = (strOS.Find(_T("/safeboot")) != -1); ::EnableWindow(GetDlgItemHWND(IDC_SBNETWORK), fSafeboot && fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_SBDSREPAIR), fSafeboot && fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMAL), fSafeboot && fEnableControls); ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMALALT), fSafeboot && fEnableControls); if (fSafeboot) { CheckDlgButton(IDC_SBNETWORK, (strOS.Find(_T("/safeboot:network")) != -1)); CheckDlgButton(IDC_SBDSREPAIR, (strOS.Find(_T("/safeboot:dsrepair")) != -1)); if (strOS.Find(_T("/safeboot:minimal")) != -1) { BOOL fAlternateShell = (strOS.Find(_T("/safeboot:minimal(alternateshell)")) != -1); CheckDlgButton(IDC_SBMINIMAL, !fAlternateShell); CheckDlgButton(IDC_SBMINIMALALT, fAlternateShell); } else { CheckDlgButton(IDC_SBMINIMAL, FALSE); CheckDlgButton(IDC_SBMINIMALALT, FALSE); } int iSafeboot = strOS.Find(_T("/safeboot")); if (iSafeboot != -1) { m_strSafeBoot = strOS.Mid(iSafeboot + 1); m_strSafeBoot = m_strSafeBoot.SpanExcluding(_T(" /")); m_strSafeBoot = CString(_T("/")) + m_strSafeBoot; } } // Check to see if the selected operating system is the default. // Then enable the button accordingly. BOOL fEnableDefault = FALSE; if (m_nDefaultIndex >= 0) { CString strDefault = m_arrayIniLines[m_nDefaultIndex]; int iEquals = strDefault.Find(_T('=')); if (iEquals != -1) { strDefault = strDefault.Mid(iEquals + 1); strDefault.MakeLower(); CString strCurrent = strOS.SpanExcluding(_T("=")); strDefault.TrimLeft(); strCurrent.TrimRight(); if (strDefault != strCurrent || index > m_nMinOSIndex) fEnableDefault = TRUE; } } ::EnableWindow(GetDlgItemHWND(IDC_SETASDEFAULT), fEnableDefault); if (!fEnableDefault && hwndFocus == GetDlgItemHWND(IDC_SETASDEFAULT)) NextDlgCtrl(); } //------------------------------------------------------------------------- // Add or remove the specified flag from the currently selected OS line. //------------------------------------------------------------------------- void CPageBootIni::ChangeCurrentOSFlag(BOOL fAdd, LPCTSTR szFlag) { int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); CString strFlagPlusSpace = CString(_T(" ")) + szFlag; CString strNewLine; if (iSelection == -1) return; if (fAdd) { if (m_arrayIniLines[iSelection].Find(szFlag) != -1) { ASSERT(0 && "the flag is already there"); return; } strNewLine = m_arrayIniLines[iSelection] + strFlagPlusSpace; } else { int iIndex = m_arrayIniLines[iSelection].Find(strFlagPlusSpace); if (iIndex == -1) { ASSERT(0 && "there is no flag"); return; } strNewLine = m_arrayIniLines[iSelection].Left(iIndex); strNewLine += m_arrayIniLines[iSelection].Mid(iIndex + strFlagPlusSpace.GetLength()); } m_arrayIniLines.SetAt(iSelection, strNewLine); UserMadeChange(); SyncControlsToIni(); ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0); } //------------------------------------------------------------------------- // Sets the "default=" line in the boot.ini. //------------------------------------------------------------------------- void CPageBootIni::SetDefaultOS(int iIndex) { if (m_nDefaultIndex == -1) return; // Get the current string "default=xxxx". Locate the location of the // '=' so we can replace the later half of the line. CString strDefault = m_arrayIniLines[m_nDefaultIndex]; int iEquals = strDefault.Find(_T('=')); if (iEquals == -1) return; CString strValue = m_arrayIniLines[iIndex].SpanExcluding(_T("=")); strValue.TrimRight(); CString strNewDefault = strDefault.Left(iEquals + 1) + strValue; m_arrayIniLines.SetAt(m_nDefaultIndex, strNewDefault); } //------------------------------------------------------------------------- // Write new contents to the BOOT.INI file. //------------------------------------------------------------------------- BOOL CPageBootIni::SetBootIniContents(const CString & strNewContents, const CString & strAddedExtension) { // Extra safety code. if ((LPCTSTR)strNewContents == NULL || *((LPCTSTR)strNewContents) == _T('\0')) return FALSE; // To write to the BOOT.INI file, we need to set it to have normal // attributes. Save the attribute settings so we can restore them. DWORD dwWritten, dwAttribs = ::GetFileAttributes(m_strFileName); ::SetFileAttributes(m_strFileName, FILE_ATTRIBUTE_NORMAL); HANDLE h = ::CreateFile(m_strFileName, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == h) { ::SetFileAttributes(m_strFileName, dwAttribs); return FALSE; } // Convert the internal BOOT.INI representation (Unicode) to ANSI for writing. USES_CONVERSION; LPSTR szBuffer = T2A((LPTSTR)(LPCTSTR)strNewContents); // CreateFile with TRUNCATE_EXISTING seems to SOMETIMES not set the file length to // zero, but to overwrite the existing file with zeroes and leave the pointer at // the end of the file. ::SetFilePointer(h, 0, NULL, FILE_BEGIN); ::WriteFile(h, (void *)szBuffer, strNewContents.GetLength(), &dwWritten, NULL); ::SetEndOfFile(h); ::CloseHandle(h); ::SetFileAttributes(m_strFileName, dwAttribs); return TRUE; } //------------------------------------------------------------------------- // We need to subclass the edit control to catch the enter key, so we // can validate the data and not close MSConfig. //------------------------------------------------------------------------- CPageBootIni * pBootIniPage = NULL; // pointer to the page, so we can call member functions WNDPROC pOldBootIniEditProc = NULL; // save old wndproc when we subclass edit control LRESULT BootIniEditSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp) { switch (wm) { case WM_GETDLGCODE: return DLGC_WANTALLKEYS; case WM_CHAR: if (wp == VK_ESCAPE || wp == VK_RETURN) { if (pBootIniPage != NULL) { pBootIniPage->NextDlgCtrl(); return 0; } } else if (wp == VK_TAB) { if (pBootIniPage != NULL) { if (::GetAsyncKeyState(VK_SHIFT) == 0) pBootIniPage->NextDlgCtrl(); else pBootIniPage->PrevDlgCtrl(); return 0; } } break; } if (pOldBootIniEditProc != NULL) // better not be null return CallWindowProc(pOldBootIniEditProc, hwnd, wm, wp, lp); return 0; } //------------------------------------------------------------------------- // Initialize the boot.ini page. Read in the INI file, set up internal // structures to represent the file, and update the controls to reflect // the internal structures. //------------------------------------------------------------------------- extern BOOL fBasicControls; BOOL CPageBootIni::OnInitDialog() { CPropertyPage::OnInitDialog(); // Check the registry for a testing flag (which would mean we aren't // operating on the real BOOT.INI file). Removed for release. // // 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)) // m_strFileName = szBoot; // } InitializePage(); if (fBasicControls) ::ShowWindow(GetDlgItemHWND(IDC_BOOTADVANCED), SW_HIDE); // Subclass the edit control (to catch the enter key). HWND hWndEdit = GetDlgItemHWND(IDC_EDITTIMEOUT); if (hWndEdit) { pOldBootIniEditProc = (WNDPROC)::GetWindowLongPtr(hWndEdit, GWLP_WNDPROC); pBootIniPage = this; ::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)&BootIniEditSubclassProc); } m_fInitialized = TRUE; return TRUE; // return TRUE unless you set the focus to a control } //------------------------------------------------------------------------- // Called when the user clicks move up or down. //------------------------------------------------------------------------- void CPageBootIni::OnBootMoveDown() { int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); ASSERT(iSelection >= m_nMinOSIndex && iSelection < m_nMaxOSIndex); if (iSelection >= m_nMinOSIndex && iSelection < m_nMaxOSIndex) { CString strTemp = m_arrayIniLines[iSelection + 1]; m_arrayIniLines.SetAt(iSelection + 1, m_arrayIniLines[iSelection]); m_arrayIniLines.SetAt(iSelection, strTemp); UserMadeChange(); SyncControlsToIni(); ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection + 1, 0); SelectLine(iSelection + 1); } } void CPageBootIni::OnBootMoveUp() { int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); ASSERT(iSelection > m_nMinOSIndex && iSelection <= m_nMaxOSIndex); if (iSelection > m_nMinOSIndex && iSelection <= m_nMaxOSIndex) { CString strTemp = m_arrayIniLines[iSelection - 1]; m_arrayIniLines.SetAt(iSelection - 1, m_arrayIniLines[iSelection]); m_arrayIniLines.SetAt(iSelection, strTemp); UserMadeChange(); SyncControlsToIni(); ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection - 1, 0); SelectLine(iSelection - 1); } } //------------------------------------------------------------------------- // Called when the user clicks on a line in the list view. //------------------------------------------------------------------------- void CPageBootIni::OnSelChangeList() { SelectLine((int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0)); } //------------------------------------------------------------------------- // The check boxes are handled uniformly - adding or removing a flag from // the currently selected OS line. //------------------------------------------------------------------------- void CPageBootIni::OnClickedBase() { ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_BASEVIDEO), _T("/basevideo")); } void CPageBootIni::OnClickedBootLog() { ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_BOOTLOG), _T("/bootlog")); } void CPageBootIni::OnClickedNoGUIBoot() { ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_NOGUIBOOT), _T("/noguiboot")); } void CPageBootIni::OnClickedSOS() { ChangeCurrentOSFlag(IsDlgButtonChecked(IDC_SOS), _T("/sos")); } //------------------------------------------------------------------------- // The safeboot flag is a little more complicated, since it has an extra // portion (from the radio buttons). //------------------------------------------------------------------------- void CPageBootIni::OnClickedSafeBoot() { CString strFlag(_T("/safeboot")); if (IsDlgButtonChecked(IDC_SBNETWORK)) strFlag += _T(":network"); else if (IsDlgButtonChecked(IDC_SBDSREPAIR)) strFlag += _T(":dsrepair"); else if (IsDlgButtonChecked(IDC_SBMINIMALALT)) strFlag += _T(":minimal(alternateshell)"); else { strFlag += _T(":minimal"); CheckDlgButton(IDC_SBMINIMAL, 1); } BOOL fSafeBoot = IsDlgButtonChecked(IDC_SAFEBOOT); ChangeCurrentOSFlag(fSafeBoot, strFlag); m_strSafeBoot = strFlag; ::EnableWindow(GetDlgItemHWND(IDC_SBNETWORK), fSafeBoot); ::EnableWindow(GetDlgItemHWND(IDC_SBDSREPAIR), fSafeBoot); ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMAL), fSafeBoot); ::EnableWindow(GetDlgItemHWND(IDC_SBMINIMALALT), fSafeBoot); } //------------------------------------------------------------------------- // Clicking on one of the safeboot radio buttons requires a little extra // processing, to remove the existing flag and add the new one. //------------------------------------------------------------------------- void CPageBootIni::OnClickedSBDSRepair() { ChangeCurrentOSFlag(FALSE, m_strSafeBoot); m_strSafeBoot = _T("/safeboot:dsrepair"); ChangeCurrentOSFlag(TRUE, m_strSafeBoot); } void CPageBootIni::OnClickedSBMinimal() { ChangeCurrentOSFlag(FALSE, m_strSafeBoot); m_strSafeBoot = _T("/safeboot:minimal"); ChangeCurrentOSFlag(TRUE, m_strSafeBoot); } void CPageBootIni::OnClickedSBMinimalAlt() { ChangeCurrentOSFlag(FALSE, m_strSafeBoot); m_strSafeBoot = _T("/safeboot:minimal(alternateshell)"); ChangeCurrentOSFlag(TRUE, m_strSafeBoot); } void CPageBootIni::OnClickedSBNetwork() { ChangeCurrentOSFlag(FALSE, m_strSafeBoot); m_strSafeBoot = _T("/safeboot:network"); ChangeCurrentOSFlag(TRUE, m_strSafeBoot); } //------------------------------------------------------------------------- // As the user enters text in the timeout field, update the line in the // ini file list box. //------------------------------------------------------------------------- void CPageBootIni::OnChangeEditTimeOut() { if (m_fIgnoreEdit) return; if (m_nTimeoutIndex == -1) return; CString strTimeout = m_arrayIniLines[m_nTimeoutIndex]; int iEquals = strTimeout.Find(_T('=')); if (iEquals == -1) return; while (strTimeout[iEquals + 1] == _T(' ') && (iEquals + 1) < strTimeout.GetLength()) iEquals++; TCHAR szValue[MAX_PATH]; GetDlgItemText(IDC_EDITTIMEOUT, szValue, MAX_PATH); CString strNewTimeout = strTimeout.Left(iEquals + 1) + szValue; m_arrayIniLines.SetAt(m_nTimeoutIndex, strNewTimeout); UserMadeChange(); int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); SyncControlsToIni(FALSE); if (iSelection != -1) ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0); } void CPageBootIni::OnKillFocusEditTimeOut() { TCHAR szValue[MAX_PATH]; GetDlgItemText(IDC_EDITTIMEOUT, szValue, MAX_PATH); CString strNewValue(_T("")); BOOL fGiveUpFocus = FALSE; int iTimeout = _ttoi(szValue); if (iTimeout < 3 || iTimeout > 999) { CString strMessage, strCaption; strMessage.LoadString(IDS_TIMEOUTVALUE); strCaption.LoadString(IDS_APPCAPTION); MessageBox(strMessage, strCaption); if (iTimeout < 3) strNewValue = _T("3"); else if (iTimeout > 999) strNewValue = _T("999"); } else if (szValue[0] == _T('0')) { // Remove leading zeros. strNewValue.Format(_T("%d"), iTimeout); fGiveUpFocus = TRUE; } if (!strNewValue.IsEmpty() && m_nTimeoutIndex != -1) { CString strTimeout = m_arrayIniLines[m_nTimeoutIndex]; int iEquals = strTimeout.Find(_T('=')); if (iEquals != -1) { while (strTimeout[iEquals + 1] == _T(' ') && (iEquals + 1) < strTimeout.GetLength()) iEquals++; CString strNewTimeout = strTimeout.Left(iEquals + 1) + strNewValue; m_arrayIniLines.SetAt(m_nTimeoutIndex, strNewTimeout); UserMadeChange(); } SetDlgItemText(IDC_EDITTIMEOUT, strNewValue); ::SendMessage(GetDlgItemHWND(IDC_EDITTIMEOUT), EM_SETSEL, (WPARAM)0, (LPARAM)-1); if (!fGiveUpFocus) GotoDlgCtrl(GetDlgItem(IDC_EDITTIMEOUT)); } } //------------------------------------------------------------------------- // Show the advanced options dialog box. //------------------------------------------------------------------------- void CPageBootIni::OnClickedBootAdvanced() { int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); if (iSelection > 0) { CString strLine(m_arrayIniLines[iSelection]); CBootIniAdvancedDlg dlg; if (dlg.ShowAdvancedOptions(strLine)) { m_arrayIniLines.SetAt(iSelection, strLine); UserMadeChange(); SyncControlsToIni(); ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0); } } } //------------------------------------------------------------------------- // If the user clicks "Set as Default", use the path information from the // currently selected line to set the new "default=" line. //------------------------------------------------------------------------- void CPageBootIni::OnClickedSetAsDefault() { if (m_fIgnoreEdit) return; // Move the currently selected line to the top of the [operating systems] // section. int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); if (iSelection < m_nMinOSIndex || iSelection > m_nMaxOSIndex) return; while (iSelection > m_nMinOSIndex) { CString strTemp = m_arrayIniLines[iSelection - 1]; m_arrayIniLines.SetAt(iSelection - 1, m_arrayIniLines[iSelection]); m_arrayIniLines.SetAt(iSelection, strTemp); iSelection -= 1; } // Get the string from the selected line. Strip off everything after the '='. SetDefaultOS(iSelection); UserMadeChange(); SyncControlsToIni(FALSE); ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0); SelectLine(iSelection); } //------------------------------------------------------------------------- // This attempts to programmatically check if each of the boot paths is // valid. If an invalid path is found, the user is given the opportunity // to remove it from the boot.ini file. //------------------------------------------------------------------------- void CPageBootIni::OnClickedCheckBootPaths() { BOOL fFoundBadLine = FALSE; BOOL fChangedFile = FALSE; BOOL fWinNTType, fWin9xType; CString strCaption; strCaption.LoadString(IDS_APPCAPTION); struct { LPCTSTR m_szSearch; BOOL * m_pType; } aOSType[] = { { _T("windows xp"), &fWinNTType }, { _T("windowsxp"), &fWinNTType }, { _T("windows nt"), &fWinNTType }, { _T("whistler"), &fWinNTType }, { _T("windows 2000"), &fWinNTType }, { _T("windows server 2003"), &fWinNTType }, { _T("microsoft windows"), &fWin9xType }, { NULL, NULL } }; // Scan through each of the operating system lines in the boot.ini file. for (int i = m_nMinOSIndex; i <= m_nMaxOSIndex; i++) { CString strLine = m_arrayIniLines[i]; strLine.MakeLower(); // Try to figure out the type of the operating system line. fWinNTType = FALSE; fWin9xType = FALSE; for (int iType = 0; aOSType[iType].m_szSearch != NULL; iType++) if (strLine.Find(aOSType[iType].m_szSearch) != -1) { (*aOSType[iType].m_pType) = TRUE; break; } // Strip off the '=' and everything after it in the boot line. int iEquals = strLine.Find(_T('=')); if (iEquals == -1) continue; strLine = strLine.Left(iEquals); strLine.TrimRight(); if (strLine.IsEmpty()) continue; // Depending on the type of the OS, we need to verify that it's // installed differently. if (fWin9xType) { // Look for the bootsect.dos file to see if this is a good drive. CString strCheck(strLine); if (strCheck.Right(1) != CString(_T("\\"))) strCheck += CString(_T("\\")); strCheck += CString(_T("bootsect.dos")); if (FileExists(strCheck)) continue; } else if (fWinNTType) { // If this line is for a recovery console (i.e. the line as "bootsect.dat" // in it), then look for the existence of that file. if (strLine.Find(_T("bootsect.dat")) != -1) { if (FileExists(strLine)) continue; } else { // Look for the SYSTEM registry hive. CString strCheck(strLine); if (strCheck.Right(1) != CString(_T("\\"))) strCheck += CString(_T("\\")); strCheck += CString(_T("system32\\config\\SYSTEM")); // Add the prefix to attempt to open an ARC path. strCheck = CString(_T("\\\\?\\GLOBALROOT\\ArcName\\")) + strCheck; if (FileExists(strCheck)) continue; } } else // this is not an OS type we can check continue; // If execution falls through to here, then the line in question was an OS // we care about, and it looks like it's invalid. Give the user the opportunity // to remove it from the BOOT.INI file. CString strMessage; strMessage.Format(IDS_BADBOOTLINE, m_arrayIniLines[i]); if (IDYES == MessageBox(strMessage, strCaption, MB_YESNO | MB_ICONQUESTION)) { m_arrayIniLines.RemoveAt(i); m_nMaxOSIndex -= 1; // Check to see if the line we just removed is the default // operating system. CString strDefault = m_arrayIniLines[m_nDefaultIndex]; iEquals = strDefault.Find(_T('=')); if (iEquals != -1) { strDefault = strDefault.Mid(iEquals + 1); strDefault.TrimLeft(); if (strDefault.CompareNoCase(strLine) == 0) SetDefaultOS(m_nMinOSIndex); } i -= 1; // so we look at the next line when the for loop increments i fChangedFile = TRUE; } fFoundBadLine = TRUE; } if (!fFoundBadLine) Message(IDS_NOBADBOOTLINES); else if (fChangedFile) { UserMadeChange(); SyncControlsToIni(); if (m_nMinOSIndex != -1) { ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, m_nMinOSIndex, 0); SelectLine(m_nMinOSIndex); } } } //------------------------------------------------------------------------- // Return the current state of the tab. //------------------------------------------------------------------------- CPageBase::TabState CPageBootIni::GetCurrentTabState() { if (!m_fInitialized) return GetAppliedTabState(); return m_stateCurrent; } //------------------------------------------------------------------------- // Applying the changes for the boot.ini tab means writing out the new // file contents. // // The base class implementation is called to maintain the applied // tab state. //------------------------------------------------------------------------- BOOL CPageBootIni::OnApply() { if (!m_fModified) return TRUE; // Build up the new contents of the boot.ini file from the // list. If there is no backup of the boot.ini file, make // one (so the original can be restored). Then write the // contents out to the file. CString strNewContents; for (int i = 0; i <= m_arrayIniLines.GetUpperBound(); i++) if (!m_arrayIniLines[i].IsEmpty()) { if (m_nTimeoutIndex == i) { CString strTimeoutValue(m_arrayIniLines[i]); strTimeoutValue.TrimLeft(_T("TIMEOUTtimeout =")); int iTimeout = _ttoi(strTimeoutValue); if (iTimeout < 3 || iTimeout > 999) { if (iTimeout < 3) strTimeoutValue = _T("3"); else if (iTimeout > 999) strTimeoutValue = _T("999"); int iEquals = m_arrayIniLines[i].Find(_T('=')); if (iEquals != -1) { CString strNewTimeout = m_arrayIniLines[i].Left(iEquals + 1) + strTimeoutValue; m_arrayIniLines.SetAt(i, strNewTimeout); } } } strNewContents += m_arrayIniLines[i] + _T("\r\n"); } // If we are currently in a "NORMAL" state, then we want to make a new // backup file (overwriting an existing one, if necessary). Otherwise, // only make a backup if there isn't already one. This preserves a good // backup when the user is making incremental changes. HRESULT hr = BackupFile(m_strFileName, _T(".backup"), (GetAppliedTabState() == NORMAL)); if (FAILED(hr)) return FALSE; SetBootIniContents(strNewContents); CPageBase::SetAppliedState(GetCurrentTabState()); m_fMadeChange = TRUE; return TRUE; } //------------------------------------------------------------------------- // Committing the changes means applying changes, then saving the current // values to the registry with the commit flag. Refill the list. // // Then call the base class implementation. //------------------------------------------------------------------------- void CPageBootIni::CommitChanges() { OnApply(); m_stateCurrent = NORMAL; ::DeleteFile(GetBackupName(m_strFileName, _T(".backup"))); CPageBase::CommitChanges(); } //------------------------------------------------------------------------- // Set the overall state of the tab to normal or diagnostic. //------------------------------------------------------------------------- void CPageBootIni::SetNormal() { // Setting the BOOT.INI tab state to normal means that the original // BOOT.INI file contents should be restored to the UI (not actually // saved until the changes are applied). If a BOOT.INI backup file // exists, we should reload the contents of it. If it doesn't exists, // reload the contents of the real BOOT.INI. // // Note - if the state is already NORMAL, don't do anything. if (m_stateCurrent == NORMAL) return; CString strBackup = GetBackupName(m_strFileName, _T(".backup")); if (FileExists(strBackup)) LoadBootIni(strBackup); else LoadBootIni(); int iSelection = (int)::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_GETCURSEL, 0, 0); SyncControlsToIni(); if (iSelection > 0) { SelectLine(iSelection); ::SendMessage(GetDlgItemHWND(IDC_LISTBOOTINI), LB_SETCURSEL, iSelection, 0); } UserMadeChange(); m_stateCurrent = NORMAL; } void CPageBootIni::SetDiagnostic() { // Don't do anything. } void CPageBootIni::OnDestroy() { // Undo the subclass pBootIniPage = NULL; HWND hWndEdit = GetDlgItemHWND(IDC_EDITTIMEOUT); if (pOldBootIniEditProc != NULL && hWndEdit) ::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)pOldBootIniEditProc); CPropertyPage::OnDestroy(); }