mirror of
https://github.com/reactos/reactos.git
synced 2025-04-20 12:29:56 +00:00

- Interface the TreeList code and populate it with the list of discovered disks and partitions. Code is adapted from USETUP. Also display the names of the disks. - Display some installation settings summary, before doing the installation proper. - Force the user to select a checkbox when (s)he acknowledges that ReactOS is alpha-quality software and may break on his/her computer or corrupt his/her data. - Improve wizard pages transitions and buttons enabling/disabling. - Press Shift-F10 to start a command-line (as in the 2nd-stage setup in syssetup.dll). - Use some explicit UNICODE functions/macros.
2043 lines
68 KiB
C
2043 lines
68 KiB
C
/*
|
|
* ReactOS applications
|
|
* Copyright (C) 2004-2008 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS GUI first stage setup application
|
|
* FILE: base/setup/reactos/reactos.c
|
|
* PROGRAMMERS: Matthias Kupfer
|
|
* Dmitry Chapyshev (dmitry@reactos.org)
|
|
*/
|
|
|
|
#include "reactos.h"
|
|
|
|
#define NTOS_MODE_USER
|
|
#include <ndk/obfuncs.h>
|
|
|
|
#include "resource.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ******************************************************************/
|
|
|
|
HANDLE ProcessHeap;
|
|
BOOLEAN IsUnattendedSetup = FALSE;
|
|
SETUPDATA SetupData;
|
|
|
|
|
|
/* 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);
|
|
}
|
|
|
|
static HFONT
|
|
CreateTitleFont(VOID)
|
|
{
|
|
NONCLIENTMETRICS ncm;
|
|
LOGFONT LogFont;
|
|
HDC hdc;
|
|
INT FontSize;
|
|
HFONT hFont;
|
|
|
|
ncm.cbSize = sizeof(NONCLIENTMETRICS);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
|
|
|
|
LogFont = ncm.lfMessageFont;
|
|
LogFont.lfWeight = FW_BOLD;
|
|
_tcscpy(LogFont.lfFaceName, _T("MS Shell Dlg"));
|
|
|
|
hdc = GetDC(NULL);
|
|
FontSize = 12;
|
|
LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72;
|
|
hFont = CreateFontIndirect(&LogFont);
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
return hFont;
|
|
}
|
|
|
|
INT DisplayError(
|
|
IN HWND hParentWnd OPTIONAL,
|
|
IN UINT uIDTitle,
|
|
IN UINT uIDMessage)
|
|
{
|
|
WCHAR message[512], caption[64];
|
|
|
|
LoadStringW(SetupData.hInstance, uIDMessage, message, ARRAYSIZE(message));
|
|
LoadStringW(SetupData.hInstance, uIDTitle, caption, ARRAYSIZE(caption));
|
|
|
|
return MessageBoxW(hParentWnd, message, caption, MB_OK | MB_ICONERROR);
|
|
}
|
|
|
|
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 */
|
|
SendDlgItemMessage(hwndDlg,
|
|
IDC_STARTTITLE,
|
|
WM_SETFONT,
|
|
(WPARAM)pSetupData->hTitleFont,
|
|
(LPARAM)TRUE);
|
|
|
|
/* Center the wizard window */
|
|
CenterWindow(GetParent(hwndDlg));
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFY:
|
|
{
|
|
LPNMHDR lpnm = (LPNMHDR)lParam;
|
|
|
|
switch (lpnm->code)
|
|
{
|
|
case PSN_SETACTIVE:
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
|
|
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);
|
|
|
|
/* 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 (MessageBoxW(GetParent(hwndDlg),
|
|
pSetupData->szAbortMessage,
|
|
pSetupData->szAbortTitle,
|
|
MB_YESNO | MB_ICONQUESTION) == 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);
|
|
PPARTENTRY PartEntry = NtOsInstall->PartEntry;
|
|
|
|
if (PartEntry && PartEntry->DriveLetter)
|
|
{
|
|
/* We have retrieved a partition that is mounted */
|
|
StringCchPrintfW(Buffer, cchBufferSize,
|
|
L"%c:%s",
|
|
PartEntry->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 (MessageBoxW(GetParent(hwndDlg),
|
|
pSetupData->szAbortMessage,
|
|
pSetupData->szAbortTitle,
|
|
MB_YESNO | MB_ICONQUESTION) == 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 (MessageBoxW(GetParent(hwndDlg),
|
|
pSetupData->szAbortMessage,
|
|
pSetupData->szAbortTitle,
|
|
MB_YESNO | MB_ICONQUESTION) == 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];
|
|
|
|
/* 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 (L'C') // FIXME!
|
|
{
|
|
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
|
|
L"%c: \x2014 %wZ",
|
|
L'C', // FIXME!
|
|
&pSetupData->USetupData.DestinationRootPath);
|
|
}
|
|
else
|
|
{
|
|
StringCchPrintfW(CurrentItemText, ARRAYSIZE(CurrentItemText),
|
|
L"%wZ",
|
|
&pSetupData->USetupData.DestinationRootPath);
|
|
}
|
|
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));
|
|
SetDlgItemTextW(GetParent(hwndDlg), ID_WIZNEXT, L"Install"); // FIXME: Localize!
|
|
|
|
/*
|
|
* 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 (MessageBoxW(GetParent(hwndDlg),
|
|
pSetupData->szAbortMessage,
|
|
pSetupData->szAbortTitle,
|
|
MB_YESNO | MB_ICONQUESTION) == 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 _COPYCONTEXT
|
|
{
|
|
PSETUPDATA pSetupData;
|
|
HWND hWndItem;
|
|
HWND hWndProgress;
|
|
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;
|
|
WCHAR Status[1024];
|
|
|
|
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;
|
|
|
|
SendMessageW(CopyContext->hWndProgress,
|
|
PBM_SETRANGE, 0,
|
|
MAKELPARAM(0, CopyContext->TotalOperations));
|
|
SendMessageW(CopyContext->hWndProgress,
|
|
PBM_SETSTEP, 1, 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;
|
|
|
|
// STRING_DELETING
|
|
StringCchPrintfW(Status, ARRAYSIZE(Status), L"Deleting %s", DstFileName);
|
|
SetWindowTextW(CopyContext->hWndItem, Status);
|
|
}
|
|
else if (Notification == SPFILENOTIFY_STARTRENAME)
|
|
{
|
|
/* 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;
|
|
|
|
// STRING_MOVING or STRING_RENAMING
|
|
if (!wcsicmp(SrcFileName, DstFileName))
|
|
StringCchPrintfW(Status, ARRAYSIZE(Status), L"Moving %s to %s", SrcFileName, DstFileName);
|
|
else
|
|
StringCchPrintfW(Status, ARRAYSIZE(Status), L"Renaming %s to %s", SrcFileName, DstFileName);
|
|
|
|
SetWindowTextW(CopyContext->hWndItem, Status);
|
|
}
|
|
else if (Notification == SPFILENOTIFY_STARTCOPY)
|
|
{
|
|
/* Display copy message */
|
|
ASSERT(Param2 == FILEOP_COPY);
|
|
|
|
DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
|
|
if (DstFileName) ++DstFileName;
|
|
else DstFileName = FilePathInfo->Target;
|
|
|
|
// STRING_COPYING
|
|
StringCchPrintfW(Status, ARRAYSIZE(Status), L"Copying %s", DstFileName);
|
|
SetWindowTextW(CopyContext->hWndItem, Status);
|
|
}
|
|
break;
|
|
}
|
|
|
|
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(CopyContext->hWndProgress, PBM_STEPIT, 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FILEOP_DOIT;
|
|
}
|
|
|
|
static DWORD
|
|
WINAPI
|
|
PrepareAndDoCopyThread(
|
|
IN LPVOID Param)
|
|
{
|
|
PSETUPDATA pSetupData;
|
|
HWND hwndDlg = (HWND)Param;
|
|
HWND hWndProgress;
|
|
LONG_PTR dwStyle;
|
|
// ERROR_NUMBER ErrorNumber;
|
|
BOOLEAN Success;
|
|
COPYCONTEXT CopyContext;
|
|
|
|
/* Retrieve pointer to the global setup data */
|
|
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
|
|
|
|
/* Get the progress handle */
|
|
hWndProgress = GetDlgItem(hwndDlg, IDC_PROCESSPROGRESS);
|
|
|
|
|
|
/*
|
|
* Preparation of the list of files to be copied
|
|
*/
|
|
|
|
/* Set status text */
|
|
SetDlgItemTextW(hwndDlg, IDC_ACTIVITY, L"Preparing the list of files to be copied, please wait...");
|
|
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
|
|
|
|
/* Set progress marquee style */
|
|
dwStyle = GetWindowLongPtrW(hWndProgress, GWL_STYLE);
|
|
SetWindowLongPtrW(hWndProgress, GWL_STYLE, dwStyle | PBS_MARQUEE);
|
|
|
|
/* Start it up */
|
|
SendMessageW(hWndProgress, PBM_SETMARQUEE, TRUE, 0);
|
|
|
|
/* Prepare the list of files */
|
|
/* ErrorNumber = */ Success = PrepareFileCopy(&pSetupData->USetupData, NULL);
|
|
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);
|
|
|
|
/* Stop it */
|
|
SendMessageW(hWndProgress, PBM_SETMARQUEE, FALSE, 0);
|
|
|
|
/* Restore progress style */
|
|
SetWindowLongPtrW(hWndProgress, GWL_STYLE, dwStyle);
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/* Stop it */
|
|
SendMessageW(hWndProgress, PBM_SETMARQUEE, FALSE, 0);
|
|
|
|
/* Restore progress style */
|
|
SetWindowLongPtrW(hWndProgress, GWL_STYLE, dwStyle);
|
|
|
|
|
|
/*
|
|
* Perform the file copy
|
|
*/
|
|
|
|
/* Set status text */
|
|
SetDlgItemTextW(hwndDlg, IDC_ACTIVITY, L"Copying the files...");
|
|
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
|
|
|
|
/* Create context for the copy process */
|
|
CopyContext.pSetupData = pSetupData;
|
|
CopyContext.hWndItem = GetDlgItem(hwndDlg, IDC_ITEM);
|
|
CopyContext.hWndProgress = hWndProgress;
|
|
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 */
|
|
SetDlgItemTextW(hwndDlg, IDC_ACTIVITY, L"Finalizing the installation...");
|
|
SetDlgItemTextW(hwndDlg, IDC_ITEM, L"");
|
|
|
|
/* Create the $winnt$.inf file */
|
|
InstallSetupInfFile(&pSetupData->USetupData);
|
|
|
|
/* 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 (MessageBoxW(GetParent(hwndDlg),
|
|
pSetupData->szAbortMessage,
|
|
pSetupData->szAbortTitle,
|
|
MB_YESNO | MB_ICONQUESTION) == 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 */
|
|
SendDlgItemMessage(hwndDlg,
|
|
IDC_FINISHTITLE,
|
|
WM_SETFONT,
|
|
(WPARAM)pSetupData->hTitleFont,
|
|
(LPARAM)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 */
|
|
ShowWindow(GetDlgItem(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)
|
|
{
|
|
BOOL ret = TRUE;
|
|
// INFCONTEXT InfContext;
|
|
// TCHAR tmp[10];
|
|
// DWORD LineLength;
|
|
// LONG Count;
|
|
|
|
/* 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);
|
|
|
|
pSetupData->PartitionList = CreatePartitionList();
|
|
|
|
pSetupData->NtOsInstallsList = CreateNTOSInstallationsList(pSetupData->PartitionList);
|
|
if (!pSetupData->NtOsInstallsList)
|
|
DPRINT1("Failed to get a list of NTOS installations; continue installation...\n");
|
|
|
|
|
|
/* new part */
|
|
pSetupData->SelectedLanguageId = pSetupData->DefaultLanguage;
|
|
wcscpy(pSetupData->DefaultLanguage, pSetupData->USetupData.LocaleID);
|
|
pSetupData->USetupData.LanguageId = (LANGID)(wcstol(pSetupData->SelectedLanguageId, NULL, 16) & 0xFFFF);
|
|
|
|
pSetupData->USetupData.LayoutList = CreateKeyboardLayoutList(pSetupData->USetupData.SetupInf, pSetupData->SelectedLanguageId, pSetupData->DefaultKBLayout);
|
|
|
|
#if 0
|
|
// get default for keyboard and language
|
|
pSetupData->DefaultKBLayout = -1;
|
|
pSetupData->DefaultLang = -1;
|
|
|
|
// TODO: get defaults from underlaying running system
|
|
if (SetupFindFirstLine(pSetupData->USetupData.SetupInf, _T("NLS"), _T("DefaultLayout"), &InfContext))
|
|
{
|
|
SetupGetStringField(&InfContext, 1, tmp, ARRAYSIZE(tmp), &LineLength);
|
|
for (Count = 0; Count < pSetupData->KbLayoutCount; Count++)
|
|
{
|
|
if (_tcscmp(tmp, pSetupData->pKbLayouts[Count].LayoutId) == 0)
|
|
{
|
|
pSetupData->DefaultKBLayout = Count;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SetupFindFirstLine(pSetupData->USetupData.SetupInf, _T("NLS"), _T("DefaultLanguage"), &InfContext))
|
|
{
|
|
SetupGetStringField(&InfContext, 1, tmp, ARRAYSIZE(tmp), &LineLength);
|
|
for (Count = 0; Count < pSetupData->LangCount; Count++)
|
|
{
|
|
if (_tcscmp(tmp, pSetupData->pLanguages[Count].LangId) == 0)
|
|
{
|
|
pSetupData->DefaultLang = Count;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
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)
|
|
{
|
|
STARTUPINFOW si = { sizeof(si) };
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if (CreateProcessW(L"cmd.exe",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi))
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Failed to launch command prompt: %lu\n", GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
UnregisterHotKey(NULL, hotkey);
|
|
GlobalDeleteAtom(hotkey);
|
|
|
|
DPRINT("HotkeyThread terminate\n");
|
|
return 0;
|
|
}
|
|
|
|
int WINAPI
|
|
_tWinMain(HINSTANCE hInst,
|
|
HINSTANCE hPrevInstance,
|
|
LPTSTR lpszCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
ULONG Error;
|
|
HANDLE hHotkeyThread;
|
|
INITCOMMONCONTROLSEX iccx;
|
|
PROPSHEETHEADER psh;
|
|
HPROPSHEETPAGE ahpsp[8];
|
|
PROPSHEETPAGE psp = {0};
|
|
UINT nPages = 0;
|
|
|
|
ProcessHeap = GetProcessHeap();
|
|
|
|
SetupData.hInstance = hInst;
|
|
SetupData.hInstallThread = NULL;
|
|
SetupData.hHaltInstallEvent = NULL;
|
|
SetupData.bStopInstall = FALSE;
|
|
|
|
/* Initialize the NT to Win32 path prefix mapping list */
|
|
InitNtToWin32PathMappingList(&SetupData.MappingList);
|
|
|
|
/* Initialize Setup, phase 0 */
|
|
InitializeSetup(&SetupData.USetupData, 0);
|
|
|
|
/* Initialize Setup, phase 1 */
|
|
Error = InitializeSetup(&SetupData.USetupData, 1);
|
|
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;
|
|
}
|
|
|
|
/* Load extra setup data (HW lists etc...) */
|
|
if (!LoadSetupData(&SetupData))
|
|
goto Quit;
|
|
|
|
hHotkeyThread = CreateThread(NULL, 0, HotkeyThread, NULL, 0, NULL);
|
|
|
|
CheckUnattendedSetup(&SetupData.USetupData);
|
|
SetupData.bUnattend = IsUnattendedSetup; // FIXME :-)
|
|
|
|
/* Cache commonly-used strings */
|
|
LoadStringW(hInst, IDS_ABORTSETUP, SetupData.szAbortMessage, ARRAYSIZE(SetupData.szAbortMessage));
|
|
LoadStringW(hInst, IDS_ABORTSETUP2, SetupData.szAbortTitle, ARRAYSIZE(SetupData.szAbortTitle));
|
|
|
|
/* 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 title font */
|
|
SetupData.hTitleFont = CreateTitleFont();
|
|
|
|
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_TYPETITLE);
|
|
psp.pszHeaderSubTitle = MAKEINTRESOURCEW(IDS_TYPESUBTITLE);
|
|
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.hTitleFont)
|
|
DeleteObject(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 */
|