Windows-Server-2003/admin/pchealth/sysinfo/msconfig/exe/pagebootini.cpp

1117 lines
34 KiB
C++

#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();
}