reactos/base/setup/reactos/drivepage.c

2167 lines
75 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/drivepage.c
* PROGRAMMERS: Matthias Kupfer
* Dmitry Chapyshev (dmitry@reactos.org)
*/
#include "reactos.h"
#include <shlwapi.h>
#include <math.h> // For pow()
// #include <ntdddisk.h>
#include <ntddstor.h>
#include <ntddscsi.h>
#include "resource.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ******************************************************************/
#define IDS_LIST_COLUMN_FIRST IDS_PARTITION_NAME
#define IDS_LIST_COLUMN_LAST IDS_PARTITION_STATUS
#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, IDS_LIST_COLUMN_FIRST + 3};
static const INT column_widths[MAX_LIST_COLUMNS] = {200, 90, 60, 60};
static const INT column_alignment[MAX_LIST_COLUMNS] = {LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_RIGHT};
/* FUNCTIONS ****************************************************************/
/**
* @brief
* Sanitize a given string in-place, by
* removing any invalid character found in it.
**/
static BOOL
DoSanitizeText(
_Inout_ PWSTR pszSanitized)
{
PWCHAR pch1, pch2;
BOOL bSanitized = FALSE;
for (pch1 = pch2 = pszSanitized; *pch1; ++pch1)
{
/* Skip any invalid character found */
if (!IS_VALID_INSTALL_PATH_CHAR(*pch1))
{
bSanitized = TRUE;
continue;
}
/* Copy over the valid ones */
*pch2 = *pch1;
++pch2;
}
*pch2 = 0;
return bSanitized;
}
/**
* @brief
* Sanitize in-place any text found in the clipboard.
**/
static BOOL
DoSanitizeClipboard(
_In_ HWND hWnd)
{
HGLOBAL hData;
LPWSTR pszText, pszSanitized;
BOOL bSanitized;
/* Protect read-only edit control from modification */
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & ES_READONLY)
return FALSE;
if (!OpenClipboard(hWnd))
return FALSE;
hData = GetClipboardData(CF_UNICODETEXT);
pszText = GlobalLock(hData);
if (!pszText)
{
CloseClipboard();
return FALSE;
}
pszSanitized = _wcsdup(pszText);
GlobalUnlock(hData);
bSanitized = (pszSanitized && DoSanitizeText(pszSanitized));
if (bSanitized)
{
/* Update clipboard text */
SIZE_T cbData = (wcslen(pszSanitized) + 1) * sizeof(WCHAR);
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbData);
pszText = GlobalLock(hData);
if (pszText)
{
CopyMemory(pszText, pszSanitized, cbData);
GlobalUnlock(hData);
SetClipboardData(CF_UNICODETEXT, hData);
}
}
free(pszSanitized);
CloseClipboard();
return bSanitized;
}
static VOID
ShowErrorTip(
_In_ HWND hEdit)
{
EDITBALLOONTIP balloon;
WCHAR szTitle[512];
WCHAR szText[512];
/* Load the resources */
LoadStringW(SetupData.hInstance, IDS_ERROR_INVALID_INSTALLDIR_CHAR_TITLE, szTitle, _countof(szTitle));
LoadStringW(SetupData.hInstance, IDS_ERROR_INVALID_INSTALLDIR_CHAR, szText, _countof(szText));
/* Show a warning balloon */
balloon.cbStruct = sizeof(balloon);
balloon.pszTitle = szTitle;
balloon.pszText = szText;
#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
balloon.ttiIcon = TTI_ERROR;
#else
balloon.ttiIcon = TTI_ERROR_LARGE;
#endif
MessageBeep(MB_ICONERROR);
Edit_ShowBalloonTip(hEdit, &balloon);
// NOTE: There is no need to hide it when other keys are pressed;
// the EDIT control will deal with that itself.
}
/**
* @brief
* Subclass edit window procedure to filter allowed characters
* for the ReactOS installation directory.
**/
static LRESULT
CALLBACK
InstallDirEditProc(
_In_ HWND hWnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
WNDPROC orgEditProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
switch (uMsg)
{
case WM_UNICHAR:
if (wParam == UNICODE_NOCHAR)
return TRUE;
__fallthrough;
case WM_IME_CHAR:
case WM_CHAR:
{
WCHAR wch = (WCHAR)wParam;
/* Let the EDIT control deal with Control characters.
* It won't emit them as raw data in the text. */
if (wParam < ' ')
break;
/* Ignore Ctrl-Backspace */
if (wParam == '\x7F')
return 0;
/* Protect read-only edit control from modification */
if (GetWindowLongPtrW(hWnd, GWL_STYLE) & ES_READONLY)
break;
if (uMsg == WM_IME_CHAR)
{
if (!IsWindowUnicode(hWnd) && HIBYTE(wch) != 0)
{
CHAR data[] = {HIBYTE(wch), LOBYTE(wch)};
MultiByteToWideChar(CP_ACP, 0, data, 2, &wch, 1);
}
}
/* Show an error and ignore input character if it's invalid */
if (!IS_VALID_INSTALL_PATH_CHAR(wch))
{
ShowErrorTip(hWnd);
return 0;
}
break;
}
case WM_PASTE:
/* Verify the text being pasted; if it was sanitized, show an error */
if (DoSanitizeClipboard(hWnd))
ShowErrorTip(hWnd);
break;
}
return CallWindowProcW(orgEditProc, hWnd, uMsg, wParam, lParam);
}
static INT_PTR
CALLBACK
MoreOptDlgProc(
_In_ HWND hDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PSETUPDATA pSetupData;
/* Retrieve pointer to the global setup data */
pSetupData = (PSETUPDATA)GetWindowLongPtrW(hDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND hEdit;
WNDPROC orgEditProc;
BOOL bIsBIOS;
UINT uID;
INT iItem, iCurrent = CB_ERR, iDefault = 0;
WCHAR szText[50];
/* Save pointer to the global setup data */
pSetupData = (PSETUPDATA)lParam;
SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)pSetupData);
/* Subclass the install-dir edit control */
hEdit = GetDlgItem(hDlg, IDC_PATH);
orgEditProc = (WNDPROC)GetWindowLongPtrW(hEdit, GWLP_WNDPROC);
SetWindowLongPtrW(hEdit, GWLP_USERDATA, (LONG_PTR)orgEditProc);
SetWindowLongPtrW(hEdit, GWLP_WNDPROC, (LONG_PTR)InstallDirEditProc);
/* Set the current installation directory */
SetWindowTextW(hEdit, pSetupData->USetupData.InstallationDirectory);
/* Initialize the list of available bootloader locations */
bIsBIOS = ((pSetupData->USetupData.ArchType == ARCH_PcAT) ||
(pSetupData->USetupData.ArchType == ARCH_NEC98x86));
for (uID = IDS_BOOTLOADER_NOINST; uID <= IDS_BOOTLOADER_VBRONLY; ++uID)
{
if ( ( bIsBIOS && (uID == IDS_BOOTLOADER_SYSTEM)) ||
(!bIsBIOS && (uID == IDS_BOOTLOADER_MBRVBR || uID == IDS_BOOTLOADER_VBRONLY)) )
{
continue; // Skip this choice.
}
LoadStringW(pSetupData->hInstance, uID, szText, ARRAYSIZE(szText));
iItem = SendDlgItemMessageW(hDlg, IDC_INSTFREELDR, CB_ADDSTRING, 0, (LPARAM)szText);
if (iItem != CB_ERR && iItem != CB_ERRSPACE)
{
UINT uBldrLoc = uID - IDS_BOOTLOADER_NOINST
- (bIsBIOS && (uID >= IDS_BOOTLOADER_SYSTEM) ? 1 : 0);
SendDlgItemMessageW(hDlg, IDC_INSTFREELDR, CB_SETITEMDATA, iItem, uBldrLoc);
/* Find the index of the current and default locations */
if (uBldrLoc == pSetupData->USetupData.BootLoaderLocation)
iCurrent = iItem;
if (uBldrLoc == (IDS_BOOTLOADER_SYSTEM - IDS_BOOTLOADER_NOINST))
iDefault = iItem;
}
}
/* Select the current location or fall back to the default one */
if (iCurrent == CB_ERR)
iCurrent = iDefault;
SendDlgItemMessageW(hDlg, IDC_INSTFREELDR, CB_SETCURSEL, iCurrent, 0);
break;
}
case WM_DESTROY:
{
/* Unsubclass the edit control */
HWND hEdit = GetDlgItem(hDlg, IDC_PATH);
WNDPROC orgEditProc = (WNDPROC)GetWindowLongPtrW(hEdit, GWLP_USERDATA);
if (orgEditProc) SetWindowLongPtrW(hEdit, GWLP_WNDPROC, (LONG_PTR)orgEditProc);
break;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
{
HWND hEdit;
BOOL bIsValid;
WCHAR InstallDir[MAX_PATH];
INT iItem;
UINT uBldrLoc = CB_ERR;
/*
* Retrieve the installation path and verify its validity.
* Check for the validity of the installation directory and
* pop up an error if this is not the case.
*/
hEdit = GetDlgItem(hDlg, IDC_PATH);
bIsValid = (GetWindowTextLengthW(hEdit) < _countof(InstallDir)); // && IsValidInstallDirectory(InstallDir);
GetWindowTextW(hEdit, InstallDir, _countof(InstallDir));
bIsValid = bIsValid && IsValidInstallDirectory(InstallDir);
if (!bIsValid)
{
// ERROR_DIRECTORY_NAME
DisplayError(hDlg,
IDS_ERROR_DIRECTORY_NAME_TITLE,
IDS_ERROR_DIRECTORY_NAME);
break; // Go back to the dialog.
}
StringCchCopyW(pSetupData->USetupData.InstallationDirectory,
_countof(pSetupData->USetupData.InstallationDirectory),
InstallDir);
/* Retrieve the bootloader location */
iItem = SendDlgItemMessageW(hDlg, IDC_INSTFREELDR, CB_GETCURSEL, 0, 0);
if (iItem != CB_ERR)
uBldrLoc = SendDlgItemMessageW(hDlg, IDC_INSTFREELDR, CB_GETITEMDATA, iItem, 0);
if (uBldrLoc == CB_ERR) // Default location: System partition / MBR & VBR
uBldrLoc = (IDS_BOOTLOADER_SYSTEM - IDS_BOOTLOADER_NOINST);
uBldrLoc = min(max(uBldrLoc, 0), 3);
pSetupData->USetupData.BootLoaderLocation = uBldrLoc;
EndDialog(hDlg, IDOK);
return TRUE;
}
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
break;
}
return FALSE;
}
/**
* @brief
* Data structure stored for each partition item in the TreeList.
* (None for disks items.)
**/
typedef struct _PARTITEM
{
PPARTENTRY PartEntry; //< Disk region this structure is associated to.
PVOLENTRY Volume; //< Associated file system volume if any, or NULL.
PVOL_CREATE_INFO VolCreate; //< Volume create information (allocated).
} PARTITEM, *PPARTITEM;
/**
* @brief
* Dialog context structure used by PartitionDlgProc() and FormatDlgProc(Worker)().
**/
typedef struct _PARTCREATE_CTX
{
PPARTITEM PartItem; //< Partition item info stored in the TreeList.
ULONG MaxSizeMB; //< Maximum possible partition size in MB.
ULONG PartSizeMB; //< Selected partition size in MB.
BOOLEAN MBRExtPart; //< Whether to create an MBR extended partition.
BOOLEAN ForceFormat; //< Whether to force formatting ('Do not format' option hidden).
} PARTCREATE_CTX, *PPARTCREATE_CTX;
static INT_PTR
CALLBACK
FormatDlgProcWorker(
_In_ PPARTCREATE_CTX PartCreateCtx,
_In_ HWND hDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
ULONG Index = 0;
INT iItem;
PCWSTR FileSystem;
PCWSTR DefaultFs;
PVOLENTRY Volume;
PVOL_CREATE_INFO VolCreate;
/* List the well-known file systems. We use the same strings
* for the displayed FS names and their actual ones. */
while (GetRegisteredFileSystems(Index++, &FileSystem))
{
iItem = SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_INSERTSTRING, -1, (LPARAM)FileSystem);
if (iItem != CB_ERR && iItem != CB_ERRSPACE)
SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_SETITEMDATA, iItem, (LPARAM)FileSystem);
}
/* Add the 'Do not format' entry if needed */
if (!PartCreateCtx->ForceFormat)
{
WCHAR szText[50];
LoadStringW(SetupData.hInstance, IDS_VOLUME_NOFORMAT, szText, _countof(szText));
iItem = SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_INSERTSTRING, 0, (LPARAM)szText);
if (iItem != CB_ERR && iItem != CB_ERRSPACE)
SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_SETITEMDATA, iItem, (LPARAM)NULL);
}
// FIXME: Read from SetupData.FsType; select the "FAT" FS instead.
DefaultFs = L"FAT";
/* Retrieve the selected volume and create information */
ASSERT(PartCreateCtx->PartItem->Volume == PartCreateCtx->PartItem->PartEntry->Volume);
Volume = PartCreateCtx->PartItem->PartEntry->Volume;
VolCreate = PartCreateCtx->PartItem->VolCreate;
/* Select the existing file system in the list if any,
* otherwise use the "DefaultFs" */
if (VolCreate && *VolCreate->FileSystemName)
FileSystem = VolCreate->FileSystemName;
else if (Volume && *Volume->Info.FileSystem)
FileSystem = Volume->Info.FileSystem;
else
FileSystem = DefaultFs;
iItem = SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_FINDSTRINGEXACT, 0, (LPARAM)FileSystem);
if (iItem == CB_ERR)
iItem = 0;
SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_SETCURSEL, (WPARAM)iItem, 0);
/* Check the quick-format option by default as it speeds up formatting */
if (!VolCreate || VolCreate->QuickFormat)
CheckDlgButton(hDlg, IDC_CHECK_QUICKFMT, BST_CHECKED);
else
CheckDlgButton(hDlg, IDC_CHECK_QUICKFMT, BST_UNCHECKED);
break;
}
case WM_COMMAND:
{
//
// NOTE:
// - CBN_SELCHANGE sent everytime a combobox list item is selected,
// *even if* the list is opened.
// - CBN_SELENDOK sent only when user finished to select an item
// by clicking on it (if the list is opened), or selection changed
// (when the list is closed but selection is done with arrow keys).
//
if ((HIWORD(wParam) == CBN_SELCHANGE /*|| HIWORD(wParam) == CBN_SELENDOK*/) &&
(LOWORD(wParam) == IDC_FSTYPE))
{
// HWND hWndList = GetDlgItem(hDlg, IDC_FSTYPE);
PCWSTR FileSystem;
INT iItem;
/* Retrieve the selected file system. Use the
* item data instead of the displayed string. */
iItem = SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_GETCURSEL, 0, 0);
if (iItem == CB_ERR)
iItem = 0; // Default entry
FileSystem = (PCWSTR)SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_GETITEMDATA, iItem, 0);
if (FileSystem == (PCWSTR)CB_ERR)
FileSystem = NULL; // Default data
/* Enable or disable formatting options,
* depending on whether we need to format */
EnableDlgItem(hDlg, IDC_CHECK_QUICKFMT, (FileSystem && *FileSystem));
break;
}
if (HIWORD(wParam) != BN_CLICKED)
break;
switch (LOWORD(wParam))
{
case IDOK:
{
PPARTITEM PartItem = PartCreateCtx->PartItem;
PVOL_CREATE_INFO VolCreate;
PCWSTR FileSystem;
INT iItem;
/*
* Retrieve the formatting options
*/
/* Retrieve the selected file system. Use the
* item data instead of the displayed string. */
iItem = SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_GETCURSEL, 0, 0);
if (iItem == CB_ERR)
iItem = 0; // Default entry
FileSystem = (PCWSTR)SendDlgItemMessageW(hDlg, IDC_FSTYPE, CB_GETITEMDATA, iItem, 0);
if (FileSystem == (PCWSTR)CB_ERR)
FileSystem = NULL; // Default data
VolCreate = PartItem->VolCreate;
/* Check if we need to format */
if (!FileSystem || !*FileSystem)
{
/* We don't format. If there is an existing
* volume-create structure, free it. */
if (VolCreate)
LocalFree(VolCreate);
PartItem->VolCreate = NULL;
/* And return */
return TRUE;
}
/* We will format: allocate and initialize
* a volume-create structure if needed */
if (!VolCreate)
VolCreate = LocalAlloc(LPTR, sizeof(*VolCreate));
if (!VolCreate)
{
DPRINT1("Failed to allocate volume-create structure\n");
return TRUE;
}
/* Cached input information that will be set to
* the FORMAT_VOLUME_INFO structure given to the
* 'FSVOLNOTIFY_STARTFORMAT' step */
// TODO: Think about which values could be defaulted...
StringCchCopyW(VolCreate->FileSystemName,
_countof(VolCreate->FileSystemName),
FileSystem);
VolCreate->MediaFlag = FMIFS_HARDDISK;
VolCreate->Label = NULL;
VolCreate->QuickFormat =
(IsDlgButtonChecked(hDlg, IDC_CHECK_QUICKFMT) == BST_CHECKED);
VolCreate->ClusterSize = 0;
/* Set the volume associated to the new create information */
VolCreate->Volume = PartItem->Volume;
/* Associate the new, or update the volume create information */
PartItem->VolCreate = VolCreate;
return TRUE;
}
}
}
}
return FALSE;
}
static INT_PTR
CALLBACK
FormatDlgProc(
_In_ HWND hDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PPARTCREATE_CTX PartCreateCtx;
/* Retrieve dialog context pointer */
PartCreateCtx = (PPARTCREATE_CTX)GetWindowLongPtrW(hDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
/* Save dialog context pointer */
PartCreateCtx = (PPARTCREATE_CTX)lParam;
SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)PartCreateCtx);
/* We actually want to format, so set the flag */
PartCreateCtx->ForceFormat = TRUE;
break;
}
case WM_COMMAND:
{
if (HIWORD(wParam) != BN_CLICKED)
break;
switch (LOWORD(wParam))
{
case IDOK:
{
/* Retrieve the formatting options */
FormatDlgProcWorker(PartCreateCtx, hDlg, uMsg, wParam, lParam);
EndDialog(hDlg, IDOK);
return TRUE;
}
case IDCANCEL:
{
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
}
}
}
return FormatDlgProcWorker(PartCreateCtx, hDlg, uMsg, wParam, lParam);
}
static INT_PTR
CALLBACK
PartitionDlgProc(
_In_ HWND hDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
PPARTCREATE_CTX PartCreateCtx;
/* Retrieve dialog context pointer */
PartCreateCtx = (PPARTCREATE_CTX)GetWindowLongPtrW(hDlg, GWLP_USERDATA);
switch (uMsg)
{
case WM_INITDIALOG:
{
PPARTENTRY PartEntry;
PDISKENTRY DiskEntry;
ULONG MaxSizeMB;
/* Save dialog context pointer */
PartCreateCtx = (PPARTCREATE_CTX)lParam;
SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)PartCreateCtx);
/* Retrieve the selected partition */
PartEntry = PartCreateCtx->PartItem->PartEntry;
DiskEntry = PartEntry->DiskEntry;
/* Set the spinner to the maximum size in MB the partition can have */
MaxSizeMB = PartCreateCtx->MaxSizeMB;
SendDlgItemMessageW(hDlg, IDC_UPDOWN_PARTSIZE, UDM_SETRANGE32, (WPARAM)1, (LPARAM)MaxSizeMB);
SendDlgItemMessageW(hDlg, IDC_UPDOWN_PARTSIZE, UDM_SETPOS32, 0, (LPARAM)MaxSizeMB);
/* Default to regular partition (non-extended on MBR disks) */
CheckDlgButton(hDlg, IDC_CHECK_MBREXTPART, BST_UNCHECKED);
/* Also, disable and hide IDC_CHECK_MBREXTPART
* if space is logical or the disk is not MBR */
if ((DiskEntry->DiskStyle == PARTITION_STYLE_MBR) &&
!PartEntry->LogicalPartition)
{
ShowDlgItem(hDlg, IDC_CHECK_MBREXTPART, SW_SHOW);
EnableDlgItem(hDlg, IDC_CHECK_MBREXTPART, TRUE);
}
else
{
ShowDlgItem(hDlg, IDC_CHECK_MBREXTPART, SW_HIDE);
EnableDlgItem(hDlg, IDC_CHECK_MBREXTPART, FALSE);
}
break;
}
case WM_COMMAND:
{
if (HIWORD(wParam) != BN_CLICKED)
break;
switch (LOWORD(wParam))
{
case IDC_CHECK_MBREXTPART:
{
/* Check for MBR-extended (container) partition */
// BST_UNCHECKED or BST_INDETERMINATE => FALSE
if (IsDlgButtonChecked(hDlg, IDC_CHECK_MBREXTPART) == BST_CHECKED)
{
/* It is, disable formatting options */
EnableDlgItem(hDlg, IDC_FS_STATIC, FALSE);
EnableDlgItem(hDlg, IDC_FSTYPE, FALSE);
EnableDlgItem(hDlg, IDC_CHECK_QUICKFMT, FALSE);
}
else
{
/* It is not, re-enable formatting options */
EnableDlgItem(hDlg, IDC_FS_STATIC, TRUE);
EnableDlgItem(hDlg, IDC_FSTYPE, TRUE);
EnableDlgItem(hDlg, IDC_CHECK_QUICKFMT, TRUE);
}
break;
}
case IDOK:
{
/* Collect all the information and return it
* to the caller for creating the partition */
PartCreateCtx->PartSizeMB = (ULONG)SendDlgItemMessageW(hDlg, IDC_UPDOWN_PARTSIZE, UDM_GETPOS32, 0, (LPARAM)NULL);
PartCreateCtx->PartSizeMB = min(max(PartCreateCtx->PartSizeMB, 1), PartCreateCtx->MaxSizeMB);
PartCreateCtx->MBRExtPart = (IsDlgButtonChecked(hDlg, IDC_CHECK_MBREXTPART) == BST_CHECKED);
/* Retrieve the formatting options */
FormatDlgProcWorker(PartCreateCtx, hDlg, uMsg, wParam, lParam);
EndDialog(hDlg, IDOK);
return TRUE;
}
case IDCANCEL:
{
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
}
}
}
return FormatDlgProcWorker(PartCreateCtx, hDlg, uMsg, wParam, lParam);
}
BOOL
CreateTreeListColumns(
IN HINSTANCE hInstance,
IN HWND hWndTreeList,
IN const UINT* pIDs,
IN const INT* pColsWidth,
IN const INT* pColsAlign,
IN UINT nNumOfColumns)
{
UINT i;
TLCOLUMN tlC;
WCHAR szText[50];
/* Create the columns */
tlC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
tlC.pszText = szText;
/* Load the column labels from the resource file */
for (i = 0; i < nNumOfColumns; i++)
{
tlC.iSubItem = i;
tlC.cx = pColsWidth[i];
tlC.fmt = pColsAlign[i];
LoadStringW(hInstance, pIDs[i], szText, ARRAYSIZE(szText));
if (TreeList_InsertColumn(hWndTreeList, i, &tlC) == -1)
return FALSE;
}
return TRUE;
}
HTLITEM
TreeListAddItem(
_In_ HWND hTreeList,
_In_opt_ HTLITEM hParent,
_In_opt_ HTLITEM hInsertAfter,
_In_ LPCWSTR lpText,
_In_ INT iImage,
_In_ INT iSelectedImage,
_In_ LPARAM lParam)
{
TLINSERTSTRUCTW Insert;
ZeroMemory(&Insert, sizeof(Insert));
Insert.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
Insert.hParent = hParent;
Insert.hInsertAfter = (hInsertAfter ? hInsertAfter : TVI_LAST);
Insert.item.pszText = (LPWSTR)lpText;
Insert.item.iImage = iImage;
Insert.item.iSelectedImage = iSelectedImage;
Insert.item.lParam = lParam;
// Insert.item.mask |= TVIF_STATE;
// Insert.item.stateMask = TVIS_OVERLAYMASK;
// Insert.item.state = INDEXTOOVERLAYMASK(1);
return TreeList_InsertItem(hTreeList, &Insert);
}
LPARAM
TreeListGetItemData(
_In_ HWND hTreeList,
_In_ HTLITEM hItem)
{
TLITEMW tlItem;
tlItem.mask = TVIF_PARAM;
tlItem.hItem = hItem;
TreeList_GetItem(hTreeList, &tlItem);
return tlItem.lParam;
}
static
PPARTITEM
GetItemPartition(
_In_ HWND hTreeList,
_In_ HTLITEM hItem)
{
HTLITEM hParentItem;
PPARTITEM PartItem;
hParentItem = TreeList_GetParent(hTreeList, hItem);
/* May or may not be a PPARTITEM: this is a PPARTITEM only when hParentItem != NULL */
PartItem = (PPARTITEM)TreeListGetItemData(hTreeList, hItem);
if (!hParentItem || !PartItem)
return NULL;
return PartItem;
}
static
PPARTITEM
GetSelectedPartition(
_In_ HWND hTreeList,
_Out_opt_ HTLITEM* phItem)
{
HTLITEM hItem;
PPARTITEM PartItem;
hItem = TreeList_GetSelection(hTreeList);
if (!hItem)
return NULL;
PartItem = GetItemPartition(hTreeList, hItem);
if (PartItem && phItem)
*phItem = hItem;
return PartItem;
}
PVOL_CREATE_INFO
FindVolCreateInTreeByVolume(
_In_ HWND hTreeList,
_In_ PVOLENTRY Volume)
{
HTLITEM hItem;
/* Enumerate every cached data in the TreeList, and for each, check
* whether its corresponding PPARTENTRY is the one we are looking for */
// for (hItem = TVI_ROOT; hItem; hItem = TreeList_GetNextItem(...)) { }
hItem = TVI_ROOT;
while ((hItem = TreeList_GetNextItem(hTreeList, hItem, TVGN_NEXTITEM)))
{
PPARTITEM PartItem = GetItemPartition(hTreeList, hItem);
if (!PartItem || !PartItem->VolCreate /* || !PartItem->Volume */)
continue;
if (PartItem->Volume == Volume)
{
/* Found it, return the associated volume-create structure */
return PartItem->VolCreate;
}
}
/* Nothing was found */
return NULL;
}
VOID
GetPartitionTypeString(
IN PPARTENTRY PartEntry,
OUT PSTR strBuffer,
IN ULONG cchBuffer)
{
if (PartEntry->PartitionType == PARTITION_ENTRY_UNUSED)
{
StringCchCopyA(strBuffer, cchBuffer,
"Unused" /* MUIGetString(STRING_FORMATUNUSED) */);
}
// else if (PartEntry == PartEntry->DiskEntry->ExtendedPartition)
else if (IsContainerPartition(PartEntry->PartitionType))
{
StringCchCopyA(strBuffer, cchBuffer,
"Extended Partition" /* MUIGetString(STRING_EXTENDED_PARTITION) */);
}
else
{
/* Do the table lookup */
PCSTR Description = LookupPartitionTypeString(PartEntry->DiskEntry->DiskStyle,
&PartEntry->PartitionType);
if (Description)
{
StringCchCopyA(strBuffer, cchBuffer, Description);
return;
}
/* We are here because the partition type is unknown */
if (cchBuffer > 0) *strBuffer = '\0';
}
if ((cchBuffer > 0) && (*strBuffer == '\0'))
{
StringCchPrintfA(strBuffer, cchBuffer,
// MUIGetString(STRING_PARTTYPE),
"Type 0x%02x",
PartEntry->PartitionType);
}
}
static VOID
PrettifySize1(
_Inout_ PULONGLONG Size,
_Out_ PCWSTR* Unit)
{
ULONGLONG DiskSize = *Size;
if (DiskSize >= 10 * GB) /* 10 GB */
{
DiskSize = RoundingDivide(DiskSize, GB);
*Unit = L"GB"; // MUIGetString(STRING_GB);
}
else
{
DiskSize = RoundingDivide(DiskSize, MB);
if (DiskSize == 0)
DiskSize = 1;
*Unit = L"MB"; // MUIGetString(STRING_MB);
}
*Size = DiskSize;
}
static VOID
PrettifySize2(
_Inout_ PULONGLONG Size,
_Out_ PCWSTR* Unit)
{
ULONGLONG PartSize = *Size;
#if 0
if (PartSize >= 10 * GB) /* 10 GB */
{
PartSize = RoundingDivide(PartSize, GB);
*Unit = L"GB"; // MUIGetString(STRING_GB);
}
else
#endif
if (PartSize >= 10 * MB) /* 10 MB */
{
PartSize = RoundingDivide(PartSize, MB);
*Unit = L"MB"; // MUIGetString(STRING_MB);
}
else
{
PartSize = RoundingDivide(PartSize, KB);
*Unit = L"KB"; // MUIGetString(STRING_KB);
}
*Size = PartSize;
}
static
HTLITEM
PrintPartitionData(
_In_ HWND hWndList,
_In_ HTLITEM htiParent,
_In_opt_ HTLITEM hInsertAfter,
_In_ PPARTENTRY PartEntry)
{
PDISKENTRY DiskEntry = PartEntry->DiskEntry;
PVOLINFO VolInfo = (PartEntry->Volume ? &PartEntry->Volume->Info : NULL);
PPARTITEM PartItem;
ULONGLONG PartSize;
HTLITEM htiPart;
CHAR PartTypeString[32];
PCHAR PartType = PartTypeString;
WCHAR LineBuffer[128];
/* Volume name */
if (PartEntry->IsPartitioned == FALSE)
{
/* Unpartitioned space: Just display the description */
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_UNPSPACE)
L"Unpartitioned space");
}
else
//
// NOTE: This could be done with the next case.
//
if ((DiskEntry->DiskStyle == PARTITION_STYLE_MBR) &&
IsContainerPartition(PartEntry->PartitionType))
{
/* Extended partition container: Just display the partition's type */
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_EXTENDED_PARTITION)
L"Extended Partition");
}
else
{
/* Drive letter and partition number */
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_HDDINFOUNK5),
L"%s (%c%c)",
(VolInfo && *VolInfo->VolumeLabel) ? VolInfo->VolumeLabel : L"Partition",
!(VolInfo && VolInfo->DriveLetter) ? L'-' : VolInfo->DriveLetter,
!(VolInfo && VolInfo->DriveLetter) ? L'-' : L':');
}
/* Allocate and initialize a partition-info structure */
PartItem = LocalAlloc(LPTR, sizeof(*PartItem));
if (!PartItem)
{
DPRINT1("Failed to allocate partition-info structure\n");
// return NULL;
// We'll store a NULL pointer?!
}
PartItem->PartEntry = PartEntry;
PartItem->Volume = PartEntry->Volume;
htiPart = TreeListAddItem(hWndList, htiParent, hInsertAfter,
LineBuffer, 1, 1,
(LPARAM)PartItem);
*LineBuffer = 0;
if (PartEntry->IsPartitioned)
{
PartTypeString[0] = '\0';
/*
* If the volume's file system is recognized, display the volume label
* (if any) and the file system name. Otherwise, display the partition
* type if it's not a new partition.
*/
if (VolInfo && *VolInfo->FileSystem &&
_wcsicmp(VolInfo->FileSystem, L"RAW") != 0)
{
// TODO: Group this part together with the similar one
// from below once the strings are in the same encoding...
if (PartEntry->New)
{
StringCchPrintfA(PartTypeString,
ARRAYSIZE(PartTypeString),
"New (%S)",
VolInfo->FileSystem);
}
else
{
StringCchPrintfA(PartTypeString,
ARRAYSIZE(PartTypeString),
"%S",
VolInfo->FileSystem);
}
PartType = PartTypeString;
}
else
/* Determine partition type */
if (PartEntry->New)
{
/* Use this description if the partition is new and not planned for formatting */
PartType = "New (Unformatted)"; // MUIGetString(STRING_UNFORMATTED);
}
else
{
/* If the partition is not new but its file system is not recognized
* (or is not formatted), use the partition type description. */
GetPartitionTypeString(PartEntry,
PartTypeString,
ARRAYSIZE(PartTypeString));
PartType = PartTypeString;
}
#if 0
if (!PartType || !*PartType)
{
PartType = MUIGetString(STRING_FORMATUNKNOWN);
}
#endif
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
L"%S",
PartType);
}
TreeList_SetItemText(hWndList, htiPart, 1, LineBuffer);
/* Format the partition size */
PartSize = GetPartEntrySizeInBytes(PartEntry);
if (!StrFormatByteSizeW(PartSize, LineBuffer, ARRAYSIZE(LineBuffer)))
{
/* We failed for whatever reason, do the hardcoded way */
PCWSTR Unit;
PrettifySize2(&PartSize, &Unit);
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
L"%6I64u %s",
PartSize, Unit);
}
TreeList_SetItemText(hWndList, htiPart, 2, LineBuffer);
/* Volume status */
*LineBuffer = 0;
if (PartEntry->IsPartitioned)
{
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_HDDINFOUNK5),
PartEntry->BootIndicator ? L"Active" : L"");
}
TreeList_SetItemText(hWndList, htiPart, 3, LineBuffer);
/* If this is the single extended partition of an MBR disk,
* recursively display its logical partitions */
if (PartEntry == DiskEntry->ExtendedPartition)
{
PLIST_ENTRY LogicalEntry;
PPARTENTRY LogicalPartEntry;
for (LogicalEntry = DiskEntry->LogicalPartListHead.Flink;
LogicalEntry != &DiskEntry->LogicalPartListHead;
LogicalEntry = LogicalEntry->Flink)
{
LogicalPartEntry = CONTAINING_RECORD(LogicalEntry, PARTENTRY, ListEntry);
PrintPartitionData(hWndList, htiPart, NULL, LogicalPartEntry);
}
/* Expand the extended partition node */
TreeList_Expand(hWndList, htiPart, TVE_EXPAND);
}
return htiPart;
}
/**
* @brief
* Called on response to the TVN_DELETEITEM notification sent by the TreeList.
**/
static
VOID
DeleteTreeItem(
_In_ HWND hWndList,
_In_ TLITEMW* ptlItem)
{
PPARTITEM PartItem;
/* Code below is equivalent to: PartItem = GetItemPartition(hWndList, ptlItem->hItem);
* except that we already have the data structure in ptlItem->lParam, so there is
* no need to call extra helpers as GetItemPartition() does. */
HTLITEM hParentItem = TreeList_GetParent(hWndList, ptlItem->hItem);
/* May or may not be a PPARTITEM: this is a PPARTITEM only when hParentItem != NULL */
PartItem = (PPARTITEM)ptlItem->lParam;
if (!hParentItem || !PartItem)
return;
if (PartItem->VolCreate)
LocalFree(PartItem->VolCreate);
LocalFree(PartItem);
}
static
VOID
PrintDiskData(
_In_ HWND hWndList,
_In_opt_ HTLITEM hInsertAfter,
_In_ PDISKENTRY DiskEntry)
{
BOOL Success;
HANDLE hDevice;
PCHAR DiskName = NULL;
ULONG Length = 0;
PPARTENTRY PrimaryPartEntry;
PLIST_ENTRY PrimaryEntry;
ULONGLONG DiskSize;
HTLITEM htiDisk;
WCHAR LineBuffer[128];
UCHAR outBuf[512];
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// L"\\Device\\Harddisk%lu\\Partition%lu",
L"\\\\.\\PhysicalDrive%lu",
DiskEntry->DiskNumber);
hDevice = CreateFileW(
LineBuffer, // device interface name
GENERIC_READ /*| GENERIC_WRITE*/, // dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
NULL, // lpSecurityAttributes
OPEN_EXISTING, // dwCreationDistribution
0, // dwFlagsAndAttributes
NULL // hTemplateFile
);
if (hDevice != INVALID_HANDLE_VALUE)
{
STORAGE_PROPERTY_QUERY Query;
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
Success = DeviceIoControl(hDevice,
IOCTL_STORAGE_QUERY_PROPERTY,
&Query,
sizeof(Query),
&outBuf,
sizeof(outBuf),
&Length,
NULL);
if (Success)
{
PSTORAGE_DEVICE_DESCRIPTOR devDesc = (PSTORAGE_DEVICE_DESCRIPTOR)outBuf;
if (devDesc->ProductIdOffset)
{
DiskName = (PCHAR)&outBuf[devDesc->ProductIdOffset];
Length -= devDesc->ProductIdOffset;
DiskName[min(Length, strlen(DiskName))] = 0;
// ( i = devDesc->ProductIdOffset; p[i] != 0 && i < Length; i++ )
}
}
CloseHandle(hDevice);
}
if (DiskName && *DiskName)
{
if (DiskEntry->DriverName.Length > 0)
{
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_HDDINFO_1),
L"Harddisk %lu (%S) (Port=%hu, Bus=%hu, Id=%hu) on %wZ",
DiskEntry->DiskNumber,
DiskName,
DiskEntry->Port,
DiskEntry->Bus,
DiskEntry->Id,
&DiskEntry->DriverName);
}
else
{
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_HDDINFO_2),
L"Harddisk %lu (%S) (Port=%hu, Bus=%hu, Id=%hu)",
DiskEntry->DiskNumber,
DiskName,
DiskEntry->Port,
DiskEntry->Bus,
DiskEntry->Id);
}
}
else
{
if (DiskEntry->DriverName.Length > 0)
{
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_HDDINFO_1),
L"Harddisk %lu (Port=%hu, Bus=%hu, Id=%hu) on %wZ",
DiskEntry->DiskNumber,
DiskEntry->Port,
DiskEntry->Bus,
DiskEntry->Id,
&DiskEntry->DriverName);
}
else
{
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
// MUIGetString(STRING_HDDINFO_2),
L"Harddisk %lu (Port=%hu, Bus=%hu, Id=%hu)",
DiskEntry->DiskNumber,
DiskEntry->Port,
DiskEntry->Bus,
DiskEntry->Id);
}
}
htiDisk = TreeListAddItem(hWndList, NULL, hInsertAfter,
LineBuffer, 0, 0,
(LPARAM)DiskEntry);
/* Disk type: MBR, GPT or RAW (Uninitialized) */
TreeList_SetItemText(hWndList, htiDisk, 1,
DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? L"MBR" :
DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? L"GPT" :
L"RAW");
/* Format the disk size */
DiskSize = GetDiskSizeInBytes(DiskEntry);
if (!StrFormatByteSizeW(DiskSize, LineBuffer, ARRAYSIZE(LineBuffer)))
{
/* We failed for whatever reason, do the hardcoded way */
PCWSTR Unit;
PrettifySize1(&DiskSize, &Unit);
StringCchPrintfW(LineBuffer, ARRAYSIZE(LineBuffer),
L"%6I64u %s",
DiskSize, Unit);
}
TreeList_SetItemText(hWndList, htiDisk, 2, LineBuffer);
/* Print partition lines */
for (PrimaryEntry = DiskEntry->PrimaryPartListHead.Flink;
PrimaryEntry != &DiskEntry->PrimaryPartListHead;
PrimaryEntry = PrimaryEntry->Flink)
{
PrimaryPartEntry = CONTAINING_RECORD(PrimaryEntry, PARTENTRY, ListEntry);
/* If this is an extended partition, recursively print the logical partitions */
PrintPartitionData(hWndList, htiDisk, NULL, PrimaryPartEntry);
}
/* Expand the disk node */
TreeList_Expand(hWndList, htiDisk, TVE_EXPAND);
}
static VOID
InitPartitionList(
_In_ HINSTANCE hInstance,
_In_ HWND hWndList)
{
const DWORD dwStyle = TVS_HASBUTTONS | /*TVS_HASLINES | TVS_LINESATROOT |*/ TVS_SHOWSELALWAYS | TVS_FULLROWSELECT;
const DWORD dwExStyle = TVS_EX_FULLROWMARK /* | TVS_EX_FULLROWITEMS*/;
HIMAGELIST hSmall;
/* Set the TreeList styles and fixup the item selection color */
TreeList_SetStyle(hWndList, TreeList_GetStyle(hWndList) | dwStyle);
// TreeList_SetStyleEx(hWndList, dwStyle, dwStyle);
TreeList_SetExtendedStyle(hWndList, dwExStyle);
TreeList_SetColor(hWndList, TVC_MARK, GetSysColor(COLOR_HIGHLIGHT));
/* Initialize its columns */
CreateTreeListColumns(hInstance,
hWndList,
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(hInstance, MAKEINTRESOURCEW(IDI_DISKDRIVE)));
ImageList_AddIcon(hSmall, LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_PARTITION)));
/* Assign the ImageList to the List View */
TreeList_SetImageList(hWndList, hSmall, TVSIL_NORMAL);
}
static VOID
DrawPartitionList(
_In_ HWND hWndList,
_In_ PPARTLIST List)
{
PLIST_ENTRY Entry;
PDISKENTRY DiskEntry;
/* Clear the list first */
TreeList_DeleteAllItems(hWndList);
/* Insert all the detected disks and partitions */
for (Entry = List->DiskListHead.Flink;
Entry != &List->DiskListHead;
Entry = Entry->Flink)
{
DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
/* Print disk entry */
PrintDiskData(hWndList, NULL, DiskEntry);
}
/* Select the first item */
// TreeList_SetFocusItem(hWndList, 1, 1);
TreeList_SelectItem(hWndList, 1);
}
static VOID
CleanupPartitionList(
_In_ HWND hWndList)
{
HIMAGELIST hSmall;
/* Cleanup all the items. Their cached data will be automatically deleted
* on response to the TVN_DELETEITEM notification sent by the TreeList
* by DeleteTreeItem() */
TreeList_DeleteAllItems(hWndList);
/* And cleanup the imagelist */
hSmall = TreeList_GetImageList(hWndList, TVSIL_NORMAL);
TreeList_SetImageList(hWndList, NULL, TVSIL_NORMAL);
ImageList_Destroy(hSmall);
}
/**
* @brief
* Create a partition in the selected disk region in the partition list,
* and update the partition list UI.
**/
static BOOLEAN
DoCreatePartition(
_In_ HWND hList,
_In_ PPARTLIST List,
_Inout_ HTLITEM* phItem,
_Inout_opt_ PPARTITEM* pPartItem,
_In_opt_ ULONGLONG SizeBytes,
_In_opt_ ULONG_PTR PartitionInfo)
{
BOOLEAN Success;
PPARTITEM PartItem;
PPARTENTRY PartEntry;
HTLITEM hParentItem;
HTLITEM hInsertAfter;
PPARTENTRY NextPart;
PartItem = (pPartItem ? *pPartItem : GetItemPartition(hList, *phItem));
if (!PartItem)
{
/* We must have a disk region... */
ASSERT(FALSE);
return FALSE;
}
PartEntry = PartItem->PartEntry;
#if 0
if (PartEntry->IsPartitioned)
{
/* Don't create a partition when one already exists */
ASSERT(FALSE);
return FALSE;
}
ASSERT(!PartEntry->Volume);
#endif
Success = CreatePartition(List,
PartEntry,
SizeBytes,
PartitionInfo);
if (!Success)
return Success;
/* Retrieve the parent item (disk or MBR extended partition) */
hParentItem = TreeList_GetParent(hList, *phItem);
hInsertAfter = TreeList_GetPrevSibling(hList, *phItem);
if (!hInsertAfter)
hInsertAfter = TVI_FIRST;
/*
* The current entry has been recreated and maybe split
* in two: new partition and remaining unused space.
* Thus, recreate the current entry.
*
* NOTE: Since we create a partition we don't care
* about its previous PartItem, so it can be deleted.
*/
{
/* Cache out the original item's volume create info */
PVOL_CREATE_INFO VolCreate = PartItem->VolCreate;
PartItem->VolCreate = NULL;
/* Remove the current item */
TreeList_DeleteItem(hList, *phItem);
/* Recreate the entry */
*phItem = PrintPartitionData(hList, hParentItem, hInsertAfter, PartEntry);
/* Retrieve the new PartItem and restore the volume create
* information associated to the newly-created partition */
PartItem = GetItemPartition(hList, *phItem);
ASSERT(PartItem);
/* Update the volume associated to the create information */
if (VolCreate)
VolCreate->Volume = PartItem->Volume;
/* Restore the volume create information */
PartItem->VolCreate = VolCreate;
if (pPartItem)
*pPartItem = PartItem;
}
/* Add also the following unused space, if any */
// NextPart = GetAdjDiskRegion(NULL, PartEntry, ENUM_REGION_NEXT);
NextPart = GetAdjUnpartitionedEntry(PartEntry, TRUE);
if (NextPart /*&& !NextPart->IsPartitioned*/)
PrintPartitionData(hList, hParentItem, *phItem, NextPart);
/* Give the focus on and select the created partition */
// TreeList_SetFocusItem(hList, 1, 1);
TreeList_SelectItem(hList, *phItem);
return TRUE;
}
/**
* @brief
* Delete the selected partition in the partition list,
* and update the partition list UI.
**/
static BOOLEAN
DoDeletePartition(
_In_ HWND hList,
_In_ PPARTLIST List,
_Inout_ HTLITEM* phItem,
_In_ PPARTITEM PartItem)
{
PPARTENTRY PartEntry = PartItem->PartEntry;
PPARTENTRY PrevPart, NextPart;
BOOLEAN PrevIsPartitioned, NextIsPartitioned;
BOOLEAN Success;
HTLITEM hParentItem;
HTLITEM hInsertAfter;
HTLITEM hAdjItem;
PPARTITEM AdjPartItem;
#if 0
if (!PartEntry->IsPartitioned)
{
/* Don't delete an unpartitioned disk region */
ASSERT(FALSE);
return FALSE;
}
#endif
/*
* Determine the nature of the previous and next disk regions,
* in order to know what to do on the corresponding tree items
* after the selected partition has been deleted.
*
* NOTE: Don't reference these pointers after DeletePartition(),
* since these disk regions might have been coalesced/reallocated.
* However we can check whether they were NULL or not.
*/
// PrevPart = GetAdjDiskRegion(NULL, PartEntry, ENUM_REGION_PREV);
// NextPart = GetAdjDiskRegion(NULL, PartEntry, ENUM_REGION_NEXT);
PrevPart = GetAdjUnpartitionedEntry(PartEntry, FALSE);
NextPart = GetAdjUnpartitionedEntry(PartEntry, TRUE);
PrevIsPartitioned = (PrevPart && PrevPart->IsPartitioned);
NextIsPartitioned = (NextPart && NextPart->IsPartitioned);
ASSERT(PartEntry->IsPartitioned); // The current partition must be partitioned.
Success = DeletePartition(List,
PartEntry,
&PartEntry);
if (!Success)
return Success;
/* Retrieve the parent item (disk or MBR extended partition) */
hParentItem = TreeList_GetParent(hList, *phItem);
/* If previous sibling isn't partitioned, remove it */
if (PrevPart && !PrevIsPartitioned)
{
hAdjItem = TreeList_GetPrevSibling(hList, *phItem);
ASSERT(hAdjItem); // TODO: Investigate
if (hAdjItem)
{
AdjPartItem = GetItemPartition(hList, hAdjItem);
ASSERT(AdjPartItem && (AdjPartItem->PartEntry == PrevPart));
AdjPartItem = NULL;
TreeList_DeleteItem(hList, hAdjItem);
}
}
/* If next sibling isn't partitioned, remove it */
if (NextPart && !NextIsPartitioned)
{
hAdjItem = TreeList_GetNextSibling(hList, *phItem);
ASSERT(hAdjItem); // TODO: Investigate
if (hAdjItem)
{
AdjPartItem = GetItemPartition(hList, hAdjItem);
ASSERT(AdjPartItem && (AdjPartItem->PartEntry == NextPart));
AdjPartItem = NULL;
TreeList_DeleteItem(hList, hAdjItem);
}
}
/* We are going to insert the updated entry after
* either, the original previous sibling (if it is
* partitioned), or the second-previous sibling
* (if the first one was already removed, see above) */
hInsertAfter = TreeList_GetPrevSibling(hList, *phItem);
if (!hInsertAfter)
hInsertAfter = TVI_FIRST;
/* Remove the current item */
TreeList_DeleteItem(hList, *phItem);
/* Add back the "new" unpartitioned space */
*phItem = PrintPartitionData(hList, hParentItem, hInsertAfter, PartEntry);
/* Give the focus on and select the unpartitioned space */
// TreeList_SetFocusItem(hList, 1, 1);
TreeList_SelectItem(hList, *phItem);
return TRUE;
}
INT_PTR
CALLBACK
DriveDlgProc(
_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, (LONG_PTR)pSetupData);
/* Initially hide and disable all partitioning buttons */
ShowDlgItem(hwndDlg, IDC_INITDISK, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_INITDISK, FALSE);
ShowDlgItem(hwndDlg, IDC_PARTCREATE, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, FALSE);
ShowDlgItem(hwndDlg, IDC_PARTFORMAT, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTFORMAT, FALSE);
ShowDlgItem(hwndDlg, IDC_PARTDELETE, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTDELETE, FALSE);
/* Initialize the partitions list */
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
UiContext.hPartList = hList;
InitPartitionList(pSetupData->hInstance, hList);
DrawPartitionList(hList, pSetupData->PartitionList);
// HACK: Wine "kwality" code doesn't still implement
// PSN_QUERYINITIALFOCUS so we "emulate" its call there...
{
PSHNOTIFY pshn = {{hwndDlg, GetWindowLong(hwndDlg, GWL_ID), PSN_QUERYINITIALFOCUS}, (LPARAM)hList};
SendMessageW(hwndDlg, WM_NOTIFY, (WPARAM)pshn.hdr.idFrom, (LPARAM)&pshn);
}
break;
}
case WM_DESTROY:
{
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
ASSERT(UiContext.hPartList == hList);
UiContext.hPartList = NULL;
CleanupPartitionList(hList);
return TRUE;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_PARTMOREOPTS:
{
DialogBoxParamW(pSetupData->hInstance,
MAKEINTRESOURCEW(IDD_ADVINSTOPTS),
hwndDlg,
MoreOptDlgProc,
(LPARAM)pSetupData);
break;
}
case IDC_INITDISK:
{
// TODO: Implement disk partitioning initialization
break;
}
case IDC_PARTCREATE:
{
HTLITEM hItem;
PPARTITEM PartItem;
PPARTENTRY PartEntry;
ULONGLONG PartSize;
ULONGLONG MaxPartSize;
ULONG MaxSizeMB;
INT_PTR ret;
PARTCREATE_CTX PartCreateCtx = {0};
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
PartItem = GetSelectedPartition(hList, &hItem);
if (!PartItem)
{
/* If the button was clicked, an empty disk
* region should have been selected first */
ASSERT(FALSE);
break;
}
PartEntry = PartItem->PartEntry;
if (PartEntry->IsPartitioned)
{
/* Don't create a partition when one already exists */
ASSERT(FALSE);
break;
}
ASSERT(!PartEntry->Volume);
/* Get the partition info stored in the TreeList */
PartCreateCtx.PartItem = PartItem;
/* Retrieve the maximum size in MB (rounded up) the partition can have */
MaxPartSize = GetPartEntrySizeInBytes(PartEntry);
MaxSizeMB = (ULONG)RoundingDivide(MaxPartSize, MB);
PartCreateCtx.MaxSizeMB = MaxSizeMB;
/* Don't force formatting by default */
PartCreateCtx.ForceFormat = FALSE;
/* Show the partitioning dialog */
ret = DialogBoxParamW(pSetupData->hInstance,
MAKEINTRESOURCEW(IDD_PARTITION),
hwndDlg,
PartitionDlgProc,
(LPARAM)&PartCreateCtx);
if (ret != IDOK)
break;
/*
* If the input size, given in MB, specifies the maximum partition
* size, it may slightly under- or over-estimate the latter due to
* rounding error. In this case, use all of the unpartitioned space.
* Otherwise, directly convert the size to bytes.
*/
PartSize = PartCreateCtx.PartSizeMB;
if (PartSize == MaxSizeMB)
PartSize = MaxPartSize;
else // if (PartSize < MaxSizeMB)
PartSize *= MB;
ASSERT(PartSize <= MaxPartSize);
if (!DoCreatePartition(hList, pSetupData->PartitionList,
&hItem, &PartItem,
PartSize,
!PartCreateCtx.MBRExtPart
? 0 : PARTITION_EXTENDED))
{
DisplayError(GetParent(hwndDlg),
IDS_ERROR_CREATE_PARTITION_TITLE,
IDS_ERROR_CREATE_PARTITION);
}
break;
}
case IDC_PARTFORMAT:
{
HTLITEM hItem;
PPARTITEM PartItem;
PPARTENTRY PartEntry;
INT_PTR ret;
PARTCREATE_CTX PartCreateCtx = {0};
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
PartItem = GetSelectedPartition(hList, &hItem);
if (!PartItem)
{
/* If the button was clicked, an empty disk
* region should have been selected first */
ASSERT(FALSE);
break;
}
PartEntry = PartItem->PartEntry;
if (!PartEntry->Volume)
{
/* Don't format an unformattable partition */
ASSERT(FALSE);
break;
}
/* Show the formatting dialog */
PartCreateCtx.PartItem = PartItem;
ret = DialogBoxParamW(pSetupData->hInstance,
MAKEINTRESOURCEW(IDD_FORMAT),
hwndDlg,
FormatDlgProc,
(LPARAM)&PartCreateCtx);
DBG_UNREFERENCED_PARAMETER(ret);
break;
}
case IDC_PARTDELETE:
{
PPARTITEM PartItem;
PPARTENTRY PartEntry;
HTLITEM hItem;
UINT uIDWarnMsg;
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
PartItem = GetSelectedPartition(hList, &hItem);
if (!PartItem)
{
// If the button was clicked, a partition
// should have been selected first...
ASSERT(FALSE);
break;
}
PartEntry = PartItem->PartEntry;
if (!PartEntry->IsPartitioned)
{
/* Don't delete an unpartitioned disk region */
ASSERT(FALSE);
break;
}
/* Choose the correct warning message to display:
* MBR-extended (container) vs. standard partition */
if (PartEntry == PartEntry->DiskEntry->ExtendedPartition)
uIDWarnMsg = IDS_WARN_DELETE_MBR_EXTENDED_PARTITION;
else
uIDWarnMsg = IDS_WARN_DELETE_PARTITION;
/* If the user really wants to delete the partition... */
if (DisplayMessage(GetParent(hwndDlg),
MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING,
MAKEINTRESOURCEW(IDS_WARN_DELETE_PARTITION_TITLE),
MAKEINTRESOURCEW(uIDWarnMsg)) == IDYES)
{
/* ... make it so! */
if (!DoDeletePartition(hList, pSetupData->PartitionList,
&hItem, PartItem))
{
// TODO: Show error if partition couldn't be deleted?
}
}
break;
}
}
break;
}
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
// On Vista+ we can use TVN_ITEMCHANGED instead, with NMTVITEMCHANGE* pointer
if (lpnm->idFrom == IDC_PARTITION && lpnm->code == TVN_SELCHANGED)
{
LPNMTREEVIEW pnmv = (LPNMTREEVIEW)lParam;
// if (!(pnmv->uChanged & TVIF_STATE)) /* The state has changed */
if (!(pnmv->itemNew.mask & TVIF_STATE))
break;
/* The item has been (de)selected */
// if (pnmv->uNewState & TVIS_SELECTED)
if (pnmv->itemNew.state & TVIS_SELECTED)
{
HTLITEM hParentItem = TreeList_GetParent(lpnm->hwndFrom, pnmv->itemNew.hItem);
/* May or may not be a PPARTENTRY: this is a PPARTENTRY only when hParentItem != NULL */
if (!hParentItem)
{
/* Hard disk */
PDISKENTRY DiskEntry = (PDISKENTRY)pnmv->itemNew.lParam;
ASSERT(DiskEntry);
/* Show the "Initialize" disk button and hide and disable the others */
ShowDlgItem(hwndDlg, IDC_INITDISK, SW_SHOW);
#if 0 // FIXME: Init disk not implemented yet!
EnableDlgItem(hwndDlg, IDC_INITDISK,
DiskEntry->DiskStyle == PARTITION_STYLE_RAW);
#else
EnableDlgItem(hwndDlg, IDC_INITDISK, FALSE);
#endif
ShowDlgItem(hwndDlg, IDC_PARTCREATE, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, FALSE);
ShowDlgItem(hwndDlg, IDC_PARTFORMAT, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTFORMAT, FALSE);
ShowDlgItem(hwndDlg, IDC_PARTDELETE, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTDELETE, FALSE);
/* Disable the "Next" button */
goto DisableWizNext;
}
else
{
/* Partition or unpartitioned space */
PPARTITEM PartItem = (PPARTITEM)pnmv->itemNew.lParam;
PPARTENTRY PartEntry;
ASSERT(PartItem);
PartEntry = PartItem->PartEntry;
ASSERT(PartEntry);
/* Hide and disable the "Initialize" disk button */
ShowDlgItem(hwndDlg, IDC_INITDISK, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_INITDISK, FALSE);
if (!PartEntry->IsPartitioned)
{
/* Show and enable the "Create" partition button */
ShowDlgItem(hwndDlg, IDC_PARTCREATE, SW_SHOW);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, TRUE);
/* Hide and disable the "Format" button */
ShowDlgItem(hwndDlg, IDC_PARTFORMAT, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTFORMAT, FALSE);
}
else
{
/* Hide and disable the "Create" partition button */
ShowDlgItem(hwndDlg, IDC_PARTCREATE, SW_HIDE);
EnableDlgItem(hwndDlg, IDC_PARTCREATE, FALSE);
/* Show the "Format" button, but enable or disable it if a formattable volume is present */
ShowDlgItem(hwndDlg, IDC_PARTFORMAT, SW_SHOW);
EnableDlgItem(hwndDlg, IDC_PARTFORMAT, !!PartEntry->Volume);
}
/* Show the "Delete" partition button, but enable or disable it if the disk region is partitioned */
ShowDlgItem(hwndDlg, IDC_PARTDELETE, SW_SHOW);
EnableDlgItem(hwndDlg, IDC_PARTDELETE, PartEntry->IsPartitioned);
/*
* Enable the "Next" button if:
*
* 1. the selected disk region is partitioned:
* it can either have a volume attached (and be either
* formatted or ready to be formatted),
* or it's not yet formatted (the installer will prompt
* for formatting parameters).
*
* 2. Or, the selected disk region is not partitioned but
* can be partitioned according to the disk's partitioning
* scheme (the installer will auto-partition the region
* and prompt for formatting parameters).
*
* In all other cases, the "Next" button is disabled.
*/
// TODO: In the future: first test needs to be augmented with:
// (... && PartEntry->Volume->IsSimpleVolume)
if ((PartEntry->IsPartitioned && PartEntry->Volume) ||
(!PartEntry->IsPartitioned && (PartitionCreateChecks(PartEntry, 0ULL, 0) == NOT_AN_ERROR)))
{
// ASSERT(PartEntry != PartEntry->DiskEntry->ExtendedPartition);
ASSERT(!IsContainerPartition(PartEntry->PartitionType));
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT);
}
else
{
goto DisableWizNext;
}
}
}
else
{
DisableWizNext:
/* Keep the "Next" button disabled. It will be enabled
* only when the user selects a valid partition. */
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
}
break;
}
if (lpnm->idFrom == IDC_PARTITION && lpnm->code == TVN_DELETEITEM)
{
/* Deleting an item from the partition list */
LPNMTREEVIEW pnmv = (LPNMTREEVIEW)lParam;
DeleteTreeItem(lpnm->hwndFrom, (TLITEMW*)&pnmv->itemOld);
break;
}
switch (lpnm->code)
{
case PSN_SETACTIVE:
{
/* Keep the "Next" button disabled. It will be enabled
* only when the user selects a valid partition. */
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK);
break;
}
case PSN_QUERYINITIALFOCUS:
{
/* Give the focus on and select the first item */
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
// TreeList_SetFocusItem(hList, 1, 1);
TreeList_SelectItem(hList, 1);
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 */
{
HTLITEM hItem;
PPARTITEM PartItem;
PPARTENTRY PartEntry;
hList = GetDlgItem(hwndDlg, IDC_PARTITION);
PartItem = GetSelectedPartition(hList, &hItem);
if (!PartItem)
{
/* Fail and don't continue the installation */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, -1);
return TRUE;
}
PartEntry = PartItem->PartEntry;
ASSERT(PartEntry);
/*
* Check whether the user wants to install ReactOS on a disk that
* is not recognized by the computer's firmware and if so, display
* a warning since such disks may not be bootable.
*/
if (PartEntry->DiskEntry->MediaType == FixedMedia &&
!PartEntry->DiskEntry->BiosFound)
{
INT nRet;
nRet = DisplayMessage(hwndDlg,
MB_OKCANCEL | MB_ICONWARNING,
L"Warning",
L"The disk you have selected for installing ReactOS\n"
L"is not visible by the firmware of your computer,\n"
L"and so may not be bootable.\n"
L"\nClick on OK to continue anyway."
L"\nClick on CANCEL to go back to the partitions list.");
if (nRet != IDOK)
{
/* Fail and don't continue the installation */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, -1);
return TRUE;
}
}
/* If this is an empty region, auto-create the partition if conditions are OK */
if (!PartEntry->IsPartitioned)
{
ULONG Error;
Error = PartitionCreateChecks(PartEntry, 0ULL, 0);
if (Error != NOT_AN_ERROR)
{
// MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
DisplayMessage(hwndDlg, MB_OK | MB_ICONERROR, NULL,
L"Could not create a partition on the selected disk region");
/* Fail and don't continue the installation */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, -1);
return TRUE;
}
/* Automatically create the partition on the whole empty space;
* it will be formatted later with default parameters */
if (!DoCreatePartition(hList, pSetupData->PartitionList,
&hItem, &PartItem,
0ULL,
0))
{
DisplayError(GetParent(hwndDlg),
IDS_ERROR_CREATE_PARTITION_TITLE,
IDS_ERROR_CREATE_PARTITION);
/* Fail and don't continue the installation */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, -1);
return TRUE;
}
/* Update PartEntry */
PartEntry = PartItem->PartEntry;
}
ASSERT(PartEntry->IsPartitioned);
// ASSERT(PartEntry != PartEntry->DiskEntry->ExtendedPartition);
ASSERT(!IsContainerPartition(PartEntry->PartitionType));
ASSERT(PartEntry->Volume);
#if 0 // TODO: Implement!
if (!IsPartitionLargeEnough(PartEntry))
{
MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
USetupData.RequiredPartitionDiskSpace);
/* Fail and don't continue the installation */
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, -1);
return TRUE;
}
#endif
/* Force formatting only if the partition doesn't have a volume (may or may not be formatted) */
if (PartEntry->Volume &&
((PartEntry->Volume->FormatState == Formatted) ||
(PartItem->VolCreate && *PartItem->VolCreate->FileSystemName)))
{
/*NOTHING*/;
}
else /* Request formatting of the selected region if it's not already formatted */
{
INT_PTR ret;
PARTCREATE_CTX PartCreateCtx = {0};
/* Show the formatting dialog */
PartCreateCtx.PartItem = PartItem;
ret = DialogBoxParamW(pSetupData->hInstance,
MAKEINTRESOURCEW(IDD_FORMAT),
hwndDlg,
FormatDlgProc,
(LPARAM)&PartCreateCtx);
/* If the user refuses to format the partition,
* fail and don't continue the installation */
if (ret != IDOK)
{
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, -1);
return TRUE;
}
/* The partition will be formatted */
}
InstallPartition = PartEntry;
break;
}
default:
break;
}
break;
}
default:
break;
}
return FALSE;
}
/* EOF */