Files
reactos/base/setup/reactos/reactos.c
T
Hermès Bélusca-Maïto b6f3d519b4 [SETUP:REACTOS] Fix navigation in the setup wizard pages and dialogs
- Fix TAB order in the Installation type page.

- Don't systematically set focus to the "Install ReactOS" radio-button.

  Always resetting the focus to the "Install" choice is unwanted,
  because, if the user instead selects "Upgrade", goes to the next
  "Upgrade/Repair" page, then goes back to the installation type page,
  the selection focus wouldn't be on what the user previously chose,
  but would instead be on the "Install" choice, while the "Upgrade"
  choice would stay checked, thus leading to an inconsistency.

  We want instead the selection to stay as the user previously chose,
  without changing it.

- Correctly handle item selection change in the Installations list,
  so that only the "selected" state change is detected and UI buttons
  are updated accordingly.
  When the `PSN_QUERYINITIALFOCUS` notification is received (whenever
  the page is made active), reselect the currently-selected item so as
  to properly update the UI buttons.

- Add missing keyboard accelerators in the Devices selection page.

- Group all the push-buttons together in the Drives/Partitions page,
  so that when the focus is on the first one and the user presses the
  Left-arrow key, the focus stays on the buttons and doesn't go to the
  partitions list.

- Remove the `PSN_QUERYINITIALFOCUS` hack in the Drives/Partition page
  dialog procedure (see commit 6cb1394bb1, PR #9272).

- Correctly handle item selection change in the Drives/Partition list,
  so that only the "selected" state change is detected and UI buttons
  are updated accordingly.
  When the `PSN_QUERYINITIALFOCUS` notification is received (whenever
  the page is made active), reselect the currently-selected item so as
  to properly update the UI buttons.

- Remove redundant `WS_VISIBLE` style for all the Edit-text fields of
  the Summary page.

- In the Start/Welcome and the Finish/Abort pages: when disabling the
  wizard "Back"/"Next" navigation buttons with `PropSheet_SetWizButtons()`,
  for the purpose of hiding them next, do *NOT* use that macro, because
  it posts the `PSM_SETWIZBUTTONS` message instead of sending it, and so,
  it would be handled after hiding the buttons. The message would then
  interfere with the hidden buttons (when both "Back" and "Next" are
  hidden, "Next" gets forcefully shown).

- Set the default button to "No" for some confirmation dialogs
  (setup abort; partition deletion; ...).
2026-07-24 23:07:40 +02:00

3478 lines
118 KiB
C

/*
* PROJECT: ReactOS GUI first stage setup application
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Main file
* COPYRIGHT: Copyright 2008-2010 Matthias Kupfer <[email protected]>
* Copyright 2008-2009 Dmitry Chapyshev <[email protected]>
* Copyright 2018-2024 Hermès Bélusca-Maïto <[email protected]>
*/
#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;
static BOOLEAN IsUnattendedSetup;
/* 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);
}
size_t
LoadAllocStringW(
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID,
_In_opt_ _Outptr_ PWSTR* pString,
_In_opt_ size_t cchBufferLen /*,
_In_opt_ PCWSTR pDefaultString*/)
{
PCWSTR pStr;
size_t Length;
/* Try to load the string from the resource */
Length = LoadStringW(hInstance, uID, (PWSTR)&pStr, 0);
if (Length == 0)
{
/* No resource string was found, return NULL */
*pString = NULL;
return 0;
}
/* If the caller gave a pointer to a buffer on input, verify whether it
* is large enough to contain the string. If not, allocate a new buffer. */
if (!*pString || (cchBufferLen < Length + 1))
{
/* Allocate a new buffer, adding a NUL-terminator */
*pString = HeapAlloc(GetProcessHeap(), 0, (Length + 1) * sizeof(WCHAR));
if (!*pString)
return 0;
}
/* Copy the string, NUL-terminated */
StringCchCopyNW(*pString, Length + 1, pStr, Length);
return Length;
}
size_t
FormatAllocStringWV(
_In_opt_ _Outptr_ PWSTR* pString,
_In_opt_ size_t cchBufferLen,
_In_ PCWSTR pszFormat,
_In_ va_list args)
{
size_t Length;
/* Retrieve the message length. If it is too long, allocate
* an auxiliary buffer; otherwise use the caller's buffer. */
Length = _vscwprintf(pszFormat, args); // Doesn't count the NUL-terminator.
if (!*pString || (Length >= cchBufferLen))
{
/* Allocate a new buffer, adding a NUL-terminator */
*pString = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (Length + 1) * sizeof(WCHAR));
if (!*pString)
return 0;
}
/* Do the printf */
StringCchVPrintfW(*pString, Length + 1, pszFormat, args);
return Length;
}
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};
PWSTR Format;
WCHAR StaticBuffer[256];
PWSTR 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))
{
Format = NULL;
(void)LoadAllocStringW(hInstance, PtrToUlong(pszFormatMessage), &Format, 0);
}
else
{
Format = (PWSTR)pszFormatMessage;
}
if (Format)
{
/* Format the message and retrieve its length. If it is too long,
* an auxiliary buffer is allocated; otherwise the static buffer
* is used. The string is built to be NUL-terminated. */
(void)FormatAllocStringWV(&Buffer, _countof(StaticBuffer), Format, args);
if (!Buffer)
{
/* Allocation failed, use the original format string verbatim */
Buffer = Format;
}
}
else
{
Format = (PWSTR)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_ICONERROR | MB_OK,
MAKEINTRESOURCEW(uIDTitle),
MAKEINTRESOURCEW(uIDMessage),
args);
va_end(args);
return iRes;
}
VOID
SetWindowResTextW(
_In_ HWND hWnd,
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID /*,
_In_opt_ PCWSTR pDefaultString*/)
{
WCHAR szText[256];
PWSTR String = szText; // Use the static buffer by default.
/* Try to load the string from the resource */
(void)LoadAllocStringW(hInstance, uID, &String, _countof(szText));
if (!String)
return;
SetWindowTextW(hWnd, String);
if (String != szText)
HeapFree(GetProcessHeap(), 0, String);
}
VOID
SetWindowResPrintfVW(
_In_ HWND hWnd,
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID,
_In_ va_list args)
{
WCHAR ResBuffer[256];
WCHAR szText[256];
PWSTR ResFmt = ResBuffer; // Use the static buffers by default.
PWSTR String = szText;
/* Try to load the string from the resource */
(void)LoadAllocStringW(hInstance, uID, &ResFmt, _countof(ResBuffer));
if (!ResFmt)
return;
/* Format the string and retrieve its length. If it is too long,
* an auxiliary buffer is allocated; otherwise the static buffer
* is used. The string is built to be NUL-terminated. */
(void)FormatAllocStringWV(&String, _countof(szText), ResFmt, args);
if (!String)
{
/* Allocation failed, use the original format string verbatim */
String = ResFmt;
}
SetWindowTextW(hWnd, String);
/* Free the buffers if needed */
if ((String != szText) && (String != ResFmt))
HeapFree(GetProcessHeap(), 0, String);
if (ResFmt && (ResFmt != ResBuffer))
HeapFree(GetProcessHeap(), 0, ResFmt);
}
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));
return TRUE;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
/* Only "Next" and "Cancel" for the first page and hide "Back".
* Don't use the PropSheet_SetWizButtons() macro, because its
* posted message could interfere with the hidden button. */
SendMessageW(GetParent(hwndDlg), PSM_SETWIZBUTTONS, 0, 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);
}
/* Ensure "Install ReactOS" is initially focused */
SetFocus(GetDlgItem(hwndDlg, IDC_INSTALL));
return FALSE;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Abort page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_ABORTPAGE);
}
/* 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));
InstallPartition = pSetupData->CurrentInstallation->Volume->PartEntry;
StringCchCopyW(pSetupData->USetupData.InstallationDirectory,
_countof(pSetupData->USetupData.InstallationDirectory),
pSetupData->CurrentInstallation->PathComponent);
/* Jump to the Summary page during repair/upgrade */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_SUMMARYPAGE);
}
}
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);
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;
/* Check whether the item has been (de)selected */
if (!(pnmv->uChanged & LVIF_STATE) ||
!((pnmv->uOldState ^ pnmv->uNewState) & LVIS_SELECTED))
{
break;
}
/* Enable or disable the "Next" button when the user
* selects or deselects an installation to upgrade */
if (pnmv->uNewState & LVIS_SELECTED)
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
else
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
break;
}
switch (lpnm->code)
{
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;
}
case PSN_QUERYINITIALFOCUS:
{
/* Reselect the currently selected item, so as to refresh the UI buttons */
INT Index;
hList = GetDlgItem(hwndDlg, IDC_NTOSLIST);
Index = ListView_GetSelectionMark(hList);
if (Index != LB_ERR)
{
/* Deselect first the item before reselecting it, so as to
* invalidate its cached state and have the LVN_ITEMCHANGED
* notification sent. */
//ListView_EnsureVisible(hList, Index, FALSE);
ListView_SetItemState(hList, Index, 0, LVIS_SELECTED);
ListView_SetItemState(hList, Index,
LVIS_FOCUSED | LVIS_SELECTED,
LVIS_FOCUSED | LVIS_SELECTED);
}
/* Focus on the installations list */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)hList);
return TRUE;
}
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Abort page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_ABORTPAGE);
}
/* 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));
InstallPartition = pSetupData->CurrentInstallation->Volume->PartEntry;
StringCchCopyW(pSetupData->USetupData.InstallationDirectory,
_countof(pSetupData->USetupData.InstallationDirectory),
pSetupData->CurrentInstallation->PathComponent);
/* We perform an upgrade */
pSetupData->RepairUpdateFlag = TRUE;
/* Jump to the Summary page during repair/upgrade */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_SUMMARYPAGE);
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);
return TRUE;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
break;
case PSN_QUERYCANCEL:
{
if (DisplayMessage(GetParent(hwndDlg),
MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Abort page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_ABORTPAGE);
}
/* 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;
}
case PSN_WIZBACK:
{
/* Return to the Install type selection page instead of the Repair/Upgrade page */
SetWindowLongW(hwndDlg, DWLP_MSGRESULT, IDD_TYPEPAGE);
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)
{
// Ideally we could add the PSWIZBF_ELEVATIONREQUIRED style.
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 */
SetWindowLongPtrW(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_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
MAKEINTRESOURCEW(IDS_ABORTSETUP2),
MAKEINTRESOURCEW(IDS_ABORTSETUP)) == IDYES)
{
/* Go to the Abort page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_ABORTPAGE);
}
/* Do not close the wizard too soon */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
case PSN_WIZBACK:
{
/* When the user performs a regular installation, go back to the previous page */
if (!pSetupData->RepairUpdateFlag)
break;
if (GetNumberOfListEntries(pSetupData->NtOsInstallsList) > 1)
{
/* Return to the Upgrade/Repair selection page, when the user is
* upgrading and there are more than one installation available */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_UPDATEREPAIRPAGE);
}
else
{
/* Return to the Install type selection page, when the user is
* upgrading and there is at most one installation available */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, IDD_TYPEPAGE);
}
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;
/* Whereas the repair/upgrade procedure may move, rename, or
* delete files, the regular installation only copies files.
* Therefore, display only the file name instead of the full
* "Copying..." message in this case. */
if (SetupData.RepairUpdateFlag)
{
SetWindowResPrintfW(UiContext.hWndItem,
SetupData.hInstance,
IDS_COPYING, // STRING_COPYING
DstFileName);
}
else
{
SetWindowTextW(UiContext.hWndItem, 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
* Enable or disable the Cancel and the Close title-bar property-sheet buttons.
*
* The buttons are however kept visible in case they are disabled
* (this informs the user they are disabled only temporarily).
**/
VOID
PropSheet_SetCloseCancel(
_In_ HWND hWndWiz,
_In_ BOOL Enable)
{
EnableDlgItem(hWndWiz, IDCANCEL, Enable);
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 Abort 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 Abort 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 Abort 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 Abort page.
* Otherwise we have been cancelled by the user, who has already switched to the Abort 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 Abort page.
* Otherwise we have been cancelled by the user, who has already switched to the Abort 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 Finish page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_FINISHPAGE);
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 the status text and set the main label in bold */
SetDlgItemTextW(hwndDlg, IDC_ACTIVITY, L"");
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
SetDlgItemFont(hwndDlg, IDC_ACTIVITY, pSetupData->hBoldFont, TRUE);
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, they will be
* re-enabled by the installation thread; hide "Back" */
PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
// PropSheet_ShowWizButtons(GetParent(hwndDlg), 0, PSWIZB_BACK);
ShowDlgItem(GetParent(hwndDlg), ID_WIZBACK, SW_HIDE);
/* 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_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2,
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 Abort page */
PropSheet_SetCurSelByID(GetParent(hwndDlg), IDD_ABORTPAGE);
}
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;
}
/**
* @brief Detects whether a Windows shell is active.
**/
static BOOL
IsShellActive(VOID)
{
/* See reactos/undocuser.h */
extern HWND WINAPI GetProgmanWindow(VOID);
/* Return success if a shell window is present, valid, and interactive */
HWND hWndProgman = GetProgmanWindow();
if (!(hWndProgman && IsWindow(hWndProgman) &&
IsWindowEnabled(hWndProgman) && IsWindowVisible(hWndProgman)))
{
hWndProgman = GetShellWindow();
}
return (hWndProgman && IsWindow(hWndProgman) &&
IsWindowEnabled(hWndProgman) && IsWindowVisible(hWndProgman));
}
typedef struct _CLOSABLE_WND_INFO
{
HWND hWndExclude; ///< Window to exclude from search
BOOL Found; ///< TRUE if a closable window was found; FALSE if not.
} CLOSABLE_WND_INFO, *PCLOSABLE_WND_INFO;
static BOOL
CALLBACK
FindUserClosableWindowProc(
_In_ HWND hWnd,
_In_ LPARAM lParam)
{
PCLOSABLE_WND_INFO pInfo = (PCLOSABLE_WND_INFO)lParam;
HMENU hSysMenu;
MENUITEMINFOW mii;
/* Skip the window to exclude */
if (hWnd == pInfo->hWndExclude)
return TRUE;
/* Skip non-interactive windows */
if (!IsWindowEnabled(hWnd) || !IsWindowVisible(hWnd))
return TRUE;
hSysMenu = GetSystemMenu(hWnd, FALSE);
if (!hSysMenu)
return TRUE; /* No menu: skip the window */
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STATE;
if (!GetMenuItemInfoW(hSysMenu, SC_CLOSE, FALSE, &mii))
return TRUE; /* No close item: skip the window */
pInfo->Found |= !(mii.fState & MFS_DISABLED);
/* Continue enumeration (return TRUE) if no close item is enabled;
* otherwise stop the enumeration (return FALSE) */
return !pInfo->Found;
}
/**
* @brief Detects whether there exist interactive closable windows opened.
**/
static BOOL
AreThereInteractiveWindows(
_In_opt_ HWND hWndExclude)
{
/* Return success if a shell is active */
if (IsShellActive())
{
return TRUE;
}
/* Otherwise, check for user-interactive closable windows */
else
{
CLOSABLE_WND_INFO Info = {hWndExclude, FALSE};
EnumWindows(FindUserClosableWindowProc, (LPARAM)&Info);
return Info.Found;
}
}
static INT_PTR CALLBACK
FinishDlgProc(
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:
{
LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)ppsp->lParam;
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (DWORD_PTR)pSetupData);
/* Set the stop-install flag if the user is aborting
* the installation: TRUE if Abort, FALSE if Finish. */
pSetupData->bStopInstall = (ppsp->pszTemplate == MAKEINTRESOURCEW(IDD_ABORTPAGE));
/* Set title font */
SetDlgItemFont(hwndDlg, IDC_FINISHTITLE, pSetupData->hTitleFont, TRUE);
/* We need to reboot at the end of the installation if this is an
* unattended setup, or if there is no shell active. If there are
* other user-interactive windows opened, we pause in WM_ACTIVATE
* the inevitable reboot countdown when the wizard is deactivated,
* and restart it when the wizard is reactivated. */
pSetupData->bMustReboot = SetupData.bUnattend;
pSetupData->bMustReboot |= !IsShellActive();
/* If we must reboot, display the countdown gauge. In any
* case, let the user restart now or postpone it to later. */
if (pSetupData->bMustReboot)
{
/* "Setup will now restart your computer..." is shown */
EnableDlgItem(hwndDlg, IDC_RESTART_PROGRESS, TRUE);
ShowDlgItem(hwndDlg, IDC_RESTART_PROGRESS, SW_SHOW);
}
else
{
/* We should not reboot automatically, change the finish
* text to "Setup needs to restart your computer..." */
UINT uMsgID = (!pSetupData->bStopInstall ? IDS_FINISH_NO_REBOOT
: IDS_ABORT_NO_REBOOT);
SetWindowResTextW(GetDlgItem(hwndDlg, IDC_FINISHTEXT),
pSetupData->hInstance,
uMsgID);
/* Hide and disable the countdown gauge */
EnableDlgItem(hwndDlg, IDC_RESTART_PROGRESS, FALSE);
ShowDlgItem(hwndDlg, IDC_RESTART_PROGRESS, SW_HIDE);
}
/* If the installation is aborted, change the "Cancel" button text to "Close" */
if (pSetupData->bStopInstall)
{
SetWindowResTextW(GetDlgItem(GetParent(hwndDlg), IDCANCEL),
GetModuleHandleW(L"comctl32.dll"),
IDS_CLOSE);
}
/* Ensure that the installer wizard window is made visible and focused */
ShowWindow(GetParent(hwndDlg), SW_SHOW);
SwitchToThisWindow(GetParent(hwndDlg), TRUE);
return TRUE;
}
case WM_DESTROY:
return TRUE;
case WM_ACTIVATE:
{
/* Only care about (de)activation only if we must reboot */
if (!pSetupData->bMustReboot)
break;
if (LOWORD(wParam) == WA_INACTIVE)
{
/* Wizard window is deactivated, check whether there are
* interactive windows. If so, pause the countdown. */
if (AreThereInteractiveWindows(GetParent(hwndDlg)))
KillTimer(hwndDlg, 1);
}
else
{
/* Wizard window is reactivated, re-enable the countdown */
SetTimer(hwndDlg, 1, 50, NULL);
}
break;
}
case WM_TIMER:
{
HWND hWndProgress;
INT Position;
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_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
HWND hWndParent = GetParent(hwndDlg);
/*
* Only "Finish" for closing the wizard, and hide "Back" and "Next".
* Don't use the PropSheet_SetWizButtons() macro, because its
* posted message would be handled after hiding the buttons.
* The message would then interfere with the hidden buttons
* (when both "Back" and "Next" are hidden, "Next" gets forcefully shown).
*/
SendMessageW(hWndParent, PSM_SETWIZBUTTONS, 0, PSWIZB_FINISH);
// PropSheet_ShowWizButtons(hWndParent, 0, PSWIZB_BACK | PSWIZB_NEXT | PSWIZB_CANCEL);
ShowDlgItem(hWndParent, ID_WIZBACK, SW_HIDE);
ShowDlgItem(hWndParent, ID_WIZNEXT, SW_HIDE);
/* Change the "Finish" button text to "Restart" */
SetWindowResTextW(GetDlgItem(hWndParent, ID_WIZFINISH),
pSetupData->hInstance,
IDS_RESTARTBTN);
if (pSetupData->bMustReboot)
{
RECT rcBtn1, rcBtn2;
/* Move the "Finish"/"Restart" button to where the "Close"/"Cancel" button is */
GetWindowRect(GetDlgItem(hWndParent, ID_WIZFINISH), &rcBtn1);
MapWindowPoints(HWND_DESKTOP /*NULL*/, hWndParent, (LPPOINT)&rcBtn1, sizeof(RECT)/sizeof(POINT));
GetWindowRect(GetDlgItem(hWndParent, IDCANCEL), &rcBtn2);
MapWindowPoints(HWND_DESKTOP /*NULL*/, hWndParent, (LPPOINT)&rcBtn2, sizeof(RECT)/sizeof(POINT));
SetWindowPos(GetDlgItem(hWndParent, ID_WIZFINISH),
HWND_TOP,
rcBtn1.left + (rcBtn2.right - rcBtn1.right),
rcBtn1.top,
0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
/* Hide and disable also the "Close"/"Cancel" buttons since we can only finish now */
ShowDlgItem(hWndParent, IDCANCEL, SW_HIDE);
PropSheet_SetCloseCancel(hWndParent, FALSE);
/* Set up the reboot progress bar and countdown timer.
* 300 steps at 50 ms each: 15 seconds */
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);
}
else if (!pSetupData->bStopInstall)
{
/* Keep the "Cancel" button shown and change its text to "Postpone" */
// PropSheet_ShowWizButtons(hWndParent, 0, PSWIZB_BACK | PSWIZB_NEXT);
SetWindowResTextW(GetDlgItem(hWndParent, IDCANCEL),
pSetupData->hInstance,
IDS_POSTPONEBTN);
}
break;
}
case PSN_WIZNEXT:
case PSN_WIZFINISH:
{
/* Press on "Finish"/"Restart" button */
pSetupData->bMustReboot = TRUE;
__fallthrough;
}
case PSN_QUERYCANCEL:
/* Press on "Cancel"/"Postpone" button */
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;
}
static PCWSTR
GetLocalSetupDllPath(VOID)
{
static WCHAR SetupDllPath[MAX_PATH] = L"";
static BOOL Init = FALSE;
BOOL Success;
DWORD PathSize;
/* Don't rebuild the path if we did it already */
if (Init)
return SetupDllPath;
Init = TRUE;
/*
* Retrieve the full path of the current running Setup instance.
* From this we build the suitable path to the Setup DLL.
*/
PathSize = GetModuleFileNameW(NULL, SetupDllPath, _countof(SetupDllPath));
SetupDllPath[_countof(SetupDllPath) - 1] = UNICODE_NULL; // Ensure NUL-termination (see WinXP bug)
Success = ((PathSize != 0) && (PathSize < _countof(SetupDllPath)) &&
(GetLastError() != ERROR_INSUFFICIENT_BUFFER));
if (Success)
{
/* Find the last path separator, remove it as well as the file name */
PWCHAR pch = wcsrchr(SetupDllPath, L'\\');
if (!pch)
pch = SetupDllPath;
/* The Setup DLL is inside the System32 sub-directory */
PathSize = _countof(SetupDllPath) - (pch - SetupDllPath);
Success = SUCCEEDED(StringCchCopyW(pch, PathSize, L"\\system32"));
}
if (!Success)
{
/* Failure: invalidate the path; the DLL won't be found and delay-loaded */
*SetupDllPath = UNICODE_NULL;
}
return SetupDllPath;
}
#ifndef DECLARE_UNICODE_STRING_SIZE
#define DECLARE_UNICODE_STRING_SIZE(_var, _size) \
WCHAR _var ## _buffer[_size]; \
UNICODE_STRING _var = { 0, (_size) * sizeof(WCHAR) , _var ## _buffer }
#endif
#include <ndk/exfuncs.h> // For NtRaiseHardError()
#define DELAYIMP_INSECURE_WRITABLE_HOOKS
#include <delayimp.h>
/**
* @brief
* Controls the delay-loading of Setup DLLs from a suitable path.
*
* @see
* https://stackoverflow.com/a/75325443
* https://devblogs.microsoft.com/oldnewthing/20170126-00/?p=95265
**/
static FARPROC
WINAPI setupDelayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
static CHAR dllPath[MAX_PATH];
static PCWSTR setupDllPath = NULL;
switch (dliNotify)
{
case dliNotePreLoadLibrary:
{
// NOTE: Add any other needed setup-specific DLLs there.
if (_stricmp(pdli->szDll, "setuplib.dll") == 0)
{
if (!setupDllPath)
setupDllPath = GetLocalSetupDllPath();
if (setupDllPath && *setupDllPath &&
SUCCEEDED(StringCchPrintfA(dllPath, _countof(dllPath), "%S\\%s",
setupDllPath, pdli->szDll)))
{
pdli->szDll = dllPath; /* Set szDll to the new path */
}
}
break; /* Load the DLL using the modified path */
}
case dliFailLoadLib:
{
/*
* Library loading failed.
* Raise a hard error instead of the default
* exception, and "cleanly" kill the process.
*/
ANSI_STRING DllPathA;
DECLARE_UNICODE_STRING_SIZE(DllPathU, MAX_PATH);
ULONG_PTR Parameters[] = {(ULONG_PTR)&DllPathU};
ULONG Response;
RtlInitAnsiString(&DllPathA, pdli->szDll);
RtlAnsiStringToUnicodeString(&DllPathU, &DllPathA, FALSE);
NtRaiseHardError(STATUS_DLL_NOT_FOUND | HARDERROR_OVERRIDE_ERRORMODE,
_countof(Parameters),
0x1,
Parameters,
OptionOk,
&Response);
ExitProcess(-1);
break;
}
default:
break;
}
return NULL;
}
/**
* @brief
* Custom delay-loading hooks for loading the Setup DLLs from a suitable path.
**/
// NOTE: MSVC 2015 Update 3 makes this a const variable.
// #if (_MSC_VER > 1900) || (_MSC_VER == 1900 && _MSC_FULL_VER >= 190024210) ...
/*ExternC*/ PfnDliHook __pfnDliNotifyHook2 = setupDelayHook;
/*ExternC*/ PfnDliHook __pfnDliFailureHook2 = setupDelayHook;
int WINAPI
_tWinMain(HINSTANCE hInst,
HINSTANCE hPrevInstance,
LPTSTR lpszCmdLine,
int nCmdShow)
{
ULONG Error;
HANDLE hHotkeyThread;
INITCOMMONCONTROLSEX iccx;
PROPSHEETHEADER psh;
HPROPSHEETPAGE ahpsp[9];
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 */
SetupData.bUnattend = IsUnattendedSetup = CheckUnattendedSetup(&SetupData.USetupData);
/* 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 */
psp.dwSize = sizeof(psp);
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(psp);
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(psp);
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(psp);
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(psp);
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(psp);
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(psp);
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 page */
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = FinishDlgProc;
psp.pszTemplate = MAKEINTRESOURCEW(IDD_FINISHPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the Abort page */
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
psp.hInstance = hInst;
psp.lParam = (LPARAM)&SetupData;
psp.pfnDlgProc = FinishDlgProc; // Same dialog procedure as the Finish page.
psp.pszTemplate = MAKEINTRESOURCEW(IDD_ABORTPAGE);
ahpsp[nPages++] = CreatePropertySheetPage(&psp);
/* Create the property sheet */
psh.dwSize = sizeof(psh);
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);
/* Force reboot if there are no other user-interactive windows opened */
SetupData.bMustReboot |= !AreThereInteractiveWindows(NULL);
/* System rebooting will be done by Winlogon if necessary */
if (SetupData.bMustReboot)
{
#if 1 // TESTING: Disable for testing the installer locally.
EnablePrivilege(SE_SHUTDOWN_NAME, TRUE);
ExitWindowsEx(EWX_REBOOT, 0);
EnablePrivilege(SE_SHUTDOWN_NAME, FALSE);
#else
DisplayMessage(NULL, MB_ICONWARNING, L"Restarting", L"Setup is now restarting your computer!");
#endif
}
return 0;
}
/* EOF */