mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-27 11:53:32 +00:00
The setuplib DLL, used also by the text-mode USETUP, isn't placed in the standard DLL search paths list. It isn't in the "current" directory nor in the one where reactos.exe is, nor in the running OS' SystemRoot or System32 directories. The DLL is instead placed in the System32 sub-directory of the ReactOS installation source. Note that this isn't a problem for USETUP, because it is already started from that directory. To control its loading, delay-load the DLL and use a delay-load hook, following the technique explained in: https://stackoverflow.com/a/75325443 https://devblogs.microsoft.com/oldnewthing/20170126-00/?p=95265 The hook is also invoked in case of loading failure, showing a more user-friendly hard-error popup and killing the installer, instead of throwing a debugger exception.
3174 lines
105 KiB
C
3174 lines
105 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 ([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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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 NULL-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[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 */
|
|
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, 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 */
|