Files
reactos/base/setup/reactos/reactos.c
Hermès Bélusca-Maïto 4920e639f9 [SETUP:REACTOS] Revamp the Install-type page layout (#7581)
- Update the page title and subtitle.
- Embolden the types of installation.
- Introduce title and subtitle for the upgrade/repair page
  (not translated yet).
2024-12-31 12:33:27 +01:00

3048 lines
101 KiB
C

/*
* ReactOS applications
* Copyright (C) 2004-2008 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS GUI first stage setup application
* FILE: base/setup/reactos/reactos.c
* PROGRAMMERS: Matthias Kupfer
* Dmitry Chapyshev (dmitry@reactos.org)
*/
#include "reactos.h"
#include <winnls.h> // For GetUserDefaultLCID()
#define NTOS_MODE_USER
#include <ndk/obfuncs.h>
#include "resource.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ******************************************************************/
HANDLE ProcessHeap;
SETUPDATA SetupData;
/* The partition where to perform the installation */
PPARTENTRY InstallPartition = NULL;
// static PVOLENTRY InstallVolume = NULL;
#define InstallVolume (InstallPartition->Volume)
/* The system partition we will actually use */
PPARTENTRY SystemPartition = NULL;
// static PVOLENTRY SystemVolume = NULL;
#define SystemVolume (SystemPartition->Volume)
/* UI elements */
UI_CONTEXT UiContext;
/* FUNCTIONS ****************************************************************/
static VOID
CenterWindow(HWND hWnd)
{
HWND hWndParent;
RECT rcParent;
RECT rcWindow;
hWndParent = GetParent(hWnd);
if (hWndParent == NULL)
hWndParent = GetDesktopWindow();
GetWindowRect(hWndParent, &rcParent);
GetWindowRect(hWnd, &rcWindow);
SetWindowPos(hWnd,
HWND_TOP,
((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2,
((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2,
0,
0,
SWP_NOSIZE);
}
/**
* @brief
* Create a bold font derived from the provided font.
**/
static HFONT
CreateBoldFont(
_In_opt_ HFONT hOrigFont,
_In_opt_ INT PointSize)
{
LOGFONTW lf = {0};
if (hOrigFont)
{
GetObjectW(hOrigFont, sizeof(lf), &lf);
}
else
{
NONCLIENTMETRICSW ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
lf = ncm.lfMessageFont;
}
/* Make the font bold, keeping the other attributes */
lf.lfWeight = FW_BOLD;
/* Determine the font height (logical units) if necessary */
if (PointSize)
{
HDC hdc = GetDC(NULL);
lf.lfHeight = -MulDiv(PointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
// lf.lfWidth = 0;
ReleaseDC(NULL, hdc);
}
return CreateFontIndirect(&lf);
}
static inline HFONT
CreateTitleFont(
_In_opt_ HFONT hOrigFont)
{
/* Title font is 12pt bold */
return CreateBoldFont(hOrigFont, 12);
}
INT
DisplayMessageV(
_In_opt_ HWND hWnd,
_In_ UINT uType,
_In_opt_ PCWSTR pszTitle,
_In_opt_ PCWSTR pszFormatMessage,
_In_ va_list args)
{
INT iRes;
HINSTANCE hInstance = NULL;
MSGBOXPARAMSW mb = {0};
LPWSTR Format;
size_t MsgLen;
WCHAR StaticBuffer[256];
LPWSTR Buffer = StaticBuffer; // Use the static buffer by default.
/* We need to retrieve the current module's instance handle if either
* the title or the format message is specified by a resource ID */
if ((pszTitle && IS_INTRESOURCE(pszTitle)) || IS_INTRESOURCE(pszFormatMessage))
hInstance = GetModuleHandleW(NULL); // SetupData.hInstance;
/* Retrieve the format message string if this is a resource */
if (pszFormatMessage && IS_INTRESOURCE(pszFormatMessage)) do
{
// LoadAllocStringW()
PCWSTR pStr;
/* Try to load the string from the resource */
MsgLen = LoadStringW(hInstance, PtrToUlong(pszFormatMessage), (LPWSTR)&pStr, 0);
if (MsgLen == 0)
{
/* No resource string was found, return NULL */
Format = NULL;
break;
}
/* Allocate a new buffer, adding a NULL-terminator */
Format = HeapAlloc(GetProcessHeap(), 0, (MsgLen + 1) * sizeof(WCHAR));
if (!Format)
{
MsgLen = 0;
break;
}
/* Copy the string, NULL-terminated */
StringCchCopyNW(Format, MsgLen + 1, pStr, MsgLen);
} while (0);
else
{
Format = (LPWSTR)pszFormatMessage;
}
if (Format)
{
/*
* Retrieve the message length. If it is too long, allocate
* an auxiliary buffer; otherwise use the static buffer.
* The string is built to be NULL-terminated.
*/
MsgLen = _vscwprintf(Format, args);
if (MsgLen >= _countof(StaticBuffer))
{
Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
if (!Buffer)
{
/* Allocation failed, use the original format string verbatim */
Buffer = Format;
}
}
if (Buffer != Format)
{
/* Do the printf as we use the caller's format string */
StringCchVPrintfW(Buffer, MsgLen + 1, Format, args);
}
}
else
{
Format = (LPWSTR)pszFormatMessage;
Buffer = Format;
}
/* Display the message */
mb.cbSize = sizeof(mb);
mb.hwndOwner = hWnd;
mb.hInstance = hInstance;
mb.lpszText = Buffer;
mb.lpszCaption = pszTitle;
mb.dwStyle = uType;
mb.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
iRes = MessageBoxIndirectW(&mb);
/* Free the buffers if needed */
if ((Buffer != StaticBuffer) && (Buffer != Format))
HeapFree(GetProcessHeap(), 0, Buffer);
if (Format && (Format != pszFormatMessage))
HeapFree(GetProcessHeap(), 0, Format);
return iRes;
}
INT
__cdecl
DisplayMessage(
_In_opt_ HWND hWnd,
_In_ UINT uType,
_In_opt_ PCWSTR pszTitle,
_In_opt_ PCWSTR pszFormatMessage,
...)
{
INT iRes;
va_list args;
va_start(args, pszFormatMessage);
iRes = DisplayMessageV(hWnd, uType, pszTitle, pszFormatMessage, args);
va_end(args);
return iRes;
}
INT
__cdecl
DisplayError(
_In_opt_ HWND hWnd,
_In_ UINT uIDTitle,
_In_ UINT uIDMessage,
...)
{
INT iRes;
va_list args;
va_start(args, uIDMessage);
iRes = DisplayMessageV(hWnd, MB_OK | MB_ICONERROR,
MAKEINTRESOURCEW(uIDTitle),
MAKEINTRESOURCEW(uIDMessage),
args);
va_end(args);
return iRes;
}
VOID
SetWindowResTextW(
_In_ HWND hWnd,
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID)
{
WCHAR szText[256];
LoadStringW(hInstance, uID, szText, _countof(szText));
SetWindowTextW(hWnd, szText);
}
VOID
SetWindowResPrintfVW(
_In_ HWND hWnd,
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID,
_In_ va_list args)
{
WCHAR ResBuffer[256];
WCHAR szText[256];
LoadStringW(hInstance, uID, ResBuffer, _countof(ResBuffer));
StringCchVPrintfW(szText, _countof(szText), ResBuffer, args);
SetWindowTextW(hWnd, szText);
}
VOID
__cdecl
SetWindowResPrintfW(
_In_ HWND hWnd,
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID,
...)
{
va_list args;
va_start(args, uID);
SetWindowResPrintfVW(hWnd, hInstance, uID, args);
va_end(args);
}
static INT_PTR CALLBACK
StartDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PSETUPDATA pSetupData;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
/* Set title font */
SetDlgItemFont(hwndDlg, IDC_STARTTITLE, pSetupData->hTitleFont, TRUE);
// TEMPTEMP: Set the ReactOS-Alpha information in bold.
// TODO: Remove once we reach 0.5/Beta :)
SetDlgItemFont(hwndDlg, IDC_WARNTEXT1, pSetupData->hBoldFont, TRUE);
SetDlgItemFont(hwndDlg, IDC_WARNTEXT2, pSetupData->hBoldFont, TRUE);
SetDlgItemFont(hwndDlg, IDC_WARNTEXT3, pSetupData->hBoldFont, TRUE);
/* Center the wizard window */
CenterWindow(GetParent(hwndDlg));
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
/* Only "Next" and "Cancel" for the first page and hide "Back" */
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
// PropSheet_ShowWizButtons(GetParent(hwndDlg), 0, PSWIZB_BACK);
ShowDlgItem(GetParent(hwndDlg), ID_WIZBACK, SW_HIDE);
break;
}
case PSN_KILLACTIVE:
{
/* Show "Back" button */
// PropSheet_ShowWizButtons(GetParent(hwndDlg), PSWIZB_BACK, PSWIZB_BACK);
ShowDlgItem(GetParent(hwndDlg), ID_WIZBACK, SW_SHOW);
break;
}
default:
break;
}
}
break;
default:
break;
}
return FALSE;
}
static INT_PTR CALLBACK
TypeDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PSETUPDATA pSetupData;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
/* Set the options in bold */
SetDlgItemFont(hwndDlg, IDC_INSTALL, pSetupData->hBoldFont, TRUE);
SetDlgItemFont(hwndDlg, IDC_UPDATE, pSetupData->hBoldFont, TRUE);
/* Check the "Install" radio button */
CheckDlgButton(hwndDlg, IDC_INSTALL, BST_CHECKED);
/*
* Enable the "Update" radio button and text only if we have
* available NT installations, otherwise disable them.
*/
if (pSetupData->NtOsInstallsList &&
GetNumberOfListEntries(pSetupData->NtOsInstallsList) != 0)
{
EnableWindow(GetDlgItem(hwndDlg, IDC_UPDATE), TRUE);
EnableWindow(GetDlgItem(hwndDlg, IDC_UPDATETEXT), TRUE);
}
else
{
EnableWindow(GetDlgItem(hwndDlg, IDC_UPDATE), FALSE);
EnableWindow(GetDlgItem(hwndDlg, IDC_UPDATETEXT), FALSE);
}
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;
case PSN_QUERYINITIALFOCUS:
{
/* Focus on "Install ReactOS" */
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_INSTALL));
return TRUE;
}
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_YESNO | MB_ICONQUESTION,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Terminate page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_RESTARTPAGE);
}
/* Do not close the wizard too soon */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
case PSN_WIZNEXT: /* Set the selected data */
{
/*
* Go update only if we have available NT installations
* and we choose to do so.
*/
if (pSetupData->NtOsInstallsList &&
GetNumberOfListEntries(pSetupData->NtOsInstallsList) != 0 &&
IsDlgButtonChecked(hwndDlg, IDC_UPDATE) == BST_CHECKED)
{
pSetupData->RepairUpdateFlag = TRUE;
/*
* Display the existing NT installations page only
* if we have more than one available NT installations.
*/
if (GetNumberOfListEntries(pSetupData->NtOsInstallsList) > 1)
{
/* pSetupData->CurrentInstallation will be set from within IDD_UPDATEREPAIRPAGE */
/* Actually the best would be to dynamically insert the page only when needed */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_UPDATEREPAIRPAGE);
}
else
{
/* Retrieve the current installation */
pSetupData->CurrentInstallation =
(PNTOS_INSTALLATION)GetListEntryData(GetCurrentListEntry(pSetupData->NtOsInstallsList));
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_DEVICEPAGE);
}
}
else
{
pSetupData->CurrentInstallation = NULL;
pSetupData->RepairUpdateFlag = FALSE;
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_DEVICEPAGE);
}
return TRUE;
}
default:
break;
}
}
break;
default:
break;
}
return FALSE;
}
BOOL
CreateListViewColumns(
IN HINSTANCE hInstance,
IN HWND hWndListView,
IN const UINT* pIDs,
IN const INT* pColsWidth,
IN const INT* pColsAlign,
IN UINT nNumOfColumns)
{
UINT i;
LVCOLUMN lvC;
WCHAR szText[50];
/* Create the columns */
lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvC.pszText = szText;
/* Load the column labels from the resource file */
for (i = 0; i < nNumOfColumns; i++)
{
lvC.iSubItem = i;
lvC.cx = pColsWidth[i];
lvC.fmt = pColsAlign[i];
LoadStringW(hInstance, pIDs[i], szText, ARRAYSIZE(szText));
if (ListView_InsertColumn(hWndListView, i, &lvC) == -1)
return FALSE;
}
return TRUE;
}
typedef VOID
(NTAPI *PGET_ENTRY_DESCRIPTION)(
IN PGENERIC_LIST_ENTRY Entry,
OUT PWSTR Buffer,
IN SIZE_T cchBufferSize);
VOID
InitGenericComboList(
IN HWND hWndList,
IN PGENERIC_LIST List,
IN PGET_ENTRY_DESCRIPTION GetEntryDescriptionProc)
{
INT Index, CurrentEntryIndex = 0;
PGENERIC_LIST_ENTRY ListEntry;
PLIST_ENTRY Entry;
WCHAR CurrentItemText[256];
for (Entry = List->ListHead.Flink;
Entry != &List->ListHead;
Entry = Entry->Flink)
{
ListEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
if (GetEntryDescriptionProc)
{
GetEntryDescriptionProc(ListEntry,
CurrentItemText,
ARRAYSIZE(CurrentItemText));
Index = SendMessageW(hWndList, CB_ADDSTRING, 0, (LPARAM)CurrentItemText);
}
else
{
Index = SendMessageW(hWndList, CB_ADDSTRING, 0, (LPARAM)L"n/a");
}
if (ListEntry == List->CurrentEntry)
CurrentEntryIndex = Index;
SendMessageW(hWndList, CB_SETITEMDATA, Index, (LPARAM)ListEntry);
}
SendMessageW(hWndList, CB_SETCURSEL, CurrentEntryIndex, 0);
}
PVOID
GetSelectedComboListItem(
IN HWND hWndList)
{
INT Index;
Index = ComboBox_GetCurSel(hWndList);
if (Index == CB_ERR)
return NULL;
return (PVOID)ComboBox_GetItemData(hWndList, Index);
}
typedef VOID
(NTAPI *PADD_ENTRY_ITEM)(
IN HWND hWndList,
IN LVITEM* plvItem,
IN PGENERIC_LIST_ENTRY Entry,
IN OUT PWSTR Buffer,
IN SIZE_T cchBufferSize);
VOID
InitGenericListView(
IN HWND hWndList,
IN PGENERIC_LIST List,
IN PADD_ENTRY_ITEM AddEntryItemProc)
{
INT CurrentEntryIndex = 0;
LVITEM lvItem;
PGENERIC_LIST_ENTRY ListEntry;
PLIST_ENTRY Entry;
WCHAR CurrentItemText[256];
for (Entry = List->ListHead.Flink;
Entry != &List->ListHead;
Entry = Entry->Flink)
{
ListEntry = CONTAINING_RECORD(Entry, GENERIC_LIST_ENTRY, Entry);
if (!AddEntryItemProc)
continue;
AddEntryItemProc(hWndList,
&lvItem,
ListEntry,
CurrentItemText,
ARRAYSIZE(CurrentItemText));
if (ListEntry == List->CurrentEntry)
CurrentEntryIndex = lvItem.iItem;
}
ListView_EnsureVisible(hWndList, CurrentEntryIndex, FALSE);
ListView_SetItemState(hWndList, CurrentEntryIndex,
LVIS_FOCUSED | LVIS_SELECTED,
LVIS_FOCUSED | LVIS_SELECTED);
}
PVOID
GetSelectedListViewItem(
IN HWND hWndList)
{
INT Index;
LVITEM item;
Index = ListView_GetSelectionMark(hWndList);
if (Index == LB_ERR)
return NULL;
item.mask = LVIF_PARAM;
item.iItem = Index;
ListView_GetItem(hWndList, &item);
return (PVOID)item.lParam;
}
static VOID
NTAPI
GetSettingDescription(
IN PGENERIC_LIST_ENTRY Entry,
OUT PWSTR Buffer,
IN SIZE_T cchBufferSize)
{
StringCchCopyW(Buffer, cchBufferSize,
((PGENENTRY)GetListEntryData(Entry))->Value);
}
static VOID
NTAPI
AddNTOSInstallationItem(
IN HWND hWndList,
IN LVITEM* plvItem,
IN PGENERIC_LIST_ENTRY Entry,
IN OUT PWSTR Buffer, // SystemRootPath
IN SIZE_T cchBufferSize)
{
PNTOS_INSTALLATION NtOsInstall = (PNTOS_INSTALLATION)GetListEntryData(Entry);
PVOLINFO VolInfo = (NtOsInstall->Volume ? &NtOsInstall->Volume->Info : NULL);
if (VolInfo && VolInfo->DriveLetter)
{
/* We have retrieved a partition that is mounted */
StringCchPrintfW(Buffer, cchBufferSize,
L"%c:%s",
VolInfo->DriveLetter,
NtOsInstall->PathComponent);
}
else
{
/* We failed somewhere, just show the NT path */
StringCchPrintfW(Buffer, cchBufferSize,
L"%wZ",
&NtOsInstall->SystemNtPath);
}
plvItem->mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
plvItem->iItem = 0;
plvItem->iSubItem = 0;
plvItem->lParam = (LPARAM)Entry;
plvItem->pszText = NtOsInstall->InstallationName;
/* Associate vendor icon */
if (FindSubStrI(NtOsInstall->VendorName, VENDOR_REACTOS))
{
plvItem->mask |= LVIF_IMAGE;
plvItem->iImage = 0;
}
else if (FindSubStrI(NtOsInstall->VendorName, VENDOR_MICROSOFT))
{
plvItem->mask |= LVIF_IMAGE;
plvItem->iImage = 1;
}
plvItem->iItem = SendMessageW(hWndList, LVM_INSERTITEMW, 0, (LPARAM)plvItem);
plvItem->iSubItem = 1;
plvItem->pszText = Buffer; // SystemRootPath;
SendMessageW(hWndList, LVM_SETITEMTEXTW, plvItem->iItem, (LPARAM)plvItem);
plvItem->iSubItem = 2;
plvItem->pszText = NtOsInstall->VendorName;
SendMessageW(hWndList, LVM_SETITEMTEXTW, plvItem->iItem, (LPARAM)plvItem);
}
#define IDS_LIST_COLUMN_FIRST IDS_INSTALLATION_NAME
#define IDS_LIST_COLUMN_LAST IDS_INSTALLATION_VENDOR
#define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
static const UINT column_ids[MAX_LIST_COLUMNS] = {IDS_LIST_COLUMN_FIRST, IDS_LIST_COLUMN_FIRST + 1, IDS_LIST_COLUMN_FIRST + 2};
static const INT column_widths[MAX_LIST_COLUMNS] = {200, 150, 100};
static const INT column_alignment[MAX_LIST_COLUMNS] = {LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT};
static INT_PTR CALLBACK
UpgradeRepairDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PSETUPDATA pSetupData;
HWND hList;
HIMAGELIST hSmall;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
/*
* Keep the "Next" button disabled. It will be enabled only
* when the user selects an installation to upgrade.
*/
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
hList = GetDlgItem(hwndDlg, IDC_NTOSLIST);
ListView_SetExtendedListViewStyleEx(hList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
CreateListViewColumns(pSetupData->hInstance,
hList,
column_ids,
column_widths,
column_alignment,
MAX_LIST_COLUMNS);
/* Create the ImageList */
hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
ILC_COLOR32 | ILC_MASK, // ILC_COLOR24
1, 1);
/* Add event type icons to the ImageList */
ImageList_AddIcon(hSmall, LoadIconW(pSetupData->hInstance, MAKEINTRESOURCEW(IDI_ROSICON)));
ImageList_AddIcon(hSmall, LoadIconW(pSetupData->hInstance, MAKEINTRESOURCEW(IDI_WINICON)));
/* Assign the ImageList to the List View */
ListView_SetImageList(hList, hSmall, LVSIL_SMALL);
InitGenericListView(hList, pSetupData->NtOsInstallsList, AddNTOSInstallationItem);
break;
}
case WM_DESTROY:
{
hList = GetDlgItem(hwndDlg, IDC_NTOSLIST);
hSmall = ListView_GetImageList(hList, LVSIL_SMALL);
ListView_SetImageList(hList, NULL, LVSIL_SMALL);
ImageList_Destroy(hSmall);
return TRUE;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_SKIPUPGRADE:
{
/* Skip the upgrade and do the usual new-installation workflow */
pSetupData->CurrentInstallation = NULL;
pSetupData->RepairUpdateFlag = FALSE;
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_DEVICEPAGE);
return TRUE;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
if (lpnm->idFrom == IDC_NTOSLIST && lpnm->code == LVN_ITEMCHANGED)
{
LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam;
if (pnmv->uChanged & LVIF_STATE) /* The state has changed */
{
/* The item has been (de)selected */
if (pnmv->uNewState & (LVIS_FOCUSED | LVIS_SELECTED))
{
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
}
else
{
/*
* Keep the "Next" button disabled. It will be enabled only
* when the user selects an installation to upgrade.
*/
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
}
}
break;
}
switch (lpnm->code)
{
#if 0
case PSN_SETACTIVE:
{
/*
* Keep the "Next" button disabled. It will be enabled only
* when the user selects an installation to upgrade.
*/
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
break;
}
#endif
case PSN_QUERYINITIALFOCUS:
{
/* Give the focus on and select the first item */
hList = GetDlgItem(hwndDlg, IDC_NTOSLIST);
ListView_SetItemState(hList, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)hList);
return TRUE;
}
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_YESNO | MB_ICONQUESTION,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Terminate page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_RESTARTPAGE);
}
/* Do not close the wizard too soon */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
case PSN_WIZNEXT: /* Set the selected data */
{
/*
* Go update only if we have available NT installations
* and we choose to do so.
*/
if (!pSetupData->NtOsInstallsList ||
GetNumberOfListEntries(pSetupData->NtOsInstallsList) == 0)
{
pSetupData->CurrentInstallation = NULL;
pSetupData->RepairUpdateFlag = FALSE;
break;
}
hList = GetDlgItem(hwndDlg, IDC_NTOSLIST);
SetCurrentListEntry(pSetupData->NtOsInstallsList,
GetSelectedListViewItem(hList));
/* Retrieve the current installation */
pSetupData->CurrentInstallation =
(PNTOS_INSTALLATION)GetListEntryData(GetCurrentListEntry(pSetupData->NtOsInstallsList));
/* We perform an upgrade */
pSetupData->RepairUpdateFlag = TRUE;
return TRUE;
}
default:
break;
}
}
break;
default:
break;
}
return FALSE;
}
static INT_PTR CALLBACK
DeviceDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PSETUPDATA pSetupData;
HWND hList;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
hList = GetDlgItem(hwndDlg, IDC_COMPUTER);
InitGenericComboList(hList, pSetupData->USetupData.ComputerList, GetSettingDescription);
hList = GetDlgItem(hwndDlg, IDC_DISPLAY);
InitGenericComboList(hList, pSetupData->USetupData.DisplayList, GetSettingDescription);
hList = GetDlgItem(hwndDlg, IDC_KEYBOARD);
InitGenericComboList(hList, pSetupData->USetupData.KeyboardList, GetSettingDescription);
// hList = GetDlgItem(hwndDlg, IDC_KEYBOARD_LAYOUT);
// InitGenericComboList(hList, pSetupData->USetupData.LayoutList, GetSettingDescription);
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;
case PSN_QUERYINITIALFOCUS:
{
/* Focus on "Computer" list */
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_COMPUTER));
return TRUE;
}
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_YESNO | MB_ICONQUESTION,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Terminate page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_RESTARTPAGE);
}
/* Do not close the wizard too soon */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
case PSN_WIZNEXT: /* Set the selected data */
{
hList = GetDlgItem(hwndDlg, IDC_COMPUTER);
SetCurrentListEntry(pSetupData->USetupData.ComputerList,
GetSelectedComboListItem(hList));
hList = GetDlgItem(hwndDlg, IDC_DISPLAY);
SetCurrentListEntry(pSetupData->USetupData.DisplayList,
GetSelectedComboListItem(hList));
hList = GetDlgItem(hwndDlg, IDC_KEYBOARD);
SetCurrentListEntry(pSetupData->USetupData.KeyboardList,
GetSelectedComboListItem(hList));
// hList = GetDlgItem(hwndDlg, IDC_KEYBOARD_LAYOUT);
// SetCurrentListEntry(pSetupData->USetupData.LayoutList,
// GetSelectedComboListItem(hList));
return TRUE;
}
default:
break;
}
}
break;
default:
break;
}
return FALSE;
}
static INT_PTR CALLBACK
SummaryDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
static WCHAR szOrgWizNextBtnText[260]; // TODO: Make it dynamic
PSETUPDATA pSetupData;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
break;
}
case WM_COMMAND:
{
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_CONFIRM_INSTALL)
{
if (IsDlgButtonChecked(hwndDlg, IDC_CONFIRM_INSTALL) == BST_CHECKED)
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
else
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
}
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
WCHAR CurrentItemText[256];
ASSERT(InstallPartition);
/* Show the current selected settings */
// FIXME! Localize
if (pSetupData->RepairUpdateFlag)
{
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
L"Upgrading/Repairing \"%s\" from \"%s\"",
pSetupData->CurrentInstallation->InstallationName,
pSetupData->CurrentInstallation->VendorName);
}
else
{
StringCchCopyW(CurrentItemText, ARRAYSIZE(CurrentItemText),
L"New ReactOS installation");
}
SetDlgItemTextW(hwndDlg, IDC_INSTALLTYPE, CurrentItemText);
SetDlgItemTextW(hwndDlg, IDC_INSTALLSOURCE, L"n/a");
SetDlgItemTextW(hwndDlg, IDC_ARCHITECTURE, L"n/a");
GetSettingDescription(GetCurrentListEntry(pSetupData->USetupData.ComputerList),
CurrentItemText,
ARRAYSIZE(CurrentItemText));
SetDlgItemTextW(hwndDlg, IDC_COMPUTER, CurrentItemText);
GetSettingDescription(GetCurrentListEntry(pSetupData->USetupData.DisplayList),
CurrentItemText,
ARRAYSIZE(CurrentItemText));
SetDlgItemTextW(hwndDlg, IDC_DISPLAY, CurrentItemText);
GetSettingDescription(GetCurrentListEntry(pSetupData->USetupData.KeyboardList),
CurrentItemText,
ARRAYSIZE(CurrentItemText));
SetDlgItemTextW(hwndDlg, IDC_KEYBOARD, CurrentItemText);
if (InstallVolume->Info.DriveLetter)
{
#if 0
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
L"%c: \x2014 %wZ",
InstallVolume->Info.DriveLetter,
&pSetupData->USetupData.DestinationRootPath);
#else
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
L"%c: \x2014 Harddisk %lu, Partition %lu",
InstallVolume->Info.DriveLetter,
InstallPartition->DiskEntry->DiskNumber,
InstallPartition->OnDiskPartitionNumber);
#endif
}
else
{
#if 0
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
L"%wZ",
&pSetupData->USetupData.DestinationRootPath);
#else
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
L"Harddisk %lu, Partition %lu",
InstallPartition->DiskEntry->DiskNumber,
InstallPartition->OnDiskPartitionNumber);
#endif
}
SetDlgItemTextW(hwndDlg, IDC_DESTDRIVE, CurrentItemText);
SetDlgItemTextW(hwndDlg, IDC_PATH,
pSetupData->USetupData.InstallationDirectory
/*pSetupData->USetupData.InstallPath.Buffer*/);
/* Change the "Next" button text to "Install" */
// PropSheet_SetNextText(GetParent(hwndDlg), ...);
GetDlgItemTextW(GetParent(hwndDlg), ID_WIZNEXT,
szOrgWizNextBtnText, ARRAYSIZE(szOrgWizNextBtnText));
SetWindowResTextW(GetDlgItem(GetParent(hwndDlg), ID_WIZNEXT),
pSetupData->hInstance,
IDS_INSTALLBTN);
/*
* Keep the "Next" button disabled. It will be enabled only
* when the user clicks on the installation approval checkbox.
*/
CheckDlgButton(hwndDlg, IDC_CONFIRM_INSTALL, BST_UNCHECKED);
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
break;
}
case PSN_QUERYINITIALFOCUS:
{
/* Focus on the confirmation check-box */
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)GetDlgItem(hwndDlg, IDC_CONFIRM_INSTALL));
return TRUE;
}
case PSN_KILLACTIVE:
{
/* Restore the original "Next" button text */
SetDlgItemTextW(GetParent(hwndDlg), ID_WIZNEXT, szOrgWizNextBtnText);
break;
}
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_YESNO | MB_ICONQUESTION,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Terminate page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_RESTARTPAGE);
}
/* Do not close the wizard too soon */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
default:
break;
}
break;
}
default:
break;
}
return FALSE;
}
typedef struct _FSVOL_CONTEXT
{
PSETUPDATA pSetupData;
// PAGE_NUMBER NextPageOnAbort;
} FSVOL_CONTEXT, *PFSVOL_CONTEXT;
static
BOOLEAN
NTAPI
FormatCallback(
_In_ CALLBACKCOMMAND Command,
_In_ ULONG Modifier,
_In_ PVOID Argument)
{
switch (Command)
{
case PROGRESS:
{
PULONG Percent = (PULONG)Argument;
DPRINT("%lu percent completed\n", *Percent);
SendMessageW(UiContext.hWndProgress, PBM_SETPOS, *Percent, 0);
break;
}
#if 0
case OUTPUT:
{
PTEXTOUTPUT output = (PTEXTOUTPUT)Argument;
DPRINT("%s\n", output->Output);
break;
}
#endif
case DONE:
{
#if 0
PBOOLEAN Success = (PBOOLEAN)Argument;
if (*Success == FALSE)
{
DPRINT("FormatEx was unable to complete successfully.\n\n");
}
#endif
DPRINT("Done\n");
break;
}
default:
DPRINT("Unknown callback %lu\n", (ULONG)Command);
break;
}
return TRUE;
}
static
BOOLEAN
NTAPI
ChkdskCallback(
_In_ CALLBACKCOMMAND Command,
_In_ ULONG Modifier,
_In_ PVOID Argument)
{
switch (Command)
{
default:
DPRINT("Unknown callback %lu\n", (ULONG)Command);
break;
}
return TRUE;
}
// PFSVOL_CALLBACK
static FSVOL_OP
CALLBACK
FsVolCallback(
_In_opt_ PVOID Context,
_In_ FSVOLNOTIFY FormatStatus,
_In_ ULONG_PTR Param1,
_In_ ULONG_PTR Param2)
{
PFSVOL_CONTEXT FsVolContext = (PFSVOL_CONTEXT)Context;
switch (FormatStatus)
{
// FIXME: Deprecate!
case ChangeSystemPartition:
{
// PPARTENTRY SystemPartition = (PPARTENTRY)Param1;
// FsVolContext->NextPageOnAbort = SELECT_PARTITION_PAGE;
// if (ChangeSystemPartitionPage(Ir, SystemPartition))
// return FSVOL_DOIT;
return FSVOL_ABORT;
}
case FSVOLNOTIFY_PARTITIONERROR:
{
switch (Param1)
{
case STATUS_PARTITION_FAILURE:
{
// ERROR_WRITE_PTABLE
DisplayError(NULL,
0, // Default to "Error"
IDS_ERROR_WRITE_PTABLE);
// FsVolContext->NextPageOnAbort = QUIT_PAGE;
// TODO: Go back to the partitioning page?
break;
}
case ERROR_SYSTEM_PARTITION_NOT_FOUND:
{
/* FIXME: improve the error dialog */
//
// Error dialog should say that we cannot find a suitable
// system partition and create one on the system. At this point,
// it may be nice to ask the user whether he wants to continue,
// or use an external drive as the system drive/partition
// (e.g. floppy, USB drive, etc...)
//
DisplayError(NULL,
0, // Default to "Error"
IDS_ERROR_SYSTEM_PARTITION);
// FsVolContext->NextPageOnAbort = SELECT_PARTITION_PAGE;
// TODO: Go back to the partitioning page
break;
}
default:
break;
}
return FSVOL_ABORT;
}
case FSVOLNOTIFY_STARTQUEUE:
case FSVOLNOTIFY_ENDQUEUE:
// NOTE: If needed, clear progress gauges.
return FSVOL_DOIT;
case FSVOLNOTIFY_STARTSUBQUEUE:
{
if ((FSVOL_OP)Param1 == FSVOL_FORMAT)
{
/*
* In case we just repair an existing installation, or make
* an unattended setup without formatting, just go to the
* filesystem check step.
*/
if (FsVolContext->pSetupData->RepairUpdateFlag)
return FSVOL_SKIP; /** HACK!! **/
if (IsUnattendedSetup && !FsVolContext->pSetupData->USetupData.FormatPartition)
return FSVOL_SKIP; /** HACK!! **/
/* Set status text */
SetDlgItemTextW(UiContext.hwndDlg, IDC_ITEM, L"");
}
else
if ((FSVOL_OP)Param1 == FSVOL_CHECK)
{
/* Set status text */
SetDlgItemTextW(UiContext.hwndDlg, IDC_ITEM, L"");
/* Filechecking step: set progress marquee style and start it up */
UiContext.dwPbStyle = GetWindowLongPtrW(UiContext.hWndProgress, GWL_STYLE);
SetWindowLongPtrW(UiContext.hWndProgress, GWL_STYLE, UiContext.dwPbStyle | PBS_MARQUEE);
SendMessageW(UiContext.hWndProgress, PBM_SETMARQUEE, TRUE, 0);
}
return FSVOL_DOIT;
}
case FSVOLNOTIFY_ENDSUBQUEUE:
{
if ((FSVOL_OP)Param1 == FSVOL_CHECK)
{
/* File-checking finished: stop the progress bar and restore its style */
SendMessageW(UiContext.hWndProgress, PBM_SETMARQUEE, FALSE, 0);
SetWindowLongPtrW(UiContext.hWndProgress, GWL_STYLE, UiContext.dwPbStyle);
}
return 0;
}
case FSVOLNOTIFY_FORMATERROR:
{
PFORMAT_VOLUME_INFO FmtInfo = (PFORMAT_VOLUME_INFO)Param1;
// FIXME: See also FSVOLNOTIFY_PARTITIONERROR
if (FmtInfo->ErrorStatus == STATUS_PARTITION_FAILURE)
{
// ERROR_WRITE_PTABLE
DisplayError(NULL,
0, // Default to "Error"
IDS_ERROR_WRITE_PTABLE);
// FsVolContext->NextPageOnAbort = QUIT_PAGE;
// TODO: Go back to the partitioning page?
return FSVOL_ABORT;
}
else
if (FmtInfo->ErrorStatus == STATUS_UNRECOGNIZED_VOLUME)
{
/* FIXME: show an error dialog */
// MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, PathBuffer);
DisplayError(NULL,
0, // Default to "Error"
IDS_ERROR_FORMAT_UNRECOGNIZED_VOLUME);
// FsVolContext->NextPageOnAbort = QUIT_PAGE;
return FSVOL_ABORT;
}
else
if (FmtInfo->ErrorStatus == STATUS_NOT_SUPPORTED)
{
INT nRet;
nRet = DisplayMessage(NULL, MB_ICONERROR | MB_OKCANCEL,
NULL, // Default to "Error"
MAKEINTRESOURCEW(IDS_ERROR_COULD_NOT_FORMAT),
FmtInfo->FileSystemName);
if (nRet == IDCANCEL)
{
// FsVolContext->NextPageOnAbort = QUIT_PAGE;
return FSVOL_ABORT;
}
else if (nRet == IDOK)
{
return FSVOL_RETRY;
}
}
else if (!NT_SUCCESS(FmtInfo->ErrorStatus))
{
ASSERT(*FmtInfo->Volume->Info.DeviceName);
DPRINT1("FormatPartition() failed with status 0x%08lx\n", FmtInfo->ErrorStatus);
// ERROR_FORMATTING_PARTITION
DisplayError(NULL,
0, // Default to "Error"
IDS_ERROR_FORMATTING_PARTITION,
FmtInfo->Volume->Info.DeviceName);
// FsVolContext->NextPageOnAbort = QUIT_PAGE;
return FSVOL_ABORT;
}
return FSVOL_RETRY;
}
case FSVOLNOTIFY_CHECKERROR:
{
PCHECK_VOLUME_INFO ChkInfo = (PCHECK_VOLUME_INFO)Param1;
if (ChkInfo->ErrorStatus == STATUS_NOT_SUPPORTED)
{
INT nRet;
nRet = DisplayMessage(NULL, MB_ICONERROR | MB_OKCANCEL,
NULL, // Default to "Error"
MAKEINTRESOURCEW(IDS_ERROR_COULD_NOT_CHECK),
ChkInfo->Volume->Info.FileSystem);
if (nRet == IDCANCEL)
{
// FsVolContext->NextPageOnAbort = QUIT_PAGE;
return FSVOL_ABORT;
}
else if (nRet == IDOK)
{
return FSVOL_SKIP;
}
}
else if (!NT_SUCCESS(ChkInfo->ErrorStatus))
{
DPRINT1("ChkdskPartition() failed with status 0x%08lx\n", ChkInfo->ErrorStatus);
DisplayError(NULL,
0, // Default to "Error"
IDS_ERROR_CHECKING_PARTITION,
ChkInfo->ErrorStatus);
return FSVOL_SKIP;
}
return FSVOL_SKIP;
}
case FSVOLNOTIFY_STARTFORMAT:
{
PFORMAT_VOLUME_INFO FmtInfo = (PFORMAT_VOLUME_INFO)Param1;
PVOL_CREATE_INFO VolCreate;
ASSERT((FSVOL_OP)Param2 == FSVOL_FORMAT);
/* Find the volume info in the partition TreeList UI.
* If none, don't format it. */
VolCreate = FindVolCreateInTreeByVolume(UiContext.hPartList,
FmtInfo->Volume);
if (!VolCreate)
return FSVOL_SKIP;
ASSERT(VolCreate->Volume == FmtInfo->Volume);
/* If there is no formatting information, skip it */
if (!*VolCreate->FileSystemName)
return FSVOL_SKIP;
ASSERT(*FmtInfo->Volume->Info.DeviceName);
/* Set status text */
if (FmtInfo->Volume->Info.DriveLetter)
{
SetWindowResPrintfW(GetDlgItem(UiContext.hwndDlg, IDC_ITEM),
SetupData.hInstance,
IDS_FORMATTING_PROGRESS1, // L"Formatting volume %c: (%s) in %s..."
FmtInfo->Volume->Info.DriveLetter,
FmtInfo->Volume->Info.DeviceName,
VolCreate->FileSystemName);
}
else
{
SetWindowResPrintfW(GetDlgItem(UiContext.hwndDlg, IDC_ITEM),
SetupData.hInstance,
IDS_FORMATTING_PROGRESS2, // L"Formatting volume %s in %s..."
FmtInfo->Volume->Info.DeviceName,
VolCreate->FileSystemName);
}
// StartFormat(FmtInfo, FileSystemList->Selected);
FmtInfo->FileSystemName = VolCreate->FileSystemName;
FmtInfo->MediaFlag = VolCreate->MediaFlag;
FmtInfo->Label = VolCreate->Label;
FmtInfo->QuickFormat = VolCreate->QuickFormat;
FmtInfo->ClusterSize = VolCreate->ClusterSize;
FmtInfo->Callback = FormatCallback;
/* Set up the progress bar */
SendMessageW(UiContext.hWndProgress,
PBM_SETRANGE, 0, MAKELPARAM(0, 100));
SendMessageW(UiContext.hWndProgress,
PBM_SETPOS, 0, 0);
return FSVOL_DOIT;
}
case FSVOLNOTIFY_ENDFORMAT:
{
PFORMAT_VOLUME_INFO FmtInfo = (PFORMAT_VOLUME_INFO)Param1;
// EndFormat(FmtInfo->ErrorStatus);
if (FmtInfo->FileSystemName)
*(PWSTR)FmtInfo->FileSystemName = UNICODE_NULL; // FIXME: HACK!
// /* Reset the file system list */
// ResetFileSystemList();
return 0;
}
case FSVOLNOTIFY_STARTCHECK:
{
PCHECK_VOLUME_INFO ChkInfo = (PCHECK_VOLUME_INFO)Param1;
PVOL_CREATE_INFO VolCreate;
ASSERT((FSVOL_OP)Param2 == FSVOL_CHECK);
/* Find the volume info in the partition TreeList UI.
* If none, don't check it. */
VolCreate = FindVolCreateInTreeByVolume(UiContext.hPartList,
ChkInfo->Volume);
if (!VolCreate)
return FSVOL_SKIP;
ASSERT(VolCreate->Volume == ChkInfo->Volume);
ASSERT(*ChkInfo->Volume->Info.DeviceName);
/* Set status text */
if (ChkInfo->Volume->Info.DriveLetter)
{
SetWindowResPrintfW(GetDlgItem(UiContext.hwndDlg, IDC_ITEM),
SetupData.hInstance,
IDS_CHECKING_PROGRESS1, // L"Checking volume %c: (%s)..."
ChkInfo->Volume->Info.DriveLetter,
ChkInfo->Volume->Info.DeviceName);
}
else
{
SetWindowResPrintfW(GetDlgItem(UiContext.hwndDlg, IDC_ITEM),
SetupData.hInstance,
IDS_CHECKING_PROGRESS2, // L"Checking volume %s..."
ChkInfo->Volume->Info.DeviceName);
}
// StartCheck(ChkInfo);
// TODO: Think about which values could be defaulted...
ChkInfo->FixErrors = TRUE;
ChkInfo->Verbose = FALSE;
ChkInfo->CheckOnlyIfDirty = TRUE;
ChkInfo->ScanDrive = FALSE;
ChkInfo->Callback = ChkdskCallback;
return FSVOL_DOIT;
}
case FSVOLNOTIFY_ENDCHECK:
{
// PCHECK_VOLUME_INFO ChkInfo = (PCHECK_VOLUME_INFO)Param1;
// EndCheck(ChkInfo->ErrorStatus);
return 0;
}
}
return 0;
}
typedef struct _COPYCONTEXT
{
PSETUPDATA pSetupData;
ULONG TotalOperations;
ULONG CompletedOperations;
} COPYCONTEXT, *PCOPYCONTEXT;
static UINT
CALLBACK
FileCopyCallback(PVOID Context,
UINT Notification,
UINT_PTR Param1,
UINT_PTR Param2)
{
PCOPYCONTEXT CopyContext = (PCOPYCONTEXT)Context;
PFILEPATHS_W FilePathInfo;
PCWSTR SrcFileName, DstFileName;
WaitForSingleObject(CopyContext->pSetupData->hHaltInstallEvent, INFINITE);
if (CopyContext->pSetupData->bStopInstall)
return FILEOP_ABORT; // Stop committing files
switch (Notification)
{
case SPFILENOTIFY_STARTSUBQUEUE:
{
CopyContext->TotalOperations = (ULONG)Param2;
CopyContext->CompletedOperations = 0;
/* Set up the progress bar */
SendMessageW(UiContext.hWndProgress,
PBM_SETRANGE, 0,
MAKELPARAM(0, CopyContext->TotalOperations));
SendMessageW(UiContext.hWndProgress,
PBM_SETSTEP, 1, 0);
SendMessageW(UiContext.hWndProgress,
PBM_SETPOS, 0, 0);
break;
}
case SPFILENOTIFY_STARTDELETE:
case SPFILENOTIFY_STARTRENAME:
case SPFILENOTIFY_STARTCOPY:
{
FilePathInfo = (PFILEPATHS_W)Param1;
if (Notification == SPFILENOTIFY_STARTDELETE)
{
/* Display delete message */
ASSERT(Param2 == FILEOP_DELETE);
DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
if (DstFileName) ++DstFileName;
else DstFileName = FilePathInfo->Target;
SetWindowResPrintfW(UiContext.hWndItem,
SetupData.hInstance,
IDS_DELETING, // STRING_DELETING
DstFileName);
}
else if (Notification == SPFILENOTIFY_STARTRENAME)
{
UINT uMsgID;
/* Display move/rename message */
ASSERT(Param2 == FILEOP_RENAME);
SrcFileName = wcsrchr(FilePathInfo->Source, L'\\');
if (SrcFileName) ++SrcFileName;
else SrcFileName = FilePathInfo->Source;
DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
if (DstFileName) ++DstFileName;
else DstFileName = FilePathInfo->Target;
if (!_wcsicmp(SrcFileName, DstFileName))
uMsgID = IDS_MOVING; // STRING_MOVING
else
uMsgID = IDS_RENAMING; // STRING_RENAMING
SetWindowResPrintfW(UiContext.hWndItem,
SetupData.hInstance,
uMsgID,
SrcFileName, DstFileName);
}
else if (Notification == SPFILENOTIFY_STARTCOPY)
{
/* Display copy message */
ASSERT(Param2 == FILEOP_COPY);
DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
if (DstFileName) ++DstFileName;
else DstFileName = FilePathInfo->Target;
SetWindowResPrintfW(UiContext.hWndItem,
SetupData.hInstance,
IDS_COPYING, // STRING_COPYING
DstFileName);
}
break;
}
case SPFILENOTIFY_COPYERROR:
{
FilePathInfo = (PFILEPATHS_W)Param1;
DPRINT1("An error happened while trying to copy file '%S' (error 0x%08lx), skipping it...\n",
FilePathInfo->Target, FilePathInfo->Win32Error);
return FILEOP_SKIP;
}
case SPFILENOTIFY_ENDDELETE:
case SPFILENOTIFY_ENDRENAME:
case SPFILENOTIFY_ENDCOPY:
{
CopyContext->CompletedOperations++;
/* SYSREG checkpoint */
if (CopyContext->TotalOperations >> 1 == CopyContext->CompletedOperations)
DPRINT1("CHECKPOINT:HALF_COPIED\n");
SendMessageW(UiContext.hWndProgress, PBM_STEPIT, 0, 0);
break;
}
}
return FILEOP_DOIT;
}
static VOID
__cdecl
RegistryStatus(IN REGISTRY_STATUS RegStatus, ...)
{
/* WARNING: Please keep this lookup table in sync with the resources! */
static const UINT StringIDs[] =
{
IDS_REG_DONE, /* Success */
IDS_REG_REGHIVEUPDATE, /* RegHiveUpdate */
IDS_REG_IMPORTFILE, /* ImportRegHive */
IDS_REG_DISPLAYSETTINGSUPDATE, /* DisplaySettingsUpdate */
IDS_REG_LOCALESETTINGSUPDATE, /* LocaleSettingsUpdate */
IDS_REG_ADDKBLAYOUTS, /* KeybLayouts */
IDS_REG_KEYBOARDSETTINGSUPDATE, /* KeybSettingsUpdate */
IDS_REG_CODEPAGEINFOUPDATE, /* CodePageInfoUpdate */
};
if (RegStatus < _countof(StringIDs))
{
va_list args;
va_start(args, RegStatus);
SetWindowResPrintfVW(UiContext.hWndItem, SetupData.hInstance, StringIDs[RegStatus], args);
va_end(args);
}
else
{
SetWindowResPrintfW(UiContext.hWndItem, SetupData.hInstance, IDS_REG_UNKNOWN, RegStatus);
}
SendMessageW(UiContext.hWndProgress, PBM_STEPIT, 0, 0);
}
/**
* @brief
* Enables or disables the Cancel and the Close title-bar
* property-sheet window buttons.
**/
VOID
PropSheet_SetCloseCancel(
_In_ HWND hWndWiz,
_In_ BOOL Enable)
{
EnableDlgItem(hWndWiz, IDCANCEL, Enable);
// ShowDlgItem(hWndWiz, IDCANCEL, Enable ? SW_SHOW : SW_HIDE);
EnableMenuItem(GetSystemMenu(hWndWiz, FALSE),
SC_CLOSE,
MF_BYCOMMAND | (Enable ? MF_ENABLED : MF_GRAYED));
}
static DWORD
WINAPI
PrepareAndDoCopyThread(
IN LPVOID Param)
{
PSETUPDATA pSetupData;
HWND hwndDlg = (HWND)Param;
HWND hWndProgress;
LONG_PTR dwStyle;
ERROR_NUMBER ErrorNumber;
BOOLEAN Success;
NTSTATUS Status;
FSVOL_CONTEXT FsVolContext;
COPYCONTEXT CopyContext;
WCHAR PathBuffer[RTL_NUMBER_OF_FIELD(PARTENTRY, DeviceName) + 1];
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
/* Get the progress handle */
hWndProgress = GetDlgItem(hwndDlg, IDC_PROCESSPROGRESS);
/* Setup global UI context */
UiContext.hwndDlg = hwndDlg;
UiContext.hWndItem = GetDlgItem(hwndDlg, IDC_ITEM);
UiContext.hWndProgress = hWndProgress;
UiContext.dwPbStyle = 0;
/* Disable the Close/Cancel buttons during all partition operations */
// TODO: Consider, alternatively, to just show an info-box saying
// that the installation process cannot be canceled at this stage?
// PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
PropSheet_SetCloseCancel(GetParent(hwndDlg), FALSE);
/*
* Find/Set the system partition, and apply all pending partition operations.
*/
/* Create context for the volume/partition operations */
FsVolContext.pSetupData = pSetupData;
/* Set status text */
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
pSetupData->hInstance,
IDS_CONFIG_SYSTEM_PARTITION);
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
/* Find or set the active system partition before starting formatting */
Success = InitSystemPartition(pSetupData->PartitionList,
InstallPartition,
&SystemPartition,
FsVolCallback,
&FsVolContext);
// if (!Success)
// return FsVolContext.NextPageOnAbort;
//
// FIXME?? If cannot use any system partition, install FreeLdr on floppy / removable media??
//
if (!Success)
{
/* Display an error if an unexpected failure happened */
MessageBoxW(GetParent(hwndDlg), L"Failed to find or set the system partition!", L"Error", MB_ICONERROR);
/* Re-enable the Close/Cancel buttons */
PropSheet_SetCloseCancel(GetParent(hwndDlg), TRUE);
/*
* We failed due to an unexpected error, keep on the copy page to view the current state,
* but enable the "Next" button to allow the user to continue to the terminate page.
*/
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
return 1;
}
/* Set status text */
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
pSetupData->hInstance,
IDS_PREPARE_PARTITIONS);
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
/* Apply all pending operations on partitions: formatting and checking */
Success = FsVolCommitOpsQueue(pSetupData->PartitionList,
SystemVolume,
InstallVolume,
FsVolCallback,
&FsVolContext);
if (!Success)
{
/* Display an error if an unexpected failure happened */
MessageBoxW(GetParent(hwndDlg), L"Failed to prepare the partitions!", L"Error", MB_ICONERROR);
/* Re-enable the Close/Cancel buttons */
PropSheet_SetCloseCancel(GetParent(hwndDlg), TRUE);
/*
* We failed due to an unexpected error, keep on the copy page to view the current state,
* but enable the "Next" button to allow the user to continue to the terminate page.
*/
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
return 1;
}
/* Re-enable the Close/Cancel buttons */
PropSheet_SetCloseCancel(GetParent(hwndDlg), TRUE);
/* Re-calculate the final destination paths */
ASSERT(InstallPartition);
Status = InitDestinationPaths(&pSetupData->USetupData,
NULL, // pSetupData->USetupData.InstallationDirectory,
InstallVolume);
if (!NT_SUCCESS(Status))
{
DisplayMessage(GetParent(hwndDlg), MB_ICONERROR, L"Error", L"InitDestinationPaths() failed with status 0x%08lx\n", Status);
/*
* We failed due to an unexpected error, keep on the copy page to view the current state,
* but enable the "Next" button to allow the user to continue to the terminate page.
*/
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
return 1;
}
/*
* Preparation of the list of files to be copied
*/
/* Set status text */
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
pSetupData->hInstance,
IDS_PREPARE_FILES);
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
/* Set progress marquee style and start it up */
dwStyle = GetWindowLongPtrW(hWndProgress, GWL_STYLE);
SetWindowLongPtrW(hWndProgress, GWL_STYLE, dwStyle | PBS_MARQUEE);
SendMessageW(hWndProgress, PBM_SETMARQUEE, TRUE, 0);
/* Prepare the list of files */
/* ErrorNumber = */ Success = PrepareFileCopy(&pSetupData->USetupData, NULL);
/* Stop progress and restore its style */
SendMessageW(hWndProgress, PBM_SETMARQUEE, FALSE, 0);
SetWindowLongPtrW(hWndProgress, GWL_STYLE, dwStyle);
if (/*ErrorNumber != ERROR_SUCCESS*/ !Success)
{
/* Display an error only if an unexpected failure happened, and not because the user cancelled the installation */
if (!pSetupData->bStopInstall)
MessageBoxW(GetParent(hwndDlg), L"Failed to prepare the list of files!", L"Error", MB_ICONERROR);
/*
* If we failed due to an unexpected error, keep on the copy page to view the current state,
* but enable the "Next" button to allow the user to continue to the terminate page.
* Otherwise we have been cancelled by the user, who has already switched to the Terminate page.
*/
if (!pSetupData->bStopInstall)
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
return 1;
}
/*
* Perform the file copy
*/
/* Set status text */
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
pSetupData->hInstance,
IDS_COPYING_FILES);
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
/* Create context for the copy process */
CopyContext.pSetupData = pSetupData;
CopyContext.TotalOperations = 0;
CopyContext.CompletedOperations = 0;
/* Do the file copying - The callback handles whether or not we should stop file copying */
if (!DoFileCopy(&pSetupData->USetupData, FileCopyCallback, &CopyContext))
{
/* Display an error only if an unexpected failure happened, and not because the user cancelled the installation */
if (!pSetupData->bStopInstall)
MessageBoxW(GetParent(hwndDlg), L"Failed to copy the files!", L"Error", MB_ICONERROR);
/*
* If we failed due to an unexpected error, keep on the copy page to view the current state,
* but enable the "Next" button to allow the user to continue to the terminate page.
* Otherwise we have been cancelled by the user, who has already switched to the Terminate page.
*/
if (!pSetupData->bStopInstall)
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
return 1;
}
// /* Set status text */
// SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
// pSetupData->hInstance,
// IDS_INSTALL_FINALIZE);
// SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
/* Create the $winnt$.inf file */
InstallSetupInfFile(&pSetupData->USetupData);
/*
* Create or update the registry hives
*/
/* Set status text */
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
pSetupData->hInstance,
pSetupData->RepairUpdateFlag ? IDS_UPDATE_REGISTRY
: IDS_CREATE_REGISTRY);
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
/* Set up the progress bar */
SendMessageW(hWndProgress,
PBM_SETRANGE, 0,
MAKELPARAM(0, 8)); // FIXME: hardcoded number of steps, see StringIDs[] array in RegistryStatus()
SendMessageW(hWndProgress,
PBM_SETSTEP, 1, 0);
SendMessageW(hWndProgress,
PBM_SETPOS, 0, 0);
ErrorNumber = UpdateRegistry(&pSetupData->USetupData,
pSetupData->RepairUpdateFlag,
pSetupData->PartitionList,
InstallVolume->Info.DriveLetter,
pSetupData->SelectedLanguageId,
RegistryStatus,
NULL /* SubstSettings */);
DBG_UNREFERENCED_PARAMETER(ErrorNumber);
SendMessageW(UiContext.hWndProgress, PBM_SETPOS, 100, 0);
/*
* And finally, install the bootloader
*/
/* Set status text */
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_ACTIVITY),
pSetupData->hInstance,
IDS_INSTALL_BOOTLOADER);
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
RtlFreeUnicodeString(&pSetupData->USetupData.SystemRootPath);
StringCchPrintfW(PathBuffer, _countof(PathBuffer),
L"%s\\", SystemPartition->DeviceName);
RtlCreateUnicodeString(&pSetupData->USetupData.SystemRootPath, PathBuffer);
DPRINT1("SystemRootPath: %wZ\n", &pSetupData->USetupData.SystemRootPath);
switch (pSetupData->USetupData.BootLoaderLocation)
{
/* Install on removable disk */
case 1:
{
// TODO: So far SETUP only supports the 1st floppy.
// Use a simple UI like comdlg32's DlgDirList* to show
// a list of drives that the user could select.
static const UNICODE_STRING FloppyDrive = RTL_CONSTANT_STRING(L"\\Device\\Floppy0\\");
static const WCHAR DriveLetter = L'A';
INT nRet;
RetryCancel:
nRet = DisplayMessage(GetParent(hwndDlg),
MB_ICONINFORMATION | MB_OKCANCEL,
L"Bootloader installation",
L"Please insert a blank floppy disk in drive %c: .\n"
L"All data in the floppy disk will be erased!\n"
L"\nClick on OK to continue."
L"\nClick on CANCEL to skip bootloader installation.",
DriveLetter);
if (nRet != IDOK)
break; /* Skip installation */
Retry:
Status = InstallBootcodeToRemovable(pSetupData->USetupData.ArchType,
&FloppyDrive,
&pSetupData->USetupData.SourceRootPath,
&pSetupData->USetupData.DestinationArcPath);
if (Status == STATUS_SUCCESS)
break; /* Successful installation */
if (Status == STATUS_DEVICE_NOT_READY)
{
// ERROR_NO_FLOPPY
nRet = DisplayMessage(GetParent(hwndDlg),
MB_ICONWARNING | MB_RETRYCANCEL,
NULL, // Default to "Error"
L"No disk detected in drive %c: .",
DriveLetter);
if (nRet == IDRETRY)
goto Retry;
}
else if ((Status == ERROR_WRITE_BOOT) ||
(Status == ERROR_INSTALL_BOOTCODE))
{
/* Error when writing the boot code */
DisplayError(GetParent(hwndDlg),
0, // Default to "Error"
IDS_ERROR_INSTALL_BOOTCODE_REMOVABLE);
}
else if (!NT_SUCCESS(Status))
{
/* Any other NTSTATUS failure code */
DPRINT1("InstallBootcodeToRemovable() failed: Status 0x%lx\n", Status);
DisplayError(GetParent(hwndDlg),
0, // Default to "Error"
IDS_ERROR_BOOTLDR_FAILED,
Status);
}
goto RetryCancel;
}
/* Install on hard-disk */
case 2: // System partition / MBR and VBR (on BIOS-based PC)
case 3: // VBR only (on BIOS-based PC)
{
/* Copy FreeLoader to the disk and save the boot entries */
Status = InstallBootManagerAndBootEntries(
pSetupData->USetupData.ArchType,
&pSetupData->USetupData.SystemRootPath,
&pSetupData->USetupData.SourceRootPath,
&pSetupData->USetupData.DestinationArcPath,
(pSetupData->USetupData.BootLoaderLocation == 2)
? 1 /* Install MBR and VBR */
: 0 /* Install VBR only */);
if (Status == STATUS_SUCCESS)
break; /* Successful installation */
if (Status == ERROR_WRITE_BOOT)
{
/* Error when writing the VBR */
DisplayError(GetParent(hwndDlg),
0, // Default to "Error"
IDS_ERROR_WRITE_BOOT,
SystemVolume->Info.FileSystem);
}
else if (Status == ERROR_INSTALL_BOOTCODE)
{
/* Error when writing the MBR */
DisplayError(GetParent(hwndDlg),
0, // Default to "Error"
IDS_ERROR_INSTALL_BOOTCODE,
L"MBR");
}
else if (Status == STATUS_NOT_SUPPORTED)
{
DisplayError(GetParent(hwndDlg),
0, // Default to "Error"
IDS_ERROR_BOOTLDR_ARCH_UNSUPPORTED);
}
else if (!NT_SUCCESS(Status))
{
/* Any other NTSTATUS failure code */
DPRINT1("InstallBootManagerAndBootEntries() failed: Status 0x%lx\n", Status);
DisplayError(GetParent(hwndDlg),
0, // Default to "Error"
IDS_ERROR_BOOTLDR_FAILED,
Status);
}
break;
}
/* Skip installation */
case 0:
default:
break;
}
/* We are done! Switch to the Terminate page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_RESTARTPAGE);
return 0;
}
static INT_PTR CALLBACK
ProcessDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PSETUPDATA pSetupData;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
/* Reset status text */
SetDlgItemTextW(hwndDlg, IDC_ACTIVITY, L"");
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
/* Create the file-copy halt (manual-reset) event */
pSetupData->hHaltInstallEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
if (!pSetupData->hHaltInstallEvent)
break;
pSetupData->bStopInstall = FALSE;
/* Start the prepare-and-copy files thread */
pSetupData->hInstallThread =
CreateThread(NULL, 0,
PrepareAndDoCopyThread,
(PVOID)hwndDlg,
CREATE_SUSPENDED,
NULL);
if (!pSetupData->hInstallThread)
{
CloseHandle(pSetupData->hHaltInstallEvent);
pSetupData->hHaltInstallEvent = NULL;
MessageBoxW(GetParent(hwndDlg), L"Cannot create the prepare-and-copy files thread!", L"Error", MB_ICONERROR);
break;
}
/* Disable all buttons during installation process - buttons will be reenabled by the installation thread */
PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
/* Resume the installation thread */
ResumeThread(pSetupData->hInstallThread);
break;
}
case PSN_QUERYCANCEL:
{
/* Halt the on-going file copy */
ResetEvent(pSetupData->hHaltInstallEvent);
if (DisplayMessage(GetParent(hwndDlg),
MB_YESNO | MB_ICONQUESTION,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Stop the file copy thread */
pSetupData->bStopInstall = TRUE;
SetEvent(pSetupData->hHaltInstallEvent);
#if 0
/* Wait for any pending installation */
WaitForSingleObject(pSetupData->hInstallThread, INFINITE);
CloseHandle(pSetupData->hInstallThread);
pSetupData->hInstallThread = NULL;
CloseHandle(pSetupData->hHaltInstallEvent);
pSetupData->hHaltInstallEvent = NULL;
#endif
// TODO: Unwind installation?!
/* Go to the Terminate page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_RESTARTPAGE);
}
else
{
/* We don't stop installation, resume file copy */
SetEvent(pSetupData->hHaltInstallEvent);
}
/* Do not close the wizard too soon */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
default:
break;
}
}
break;
default:
break;
}
return FALSE;
}
static INT_PTR CALLBACK
RestartDlgProc(
IN HWND hwndDlg,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PSETUPDATA pSetupData;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
/* Set title font */
SetDlgItemFont(hwndDlg, IDC_FINISHTITLE, pSetupData->hTitleFont, TRUE);
break;
case WM_TIMER:
{
INT Position;
HWND hWndProgress;
hWndProgress = GetDlgItem(hwndDlg, IDC_RESTART_PROGRESS);
Position = SendMessageW(hWndProgress, PBM_GETPOS, 0, 0);
if (Position == 300)
{
KillTimer(hwndDlg, 1);
PropSheet_PressButton(GetParent(hwndDlg), PSBTN_FINISH);
}
else
{
SendMessageW(hWndProgress, PBM_SETPOS, Position + 1, 0);
}
return TRUE;
}
case WM_DESTROY:
return TRUE;
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
/* Only "Finish" for closing the wizard */
ShowDlgItem(GetParent(hwndDlg), IDCANCEL, SW_HIDE);
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
/* Set up the reboot progress bar */
SendDlgItemMessage(hwndDlg, IDC_RESTART_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 300));
SendDlgItemMessage(hwndDlg, IDC_RESTART_PROGRESS, PBM_SETPOS, 0, 0);
SetTimer(hwndDlg, 1, 50, NULL);
break;
}
default:
break;
}
}
break;
default:
break;
}
return FALSE;
}
BOOL LoadSetupData(
IN OUT PSETUPDATA pSetupData)
{
pSetupData->PartitionList = CreatePartitionList();
if (!pSetupData->PartitionList)
{
DPRINT1("Could not enumerate available disks; failing installation\n");
return FALSE;
}
pSetupData->NtOsInstallsList = CreateNTOSInstallationsList(pSetupData->PartitionList);
if (!pSetupData->NtOsInstallsList)
DPRINT1("Failed to get a list of NTOS installations; continue installation...\n");
/* Load the hardware, language and keyboard layout lists */
pSetupData->USetupData.ComputerList = CreateComputerTypeList(pSetupData->USetupData.SetupInf);
pSetupData->USetupData.DisplayList = CreateDisplayDriverList(pSetupData->USetupData.SetupInf);
pSetupData->USetupData.KeyboardList = CreateKeyboardDriverList(pSetupData->USetupData.SetupInf);
pSetupData->USetupData.LanguageList = CreateLanguageList(pSetupData->USetupData.SetupInf, pSetupData->DefaultLanguage);
/* If not unattended, overwrite language and locale with
* the current ones of the running ReactOS instance */
if (!IsUnattendedSetup)
{
LCID LocaleID = GetUserDefaultLCID();
StringCchPrintfW(pSetupData->DefaultLanguage,
_countof(pSetupData->DefaultLanguage),
L"%08lx", LocaleID);
StringCchPrintfW(pSetupData->USetupData.LocaleID,
_countof(pSetupData->USetupData.LocaleID),
L"%08lx", LocaleID);
}
/* new part */
pSetupData->SelectedLanguageId = pSetupData->DefaultLanguage;
wcscpy(pSetupData->DefaultLanguage, pSetupData->USetupData.LocaleID); // FIXME: In principle, only when unattended.
pSetupData->USetupData.LanguageId = (LANGID)(wcstol(pSetupData->SelectedLanguageId, NULL, 16) & 0xFFFF);
pSetupData->USetupData.LayoutList = CreateKeyboardLayoutList(pSetupData->USetupData.SetupInf,
pSetupData->SelectedLanguageId,
pSetupData->DefaultKBLayout);
/* If not unattended, overwrite keyboard layout with
* the current one of the running ReactOS instance */
if (!IsUnattendedSetup)
{
C_ASSERT(_countof(pSetupData->DefaultKBLayout) >= KL_NAMELENGTH);
/* If the call fails, keep the default already stored in the buffer */
GetKeyboardLayoutNameW(pSetupData->DefaultKBLayout);
}
/* Change the default entries in the language and keyboard layout lists */
{
PGENERIC_LIST LanguageList = pSetupData->USetupData.LanguageList;
PGENERIC_LIST LayoutList = pSetupData->USetupData.LayoutList;
PGENERIC_LIST_ENTRY ListEntry;
/* Search for default language */
for (ListEntry = GetFirstListEntry(LanguageList); ListEntry;
ListEntry = GetNextListEntry(ListEntry))
{
PCWSTR LocaleId = ((PGENENTRY)GetListEntryData(ListEntry))->Id;
if (!_wcsicmp(pSetupData->DefaultLanguage, LocaleId))
{
DPRINT("found %S in LanguageList\n", LocaleId);
SetCurrentListEntry(LanguageList, ListEntry);
break;
}
}
/* Search for default layout */
for (ListEntry = GetFirstListEntry(LayoutList); ListEntry;
ListEntry = GetNextListEntry(ListEntry))
{
PCWSTR pszLayoutId = ((PGENENTRY)GetListEntryData(ListEntry))->Id;
if (!_wcsicmp(pSetupData->DefaultKBLayout, pszLayoutId))
{
DPRINT("Found %S in LayoutList\n", pszLayoutId);
SetCurrentListEntry(LayoutList, ListEntry);
break;
}
}
}
return TRUE;
}
VOID
InitNtToWin32PathMappingList(
IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList)
{
InitializeListHead(&MappingList->List);
MappingList->MappingsCount = 0;
}
VOID
FreeNtToWin32PathMappingList(
IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList)
{
PLIST_ENTRY ListEntry;
PVOID Entry;
while (!IsListEmpty(&MappingList->List))
{
ListEntry = RemoveHeadList(&MappingList->List);
Entry = (PVOID)CONTAINING_RECORD(ListEntry, NT_WIN32_PATH_MAPPING, ListEntry);
HeapFree(ProcessHeap, 0, Entry);
}
MappingList->MappingsCount = 0;
}
/*
* Attempts to convert a pure NT file path into a corresponding Win32 path.
* Adapted from GetInstallSourceWin32() in dll/win32/syssetup/wizard.c
*/
BOOL
ConvertNtPathToWin32Path(
IN OUT PNT_WIN32_PATH_MAPPING_LIST MappingList,
OUT PWSTR pwszPath,
IN DWORD cchPathMax,
IN PCWSTR pwszNTPath)
{
BOOL FoundDrive = FALSE, RetryOnce = FALSE;
PLIST_ENTRY ListEntry;
PNT_WIN32_PATH_MAPPING Entry;
PCWSTR pwszNtPathToMap = pwszNTPath;
PCWSTR pwszRemaining = NULL;
DWORD cchDrives;
PWCHAR pwszDrive;
WCHAR wszDrives[512];
WCHAR wszNTPath[MAX_PATH];
WCHAR TargetPath[MAX_PATH];
*pwszPath = UNICODE_NULL;
/*
* We find first a mapping inside the MappingList. If one is found, use it
* to build the Win32 path. If there is none, we need to create one by
* checking the Win32 drives (and possibly NT symlinks too).
* In case of success, add the newly found mapping to the list and use it
* to build the Win32 path.
*/
for (ListEntry = MappingList->List.Flink;
ListEntry != &MappingList->List;
ListEntry = ListEntry->Flink)
{
Entry = CONTAINING_RECORD(ListEntry, NT_WIN32_PATH_MAPPING, ListEntry);
DPRINT("Testing '%S' --> '%S'\n", Entry->Win32Path, Entry->NtPath);
/* Check whether the queried NT path prefixes the user-provided NT path */
FoundDrive = !_wcsnicmp(pwszNtPathToMap, Entry->NtPath, wcslen(Entry->NtPath));
if (FoundDrive)
{
/* Found it! */
/* Set the pointers and go build the Win32 path */
pwszDrive = Entry->Win32Path;
pwszRemaining = pwszNTPath + wcslen(Entry->NtPath);
goto Quit;
}
}
/*
* No mapping exists for this path yet: try to find one now.
*/
/* Retrieve the mounted drives (available drive letters) */
cchDrives = GetLogicalDriveStringsW(_countof(wszDrives) - 1, wszDrives);
if (cchDrives == 0 || cchDrives >= _countof(wszDrives))
{
/* Buffer too small or failure */
DPRINT1("ConvertNtPathToWin32Path: GetLogicalDriveStringsW failed\n");
return FALSE;
}
/* We go back there once if RetryOnce == TRUE */
Retry:
/* Enumerate the mounted drives */
for (pwszDrive = wszDrives; *pwszDrive; pwszDrive += wcslen(pwszDrive) + 1)
{
/* Retrieve the NT path corresponding to the current Win32 DOS path */
pwszDrive[2] = UNICODE_NULL; // Temporarily remove the backslash
QueryDosDeviceW(pwszDrive, wszNTPath, _countof(wszNTPath));
pwszDrive[2] = L'\\'; // Restore the backslash
DPRINT("Testing '%S' --> '%S'\n", pwszDrive, wszNTPath);
/* Check whether the queried NT path prefixes the user-provided NT path */
FoundDrive = !_wcsnicmp(pwszNtPathToMap, wszNTPath, wcslen(wszNTPath));
if (!FoundDrive)
{
PWCHAR ptr, ptr2;
/*
* Check whether this was a network share that has a drive letter,
* but the user-provided NT path points to this share without
* mentioning the drive letter.
*
* The format is: \Device\<network_redirector>\;X:<data>\share\path
* The corresponding drive letter is 'X'.
* A system-provided network redirector (LanManRedirector or Mup)
* or a 3rd-party one may be used.
*
* We check whether the user-provided NT path has the form:
* \Device\<network_redirector>\<data>\share\path
* as it obviously did not have the full form (the previous check
* would have been OK otherwise).
*/
if (!_wcsnicmp(wszNTPath, L"\\Device\\", _countof(L"\\Device\\")-1) &&
(ptr = wcschr(wszNTPath + _countof(L"\\Device\\")-1, L'\\')) &&
wcslen(++ptr) >= 3 && ptr[0] == L';' && ptr[2] == L':')
{
/*
* Normally the specified drive letter should correspond
* to the one used for the mapping. But we will ignore
* if it happens not to be the case.
*/
if (pwszDrive[0] != ptr[1])
{
DPRINT1("Peculiar: expected network share drive letter %C different from actual one %C\n",
pwszDrive[0], ptr[1]);
}
/* Remove the drive letter from the NT network share path */
ptr2 = ptr + 3;
/* Swallow as many possible consecutive backslashes as there could be */
while (*ptr2 == L'\\') ++ptr2;
memmove(ptr, ptr2, (wcslen(ptr2) + 1) * sizeof(WCHAR));
/* Now do the check again */
FoundDrive = !_wcsnicmp(pwszNtPathToMap, wszNTPath, wcslen(wszNTPath));
}
}
if (FoundDrive)
{
/* Found it! */
pwszDrive[2] = UNICODE_NULL; // Remove the backslash
if (pwszNtPathToMap == pwszNTPath)
{
ASSERT(!RetryOnce && pwszNTPath != TargetPath);
pwszRemaining = pwszNTPath + wcslen(wszNTPath);
}
break;
}
}
if (FoundDrive)
{
/* A mapping was found, add it to the cache */
Entry = HeapAlloc(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(*Entry));
if (!Entry)
{
DPRINT1("ConvertNtPathToWin32Path: Cannot allocate memory\n");
return FALSE;
}
StringCchCopyNW(Entry->NtPath, _countof(Entry->NtPath),
pwszNTPath, pwszRemaining - pwszNTPath);
StringCchCopyW(Entry->Win32Path, _countof(Entry->Win32Path), pwszDrive);
/* Insert it as the most recent entry */
InsertHeadList(&MappingList->List, &Entry->ListEntry);
MappingList->MappingsCount++;
/* Set the pointers and go build the Win32 path */
pwszDrive = Entry->Win32Path;
goto Quit;
}
/*
* We failed, perhaps because the beginning of the NT path used a symlink.
* Try to see whether this is the case by attempting to resolve it.
* If the symlink resolution gives nothing, or we already failed once,
* there is no hope in converting the path to Win32.
* Otherwise, symlink resolution succeeds but we need to recheck again
* the drives list.
*/
/*
* In theory we would have to parse each element in the NT path and going
* until finding a symlink object (otherwise we would fail straight away).
* However here we can use guessing instead, since we know which kind of
* NT paths we are likely to manipulate: \Device\HarddiskX\PartitionY\ and
* the like (including \Device\HarddiskVolumeX\) and the other ones that
* are supported in setuplib\utils\arcname.c .
*
* But actually, all the supported names in arcname.c are real devices,
* and only \Device\HarddiskX\PartitionY\ may refer to a symlink, so we
* just check for it.
*/
if (!RetryOnce && !FoundDrive)
{
ULONG DiskNumber, PartitionNumber;
INT Length;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE LinkHandle;
UNICODE_STRING SymLink, Target;
if (swscanf(pwszNTPath, L"\\Device\\Harddisk%lu\\Partition%lu%n",
&DiskNumber, &PartitionNumber, &Length) != 2)
{
/* Definitively not a recognized path, bail out */
return FALSE;
}
/* Check whether \Device\HarddiskX\PartitionY is a symlink */
RtlInitEmptyUnicodeString(&SymLink, (PWCHAR)pwszNTPath, Length * sizeof(WCHAR));
SymLink.Length = SymLink.MaximumLength;
InitializeObjectAttributes(&ObjectAttributes,
&SymLink,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenSymbolicLinkObject(&LinkHandle,
SYMBOLIC_LINK_QUERY,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
/* Not a symlink, or something else happened: bail out */
DPRINT1("ConvertNtPathToWin32Path: NtOpenSymbolicLinkObject(%wZ) failed, Status 0x%08lx\n",
&SymLink, Status);
return FALSE;
}
*TargetPath = UNICODE_NULL;
RtlInitEmptyUnicodeString(&Target, TargetPath, sizeof(TargetPath));
/* Resolve the link and close its handle */
Status = NtQuerySymbolicLinkObject(LinkHandle, &Target, NULL);
NtClose(LinkHandle);
/* Check for success */
if (!NT_SUCCESS(Status))
{
/* Not a symlink, or something else happened: bail out */
DPRINT1("ConvertNtPathToWin32Path: NtQuerySymbolicLinkObject(%wZ) failed, Status 0x%08lx\n",
&SymLink, Status);
return FALSE;
}
/* Set the pointers */
pwszRemaining = pwszNTPath + Length;
pwszNtPathToMap = TargetPath; // Point to our local buffer
/* Retry once */
RetryOnce = TRUE;
goto Retry;
}
ASSERT(!FoundDrive);
Quit:
if (FoundDrive)
{
StringCchPrintfW(pwszPath, cchPathMax,
L"%s%s",
pwszDrive,
pwszRemaining);
DPRINT("ConvertNtPathToWin32Path: %S\n", pwszPath);
return TRUE;
}
return FALSE;
}
/* Used to enable and disable the shutdown privilege */
/* static */ BOOL
EnablePrivilege(
IN LPCWSTR lpszPrivilegeName,
IN BOOL bEnablePrivilege)
{
BOOL Success;
HANDLE hToken;
TOKEN_PRIVILEGES tp;
Success = OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hToken);
if (!Success) return Success;
Success = LookupPrivilegeValueW(NULL,
lpszPrivilegeName,
&tp.Privileges[0].Luid);
if (!Success) goto Quit;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0);
Success = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
Quit:
CloseHandle(hToken);
return Success;
}
/* Copied from HotkeyThread() in dll/win32/syssetup/install.c */
static DWORD CALLBACK
HotkeyThread(LPVOID Parameter)
{
ATOM hotkey;
MSG msg;
DPRINT("HotkeyThread start\n");
hotkey = GlobalAddAtomW(L"Setup Shift+F10 Hotkey");
if (!RegisterHotKey(NULL, hotkey, MOD_SHIFT, VK_F10))
DPRINT1("RegisterHotKey failed with %lu\n", GetLastError());
while (GetMessageW(&msg, NULL, 0, 0))
{
if (msg.hwnd == NULL && msg.message == WM_HOTKEY && msg.wParam == hotkey)
{
WCHAR CmdLine[] = L"cmd.exe"; // CreateProcess can modify this buffer.
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessW(NULL,
CmdLine,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
DPRINT1("Failed to launch command prompt: %lu\n", GetLastError());
}
}
}
UnregisterHotKey(NULL, hotkey);
GlobalDeleteAtom(hotkey);
DPRINT("HotkeyThread terminate\n");
return 0;
}
int WINAPI
_tWinMain(HINSTANCE hInst,
HINSTANCE hPrevInstance,
LPTSTR lpszCmdLine,
int nCmdShow)
{
ULONG Error;
HANDLE hHotkeyThread;
INITCOMMONCONTROLSEX iccx;
PROPSHEETHEADER psh;
HPROPSHEETPAGE ahpsp[8];
PROPSHEETPAGE psp = {0};
UINT nPages = 0;
ProcessHeap = GetProcessHeap();
SetupData.hInstance = hInst;
SetupData.hInstallThread = NULL;
SetupData.hHaltInstallEvent = NULL;
SetupData.bStopInstall = FALSE;
/* Initialize the NT to Win32 path prefix mapping list */
InitNtToWin32PathMappingList(&SetupData.MappingList);
/* Initialize Setup */
Error = InitializeSetup(&SetupData.USetupData, NULL,
&SpFileExports, &SpInfExports);
if (Error != ERROR_SUCCESS)
{
//
// TODO: Write an error mapper (much like the MUIDisplayError of USETUP)
//
if (Error == ERROR_NO_SOURCE_DRIVE)
MessageBoxW(NULL, L"GetSourcePaths failed!", L"Error", MB_ICONERROR);
else if (Error == ERROR_LOAD_TXTSETUPSIF)
DisplayError(NULL, IDS_CAPTION, IDS_NO_TXTSETUP_SIF);
else // FIXME!!
MessageBoxW(NULL, L"Unknown error!", L"Error", MB_ICONERROR);
goto Quit;
}
/* Retrieve any supplemental options from the unattend file */
CheckUnattendedSetup(&SetupData.USetupData);
SetupData.bUnattend = IsUnattendedSetup; // FIXME :-)
/* Load extra setup data (HW lists etc...) */
if (!LoadSetupData(&SetupData))
goto Quit;
hHotkeyThread = CreateThread(NULL, 0, HotkeyThread, NULL, 0, NULL);
/* Whenever any of the common controls are used in your app,
* you must call InitCommonControlsEx() to register the classes
* for those controls. */
iccx.dwSize = sizeof(iccx);
iccx.dwICC = ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_PROGRESS_CLASS;
InitCommonControlsEx(&iccx);
/* Register the TreeList control */
// RegisterTreeListClass(hInst);
TreeListRegister(hInst);
/* Create the title and bold fonts */
SetupData.hTitleFont = CreateTitleFont(NULL);
SetupData.hBoldFont = CreateBoldFont(NULL, 0);
if (!SetupData.bUnattend)
{
/* Create the Start page, until setup is working */
// NOTE: What does "until setup is working" mean??
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = StartDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_STARTPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the install type selection page */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCEW(IDS_TYPETITLE);
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_TYPESUBTITLE);
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = TypeDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_TYPEPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the upgrade/repair selection page */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCEW(IDS_UPDATETITLE);
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_UPDATESUBTITLE);
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = UpgradeRepairDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_UPDATEREPAIRPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the device settings page */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCEW(IDS_DEVICETITLE);
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_DEVICESUBTITLE);
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = DeviceDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_DEVICEPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the install device settings page / boot method / install directory */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCEW(IDS_DRIVETITLE);
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_DRIVESUBTITLE);
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = DriveDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_DRIVEPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the summary page */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCEW(IDS_SUMMARYTITLE);
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_SUMMARYSUBTITLE);
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = SummaryDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_SUMMARYPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
}
/* Create the installation progress page */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.pszHeaderTitle = MAKEINTRESOURCEW(IDS_PROCESSTITLE);
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_PROCESSSUBTITLE);
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = ProcessDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROCESSPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the finish-and-reboot page */
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = RestartDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_RESTARTPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the property sheet */
psh.dwSize = sizeof(PROPSHEETHEADER);
psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
psh.hInstance = hInst;
psh.hwndParent = NULL;
psh.nPages = nPages;
psh.nStartPage = 0;
psh.phpage = ahpsp;
psh.pszbmWatermark = MAKEINTRESOURCEW(IDB_WATERMARK);
psh.pszbmHeader = MAKEINTRESOURCEW(IDB_HEADER);
/* Display the wizard */
PropertySheet(&psh);
/* Wait for any pending installation */
WaitForSingleObject(SetupData.hInstallThread, INFINITE);
CloseHandle(SetupData.hInstallThread);
SetupData.hInstallThread = NULL;
CloseHandle(SetupData.hHaltInstallEvent);
SetupData.hHaltInstallEvent = NULL;
if (SetupData.hBoldFont)
DeleteFont(SetupData.hBoldFont);
if (SetupData.hTitleFont)
DeleteFont(SetupData.hTitleFont);
/* Unregister the TreeList control */
// UnregisterTreeListClass(hInst);
TreeListUnregister(hInst);
if (hHotkeyThread)
{
PostThreadMessageW(GetThreadId(hHotkeyThread), WM_QUIT, 0, 0);
CloseHandle(hHotkeyThread);
}
Quit:
/* Setup has finished */
FinishSetup(&SetupData.USetupData);
/* Free the NT to Win32 path prefix mapping list */
FreeNtToWin32PathMappingList(&SetupData.MappingList);
#if 0 // NOTE: Disabled for testing purposes only!
EnablePrivilege(SE_SHUTDOWN_NAME, TRUE);
ExitWindowsEx(EWX_REBOOT, 0);
EnablePrivilege(SE_SHUTDOWN_NAME, FALSE);
#endif
return 0;
}
/* EOF */