1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

[ui] improve progress report when disabling Windows 11 installation restrictions

* Also fix Image Options content being lost when switching language.
This commit is contained in:
Pete Batard 2021-10-09 16:15:21 +01:00
parent b043db33e6
commit 957ec183c9
No known key found for this signature in database
GPG key ID: 38E0CF5E69EDD671
11 changed files with 221 additions and 133 deletions

View file

@ -3,9 +3,9 @@ o Version 3.16 (2021.10.??)
Fix BIOS boot support for Arch derivatives Fix BIOS boot support for Arch derivatives
Fix removal of some boot entries for Ubuntu derivatives Fix removal of some boot entries for Ubuntu derivatives
Fix log not being saved on exit Fix log not being saved on exit
Add Windows 11 "Extended" installation support (Disables TPM/Secure Boot/RAM requirements)
Add UEFI Shell ISO downloads Add UEFI Shell ISO downloads
Add support for Intel NUC card readers Add support for Intel NUC card readers
Add Windows 11 extended installation support (disables TPM or Secure Boot requirement)
Improve Windows 11 support Improve Windows 11 support
Improve Windows version reporting Improve Windows version reporting
Speed up clearing of MBR/GPT Speed up clearing of MBR/GPT

View file

@ -11,7 +11,7 @@
<Identity <Identity
Name="19453.net.Rufus" Name="19453.net.Rufus"
Publisher="CN=7AC86D13-3E5A-491A-ADD5-80095C212740" Publisher="CN=7AC86D13-3E5A-491A-ADD5-80095C212740"
Version="3.16.1832.0" /> Version="3.16.1833.0" />
<Properties> <Properties>
<DisplayName>Rufus</DisplayName> <DisplayName>Rufus</DisplayName>

View file

@ -5,6 +5,12 @@ To edit a translation, please make sure to follow:
https://github.com/pbatard/rufus/wiki/Localization#Editing_an_existing_translation https://github.com/pbatard/rufus/wiki/Localization#Editing_an_existing_translation
Or simply download https://rufus-web.akeo.ie/locale/pollock.exe and follow its directions. Or simply download https://rufus-web.akeo.ie/locale/pollock.exe and follow its directions.
o v3.?? (????.??.??)
- *UPDATED* MSG_305 Reworked the message to mention the new Windows 11 Extended option
- *NEW* MSG_322 "Standard Windows 11 Installation (TPM 2.0, Secure Boot, 8GB+ RAM)"
- *NEW* MSG_323 "Extended Windows 11 Installation (no TPM/no Secure Boot/8GB- RAM)"
- *NEW* MSG_324 "Removing Windows 11 installation restrictions: %s"
o v3.14 (2021.03.31) o v3.14 (2021.03.31)
- *UPDATED* MSG_068 "Error while partitioning drive." -> "Could not partition drive." - *UPDATED* MSG_068 "Error while partitioning drive." -> "Could not partition drive."
- *UPDATED* MSG_274 "IsoHybrid image detected" -> "%s image detected" - *UPDATED* MSG_274 "IsoHybrid image detected" -> "%s image detected"

View file

@ -559,9 +559,10 @@ t MSG_301 "Show application settings"
t MSG_302 "Show information about this application" t MSG_302 "Show information about this application"
t MSG_303 "Show the log" t MSG_303 "Show the log"
t MSG_304 "Create a disk image of the selected device" t MSG_304 "Create a disk image of the selected device"
t MSG_305 "Use this option to indicate if you want to run Windows directly from this drive (Windows To Go) or if you " t MSG_305 "Use this option to indicate if you plan to install Windows to a different disk, or if you want to run Windows "
"want to install Windows to a different disk.\r\nIn 'Extended Windows 11 Installation' mode, Rufus patches the " "directly from this drive (Windows To Go).\r\nIn 'Extended Windows 11 Installation' mode, Rufus will patch the "
"media so that Windows 11 can be installed on platforms that don't meet the TPM 2.0 or Secure Boot requirements." "media so that Windows 11 can be installed on platforms that don't meet the TPM 2.0, Secure Boot or minimum RAM "
"requirements."
# You can see this status message by pressing <Ctrl>-<Alt>-<Z> and then selecting START. # You can see this status message by pressing <Ctrl>-<Alt>-<Z> and then selecting START.
# It's the same as MSG_286 but with a process that *may* be faster, hence the name. # It's the same as MSG_286 but with a process that *may* be faster, hence the name.
t MSG_306 "Fast-zeroing drive: %s" t MSG_306 "Fast-zeroing drive: %s"
@ -583,9 +584,9 @@ t MSG_319 "Ignore Boot Marker"
t MSG_320 "Refreshing partition layout (%s)..." t MSG_320 "Refreshing partition layout (%s)..."
t MSG_321 "The image you have selected is an ISOHybrid, but its creators have not made it compatible with ISO/File " t MSG_321 "The image you have selected is an ISOHybrid, but its creators have not made it compatible with ISO/File "
"copy mode.\nAs a result, DD image writing mode will be enforced." "copy mode.\nAs a result, DD image writing mode will be enforced."
t MSG_322 "Standard Windows 11 Installation (TPM 2.0 / Secure Boot / 8GB+ RAM)" t MSG_322 "Standard Windows 11 Installation (TPM 2.0, Secure Boot, 8GB+ RAM)"
t MSG_323 "Extended Windows 11 Installation (no TPM/no Secure Boot/8GB- RAM)" t MSG_323 "Extended Windows 11 Installation (no TPM/no Secure Boot/8GB- RAM)"
t MSG_324 "Removing Windows 11 installation restrictions (%s)" t MSG_324 "Removing Windows 11 installation restrictions: %s"
######################################################################### #########################################################################
l "ar-SA" "Arabic (العربية)" 0x0401, 0x0801, 0x0c01, 0x1001, 0x1401, 0x1801, 0x1c01, 0x2001, 0x2401, 0x2801, 0x2c01, 0x3001, 0x3401, 0x3801, 0x3c01, 0x4001 l "ar-SA" "Arabic (العربية)" 0x0401, 0x0801, 0x0c01, 0x1001, 0x1401, 0x1801, 0x1c01, 0x2001, 0x2401, 0x2801, 0x2c01, 0x3001, 0x3401, 0x3801, 0x3c01, 0x4001

View file

@ -1452,7 +1452,7 @@ BOOL RemoveWindows11Restrictions(char drive_letter)
boot_wim_path[0] = drive_letter; boot_wim_path[0] = drive_letter;
PrintInfo(0, MSG_324, lmprintf(MSG_307)); UpdateProgressWithInfoForce(OP_PATCH, MSG_324, 0, PATCH_PROGRESS_TOTAL);
uprintf("Mounting '%s'...", boot_wim_path); uprintf("Mounting '%s'...", boot_wim_path);
mount_path = WimMountImage(boot_wim_path, wim_index); mount_path = WimMountImage(boot_wim_path, wim_index);
@ -1462,6 +1462,7 @@ BOOL RemoveWindows11Restrictions(char drive_letter)
static_sprintf(path, "%s\\Windows\\System32\\config\\SYSTEM", mount_path); static_sprintf(path, "%s\\Windows\\System32\\config\\SYSTEM", mount_path);
if (!MountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name, path)) if (!MountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name, path))
goto out; goto out;
UpdateProgressWithInfoForce(OP_PATCH, MSG_324, 102, PATCH_PROGRESS_TOTAL);
is_hive_mounted = TRUE; is_hive_mounted = TRUE;
static_sprintf(key_path, "%s\\Setup", offline_hive_name); static_sprintf(key_path, "%s\\Setup", offline_hive_name);
@ -1490,6 +1491,7 @@ BOOL RemoveWindows11Restrictions(char drive_letter)
} }
uprintf("Created 'HKLM\\SYSTEM\\Setup\\LabConfig\\%s' registry key", key_name[i]); uprintf("Created 'HKLM\\SYSTEM\\Setup\\LabConfig\\%s' registry key", key_name[i]);
} }
UpdateProgressWithInfoForce(OP_PATCH, MSG_324, 103, PATCH_PROGRESS_TOTAL);
r = TRUE; r = TRUE;
out: out:
@ -1497,12 +1499,15 @@ out:
RegCloseKey(hSubKey); RegCloseKey(hSubKey);
if (hKey != NULL) if (hKey != NULL)
RegCloseKey(hKey); RegCloseKey(hKey);
if (is_hive_mounted) if (is_hive_mounted) {
UnmountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name); UnmountRegistryHive(HKEY_LOCAL_MACHINE, offline_hive_name);
UpdateProgressWithInfoForce(OP_PATCH, MSG_324, 104, PATCH_PROGRESS_TOTAL);
}
if (mount_path) { if (mount_path) {
uprintf("Unmounting '%s'...", boot_wim_path, wim_index); uprintf("Unmounting '%s'...", boot_wim_path, wim_index);
WimUnmountImage(boot_wim_path, wim_index); WimUnmountImage(boot_wim_path, wim_index);
} }
UpdateProgressWithInfo(OP_PATCH, MSG_324, PATCH_PROGRESS_TOTAL, PATCH_PROGRESS_TOTAL);
free(mount_path); free(mount_path);
return r; return r;
} }

View file

@ -2707,6 +2707,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
InitDialog(hDlg); InitDialog(hDlg);
GetDevices(0); GetDevices(0);
EnableControls(TRUE, FALSE); EnableControls(TRUE, FALSE);
UpdateImage(FALSE);
// The AppStore version does not need the internal check for updates // The AppStore version does not need the internal check for updates
if (!appstore_version) if (!appstore_version)
CheckForUpdates(FALSE); CheckForUpdates(FALSE);

View file

@ -69,6 +69,7 @@
#define MAX_SIZE_SUFFIXES 6 // bytes, KB, MB, GB, TB, PB #define MAX_SIZE_SUFFIXES 6 // bytes, KB, MB, GB, TB, PB
#define MAX_CLUSTER_SIZES 18 #define MAX_CLUSTER_SIZES 18
#define MAX_PROGRESS 0xFFFF #define MAX_PROGRESS 0xFFFF
#define PATCH_PROGRESS_TOTAL 207
#define MAX_LOG_SIZE 0x7FFFFFFE #define MAX_LOG_SIZE 0x7FFFFFFE
#define MAX_REFRESH 25 // How long we should wait to refresh UI elements (in ms) #define MAX_REFRESH 25 // How long we should wait to refresh UI elements (in ms)
#define MAX_GUID_STRING_LENGTH 40 #define MAX_GUID_STRING_LENGTH 40
@ -251,6 +252,7 @@ enum action_type {
OP_CREATE_FS, OP_CREATE_FS,
OP_FIX_MBR, OP_FIX_MBR,
OP_FILE_COPY, OP_FILE_COPY,
OP_PATCH,
OP_FINALIZE, OP_FINALIZE,
OP_MAX OP_MAX
}; };
@ -516,7 +518,9 @@ extern void PrintStatusInfo(BOOL info, BOOL debug, unsigned int duration, int ms
#define PrintInfo(...) PrintStatusInfo(TRUE, FALSE, __VA_ARGS__) #define PrintInfo(...) PrintStatusInfo(TRUE, FALSE, __VA_ARGS__)
#define PrintInfoDebug(...) PrintStatusInfo(TRUE, TRUE, __VA_ARGS__) #define PrintInfoDebug(...) PrintStatusInfo(TRUE, TRUE, __VA_ARGS__)
extern void UpdateProgress(int op, float percent); extern void UpdateProgress(int op, float percent);
extern void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total); extern void _UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total, BOOL force);
#define UpdateProgressWithInfo(op, msg, processed, total) _UpdateProgressWithInfo(op, msg, processed, total, FALSE)
#define UpdateProgressWithInfoForce(op, msg, processed, total) _UpdateProgressWithInfo(op, msg, processed, total, TRUE)
#define UpdateProgressWithInfoInit(hProgressDialog, bNoAltMode) UpdateProgressWithInfo(OP_INIT, (int)bNoAltMode, (uint64_t)(uintptr_t)hProgressDialog, 0); #define UpdateProgressWithInfoInit(hProgressDialog, bNoAltMode) UpdateProgressWithInfo(OP_INIT, (int)bNoAltMode, (uint64_t)(uintptr_t)hProgressDialog, 0);
extern const char* StrError(DWORD error_code, BOOL use_default_locale); extern const char* StrError(DWORD error_code, BOOL use_default_locale);
extern char* GuidToString(const GUID* guid); extern char* GuidToString(const GUID* guid);

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326 IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 3.16.1832" CAPTION "Rufus 3.16.1833"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0 FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -395,8 +395,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,16,1832,0 FILEVERSION 3,16,1833,0
PRODUCTVERSION 3,16,1832,0 PRODUCTVERSION 3,16,1833,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -414,13 +414,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie" VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting" VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "3.16.1832" VALUE "FileVersion", "3.16.1833"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-3.16.exe" VALUE "OriginalFilename", "rufus-3.16.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "3.16.1832" VALUE "ProductVersion", "3.16.1833"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -37,6 +37,8 @@
#include "msapi_utf8.h" #include "msapi_utf8.h"
#include "localization.h" #include "localization.h"
#define FACILITY_WIM 322
/* /*
* Globals * Globals
*/ */
@ -627,6 +629,16 @@ static const char *GetVdsError(DWORD error_code)
} }
} }
static const char* GetVimError(DWORD error_code)
{
switch (error_code) {
case 0xC1420127:
return "The specified image in the specified wim is already mounted for read and write access.";
default:
return NULL;
}
}
// Convert a windows error to human readable string // Convert a windows error to human readable string
const char *WindowsErrorString(void) const char *WindowsErrorString(void)
{ {
@ -637,11 +649,14 @@ const char *WindowsErrorString(void)
error_code = GetLastError(); error_code = GetLastError();
// Check for VDS error codes // Check for VDS error codes
if ((SCODE_FACILITY(error_code) == FACILITY_ITF) && (GetVdsError(error_code) != NULL)) { if ((HRESULT_FACILITY(error_code) == FACILITY_ITF) && (GetVdsError(error_code) != NULL)) {
static_sprintf(err_string, "[0x%08lX] %s", error_code, GetVdsError(error_code)); static_sprintf(err_string, "[0x%08lX] %s", error_code, GetVdsError(error_code));
return err_string; return err_string;
} }
if ((HRESULT_FACILITY(error_code) == FACILITY_WIM) && (GetVimError(error_code) != NULL)) {
static_sprintf(err_string, "[0x%08lX] %s", error_code, GetVimError(error_code));
return err_string;
}
static_sprintf(err_string, "[0x%08lX] ", error_code); static_sprintf(err_string, "[0x%08lX] ", error_code);
presize = (DWORD)strlen(err_string); presize = (DWORD)strlen(err_string);

View file

@ -1181,6 +1181,8 @@ void InitProgress(BOOL bOnlyFormat)
break; break;
case BT_IMAGE: case BT_IMAGE:
nb_slots[OP_FILE_COPY] = (img_report.is_iso || img_report.is_windows_img) ? -1 : 0; nb_slots[OP_FILE_COPY] = (img_report.is_iso || img_report.is_windows_img) ? -1 : 0;
if (HAS_WINDOWS(img_report) && ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_EXTENDED)
nb_slots[OP_PATCH] = -1;
break; break;
default: default:
nb_slots[OP_FILE_COPY] = 2 + 1; nb_slots[OP_FILE_COPY] = 2 + 1;
@ -1387,7 +1389,7 @@ static void bar_update(struct bar_progress* bp, uint64_t howmuch, uint64_t dltim
// display percentage completed, rate of transfer and estimated remaining duration. // display percentage completed, rate of transfer and estimated remaining duration.
// During init (op = OP_INIT) an optional HWND can be passed on which to look for // During init (op = OP_INIT) an optional HWND can be passed on which to look for
// a progress bar. Part of the code (eta, speed) comes from GNU wget. // a progress bar. Part of the code (eta, speed) comes from GNU wget.
void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total) void _UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total, BOOL force)
{ {
static int last_update_progress_type = UPT_PERCENT; static int last_update_progress_type = UPT_PERCENT;
static struct bar_progress bp = { 0 }; static struct bar_progress bp = { 0 };
@ -1486,7 +1488,7 @@ void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total)
static_sprintf(msg_data, "%0.1f%%", percent); static_sprintf(msg_data, "%0.1f%%", percent);
break; break;
} }
if ((bp.count == bp.total_length) || (current_time > last_refresh + MAX_REFRESH)) { if ((force) || (bp.count == bp.total_length) || (current_time > last_refresh + MAX_REFRESH)) {
if (op < 0) { if (op < 0) {
SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS * percent / 100.0f), 0); SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS * percent / 100.0f), 0);
if (op == OP_NOOP_WITH_TASKBAR) if (op == OP_NOOP_WITH_TASKBAR)
@ -1494,8 +1496,8 @@ void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total)
} else { } else {
UpdateProgress(op, (float)percent); UpdateProgress(op, (float)percent);
} }
if ((msg >= 0) && ((current_time > bp.last_screen_update + SCREEN_REFRESH_INTERVAL) || if ((force) || ((msg >= 0) && ((current_time > bp.last_screen_update + SCREEN_REFRESH_INTERVAL) ||
(last_update_progress_type != update_progress_type) || (bp.count == bp.total_length))) { (last_update_progress_type != update_progress_type) || (bp.count == bp.total_length)))) {
PrintInfo(0, msg, msg_data); PrintInfo(0, msg, msg_data);
bp.last_screen_update = current_time; bp.last_screen_update = current_time;
} }

276
src/vhd.c
View file

@ -57,6 +57,47 @@
#define SECONDS_SINCE_JAN_1ST_2000 946684800 #define SECONDS_SINCE_JAN_1ST_2000 946684800
#define INVALID_CALLBACK_VALUE 0xFFFFFFFF
#define WIM_FLAG_RESERVED 0x00000001
#define WIM_FLAG_VERIFY 0x00000002
#define WIM_FLAG_INDEX 0x00000004
#define WIM_FLAG_NO_APPLY 0x00000008
#define WIM_FLAG_NO_DIRACL 0x00000010
#define WIM_FLAG_NO_FILEACL 0x00000020
#define WIM_FLAG_SHARE_WRITE 0x00000040
#define WIM_FLAG_FILEINFO 0x00000080
#define WIM_FLAG_NO_RP_FIX 0x00000100
// Bitmask for the kind of progress we want to report in the WIM progress callback
#define WIM_REPORT_PROGRESS 0x00000001
#define WIM_REPORT_PROCESS 0x00000002
#define WIM_REPORT_FILEINFO 0x00000004
// From https://docs.microsoft.com/en-us/previous-versions/msdn10/dd834960(v=msdn.10)
// as well as https://msfn.org/board/topic/150700-wimgapi-wimmountimage-progressbar/
enum WIMMessage {
WIM_MSG = WM_APP + 0x1476,
WIM_MSG_TEXT,
WIM_MSG_PROGRESS, // Indicates an update in the progress of an image application.
WIM_MSG_PROCESS, // Enables the caller to prevent a file or a directory from being captured or applied.
WIM_MSG_SCANNING, // Indicates that volume information is being gathered during an image capture.
WIM_MSG_SETRANGE, // Indicates the number of files that will be captured or applied.
WIM_MSG_SETPOS, // Indicates the number of files that have been captured or applied.
WIM_MSG_STEPIT, // Indicates that a file has been either captured or applied.
WIM_MSG_COMPRESS, // Enables the caller to prevent a file resource from being compressed during a capture.
WIM_MSG_ERROR, // Alerts the caller that an error has occurred while capturing or applying an image.
WIM_MSG_ALIGNMENT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_RETRY, // Sent when the file is being reapplied because of a network timeout.
WIM_MSG_SPLIT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_FILEINFO, // Used in conjunction with WimApplyImages()'s WIM_FLAG_FILEINFO flag to provide detailed file info.
WIM_MSG_INFO, // Sent when an info message is available.
WIM_MSG_WARNING, // Sent when a warning message is available.
WIM_MSG_CHK_PROCESS,
WIM_MSG_SUCCESS = 0,
WIM_MSG_ABORT_IMAGE = -1
};
/* /*
* VHD Fixed HD footer (Big Endian) * VHD Fixed HD footer (Big Endian)
* http://download.microsoft.com/download/f/f/e/ffef50a5-07dd-4cf8-aaa3-442c0673a029/Virtual%20Hard%20Disk%20Format%20Spec_10_18_06.doc * http://download.microsoft.com/download/f/f/e/ffef50a5-07dd-4cf8-aaa3-442c0673a029/Virtual%20Hard%20Disk%20Format%20Spec_10_18_06.doc
@ -114,13 +155,15 @@ extern int default_thread_priority;
extern BOOL ignore_boot_marker; extern BOOL ignore_boot_marker;
static uint8_t wim_flags = 0; static uint8_t wim_flags = 0;
static uint32_t progress_report_mask;
static uint64_t progress_offset = 0, progress_total = 100;
static wchar_t wmount_path[MAX_PATH] = { 0 }, wmount_track[MAX_PATH] = { 0 }; static wchar_t wmount_path[MAX_PATH] = { 0 }, wmount_track[MAX_PATH] = { 0 };
static char sevenzip_path[MAX_PATH]; static char sevenzip_path[MAX_PATH];
static const char conectix_str[] = VHD_FOOTER_COOKIE; static const char conectix_str[] = VHD_FOOTER_COOKIE;
static BOOL count_files; static BOOL count_files, use_msg_progress = FALSE;
// Apply/Mount image functionality // Apply/Mount image functionality
static const char *_image, *_dst; static const char *_image, *_dst;
static int _index; static int _index, progress_op = OP_FILE_COPY, progress_msg = MSG_267;
static BOOL Get7ZipPath(void) static BOOL Get7ZipPath(void)
{ {
@ -355,6 +398,79 @@ out:
return is_bootable_img; return is_bootable_img;
} }
// WIM operations progress callback
DWORD WINAPI WimProgressCallback(DWORD dwMsgId, WPARAM wParam, LPARAM lParam, PVOID pvIgnored)
{
PBOOL pbCancel = NULL;
PWIN32_FIND_DATA pFileData;
const char* level = NULL;
uint64_t size;
switch (dwMsgId) {
case WIM_MSG_PROGRESS:
// The default WIM progress is useless for apply (freezes at 95%, which is usually when
// only half the files have been processed), so we only use it for mounting/unmounting.
if (!(progress_report_mask & WIM_REPORT_PROGRESS))
break;
UpdateProgressWithInfo(progress_op, progress_msg, progress_offset + wParam, progress_total);
break;
case WIM_MSG_PROCESS:
if (!(progress_report_mask & WIM_REPORT_PROCESS))
break;
// The amount of files processed is overwhelming (16k+ for a typical image),
// and trying to display it *WILL* slow us down, so we don't.
#if 0
uprintf("%S", (PWSTR)wParam);
PrintStatus(0, MSG_000, str); // MSG_000 is "%s"
#endif
if (count_files) {
wim_nb_files++;
} else {
// At the end of an actual apply, the WIM API re-lists a bunch of directories it already processed,
// so, even as we try to compensate, we might end up with more entries than counted - ignore those.
if (wim_proc_files < wim_nb_files)
wim_proc_files++;
else
wim_extra_files++;
UpdateProgressWithInfo(progress_op, progress_msg, wim_proc_files, wim_nb_files);
}
// Halt on error
if (IS_ERROR(FormatStatus)) {
pbCancel = (PBOOL)lParam;
*pbCancel = TRUE;
break;
}
break;
case WIM_MSG_FILEINFO:
if (!(progress_report_mask & WIM_REPORT_FILEINFO))
break;
pFileData = (PWIN32_FIND_DATA)lParam;
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
uprintf("Creating: %S", (PWSTR)wParam);
} else {
size = (((uint64_t)pFileData->nFileSizeHigh) << 32) + pFileData->nFileSizeLow;
uprintf("Extracting: %S (%s)", (PWSTR)wParam, SizeToHumanReadable(size, FALSE, FALSE));
}
break;
case WIM_MSG_RETRY:
level = "retry";
// fall through
case WIM_MSG_INFO:
if (level == NULL) level = "info";
// fall through
case WIM_MSG_WARNING:
if (level == NULL) level = "warning";
// fall through
case WIM_MSG_ERROR:
if (level == NULL) level = "error";
SetLastError((DWORD)lParam);
uprintf("WIM processing %s: %S [err = %d]\n", level, (PWSTR)wParam, WindowsErrorString());
break;
}
return IS_ERROR(FormatStatus) ? WIM_MSG_ABORT_IMAGE : WIM_MSG_SUCCESS;
}
// Find out if we have any way to extract/apply WIM files on this platform // Find out if we have any way to extract/apply WIM files on this platform
// Returns a bitfield of the methods we can use (1 = Extract using wimgapi, 2 = Extract using 7-Zip, 4 = Apply using wimgapi) // Returns a bitfield of the methods we can use (1 = Extract using wimgapi, 2 = Extract using 7-Zip, 4 = Apply using wimgapi)
uint8_t WimExtractCheck(BOOL bSilent) uint8_t WimExtractCheck(BOOL bSilent)
@ -384,15 +500,26 @@ uint8_t WimExtractCheck(BOOL bSilent)
return wim_flags; return wim_flags;
} }
//
// Looks like Microsoft's idea of "mount" for WIM images involves the creation // Looks like Microsoft's idea of "mount" for WIM images involves the creation
// of a as many virtual junctions as there exist directories on the image... // of a as many virtual junctions as there exist directories on the image...
// So, yeah, this is both very slow and wasteful of space. // So, yeah, this is both very slow and wasteful of space.
//
// NB: You can see mounted WIMs, along with their mountpoint, by checking:
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WIMMount\Mounted Images\
// You can also mount/unmount images from an elevated prompt with something like:
// dism /mount-image [/readonly] /imagefile:F:\sources\boot.wim /index:2 /mountdir:C:\test\offline
// dism /unmount-image /discard /mountdir:C:\test\offline
//
static DWORD WINAPI WimMountImageThread(LPVOID param) static DWORD WINAPI WimMountImageThread(LPVOID param)
{ {
BOOL r = FALSE; BOOL r = FALSE;
wconvert(temp_dir); wconvert(temp_dir);
wchar_t* wimage = utf8_to_wchar(_image); wchar_t* wimage = utf8_to_wchar(_image);
PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMMountImage, Wimgapi); PF_INIT_OR_OUT(WIMMountImage, Wimgapi);
PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
if (wmount_path[0] != 0) { if (wmount_path[0] != 0) {
uprintf("WimMountImage: An image is already mounted"); uprintf("WimMountImage: An image is already mounted");
@ -417,7 +544,18 @@ static DWORD WINAPI WimMountImageThread(LPVOID param)
goto out; goto out;
} }
progress_report_mask = WIM_REPORT_PROGRESS;
progress_op = OP_PATCH;
progress_msg = MSG_324;
progress_offset = 1;
progress_total = PATCH_PROGRESS_TOTAL;
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
uprintf("WimMountImage: Could not set progress callback: %s", WindowsErrorString());
goto out;
}
r = pfWIMMountImage(wmount_path, wimage, _index, wmount_track); r = pfWIMMountImage(wmount_path, wimage, _index, wmount_track);
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
if (!r) { if (!r) {
uprintf("Could not mount '%S [%d]' on '%S': %s", wimage, _index, wmount_path, WindowsErrorString()); uprintf("Could not mount '%S [%d]' on '%S': %s", wimage, _index, wmount_path, WindowsErrorString());
goto out; goto out;
@ -455,22 +593,39 @@ static DWORD WINAPI WimUnmountImageThread(LPVOID param)
{ {
BOOL r = FALSE; BOOL r = FALSE;
wchar_t* wimage = utf8_to_wchar(_image); wchar_t* wimage = utf8_to_wchar(_image);
PF_INIT_OR_OUT(WIMRegisterMessageCallback, Wimgapi);
PF_INIT_OR_OUT(WIMUnmountImage, Wimgapi); PF_INIT_OR_OUT(WIMUnmountImage, Wimgapi);
PF_INIT_OR_OUT(WIMUnregisterMessageCallback, Wimgapi);
if (wmount_path[0] == 0) { if (wmount_path[0] == 0) {
uprintf("WimUnmountImage: No image is mounted"); uprintf("WimUnmountImage: No image is mounted");
goto out; goto out;
} }
progress_report_mask = WIM_REPORT_PROGRESS;
progress_op = OP_PATCH;
progress_msg = MSG_324;
progress_offset = 105;
progress_total = PATCH_PROGRESS_TOTAL;
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
uprintf("WimUnmountImage: Could not set progress callback: %s", WindowsErrorString());
goto out;
}
r = pfWIMUnmountImage(wmount_path, wimage, _index, TRUE); r = pfWIMUnmountImage(wmount_path, wimage, _index, TRUE);
pfWIMUnregisterMessageCallback(NULL, (FARPROC)WimProgressCallback);
if (!r) { if (!r) {
uprintf("Could not unmount '%S': %s", wmount_path, WindowsErrorString()); uprintf("Could not unmount '%S': %s", wmount_path, WindowsErrorString());
goto out; goto out;
} }
uprintf("Unmounted '%S [%d]'", wmount_path, _index); uprintf("Unmounted '%S [%d]'", wmount_path, _index);
RemoveDirectoryW(wmount_path); if (!RemoveDirectoryW(wmount_track))
wmount_path[0] = 0; uprintf("Could not delete '%S' : %s", wmount_track, WindowsErrorString());
RemoveDirectoryW(wmount_track);
wmount_track[0] = 0; wmount_track[0] = 0;
if (!RemoveDirectoryW(wmount_path))
uprintf("Could not delete '%S' : %s", wmount_path, WindowsErrorString());
wmount_path[0] = 0;
out: out:
safe_free(wimage); safe_free(wimage);
ExitThread((DWORD)r); ExitThread((DWORD)r);
@ -652,112 +807,6 @@ BOOL WimExtractFile(const char* image, int index, const char* src, const char* d
|| ((wim_flags & WIM_HAS_API_EXTRACT) && WimExtractFile_API(image, index, src, dst, bSilent)) ); || ((wim_flags & WIM_HAS_API_EXTRACT) && WimExtractFile_API(image, index, src, dst, bSilent)) );
} }
// From https://docs.microsoft.com/en-us/previous-versions/msdn10/dd834960(v=msdn.10)
// as well as https://msfn.org/board/topic/150700-wimgapi-wimmountimage-progressbar/
enum WIMMessage {
WIM_MSG = WM_APP + 0x1476,
WIM_MSG_TEXT,
WIM_MSG_PROGRESS, // Indicates an update in the progress of an image application.
WIM_MSG_PROCESS, // Enables the caller to prevent a file or a directory from being captured or applied.
WIM_MSG_SCANNING, // Indicates that volume information is being gathered during an image capture.
WIM_MSG_SETRANGE, // Indicates the number of files that will be captured or applied.
WIM_MSG_SETPOS, // Indicates the number of files that have been captured or applied.
WIM_MSG_STEPIT, // Indicates that a file has been either captured or applied.
WIM_MSG_COMPRESS, // Enables the caller to prevent a file resource from being compressed during a capture.
WIM_MSG_ERROR, // Alerts the caller that an error has occurred while capturing or applying an image.
WIM_MSG_ALIGNMENT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_RETRY, // Sent when the file is being reapplied because of a network timeout.
WIM_MSG_SPLIT, // Enables the caller to align a file resource on a particular alignment boundary.
WIM_MSG_FILEINFO, // Used in conjunction with WimApplyImages()'s WIM_FLAG_FILEINFO flag to provide detailed file info.
WIM_MSG_INFO, // Sent when an info message is available.
WIM_MSG_WARNING, // Sent when a warning message is available.
WIM_MSG_CHK_PROCESS,
WIM_MSG_SUCCESS = 0x00000000,
WIM_MSG_ABORT_IMAGE = -1
};
#define INVALID_CALLBACK_VALUE 0xFFFFFFFF
#define WIM_FLAG_RESERVED 0x00000001
#define WIM_FLAG_VERIFY 0x00000002
#define WIM_FLAG_INDEX 0x00000004
#define WIM_FLAG_NO_APPLY 0x00000008
#define WIM_FLAG_NO_DIRACL 0x00000010
#define WIM_FLAG_NO_FILEACL 0x00000020
#define WIM_FLAG_SHARE_WRITE 0x00000040
#define WIM_FLAG_FILEINFO 0x00000080
#define WIM_FLAG_NO_RP_FIX 0x00000100
// Progress callback
DWORD WINAPI WimProgressCallback(DWORD dwMsgId, WPARAM wParam, LPARAM lParam, PVOID pvIgnored)
{
PBOOL pbCancel = NULL;
PWIN32_FIND_DATA pFileData;
const char* level = NULL;
uint64_t size;
switch (dwMsgId) {
case WIM_MSG_PROGRESS:
// The default WIM progress is useless (freezes at 95%, which is usually when only half
// the files have been processed), so we don't use it
#if 0
PrintInfo(0, MSG_267, (DWORD)wParam);
UpdateProgress(OP_FILE_COPY, 0.98f*(DWORD)wParam);
#endif
break;
case WIM_MSG_PROCESS:
// The amount of files processed is overwhelming (16k+ for a typical image),
// and trying to display it *WILL* slow us down, so we don't.
#if 0
uprintf("%S", (PWSTR)wParam);
PrintStatus(0, MSG_000, str); // MSG_000 is "%s"
#endif
if (count_files) {
wim_nb_files++;
} else {
// At the end of an actual apply, the WIM API re-lists a bunch of directories it already processed,
// so, even as we try to compensate, we might end up with more entries than counted - ignore those.
if (wim_proc_files < wim_nb_files)
wim_proc_files++;
else
wim_extra_files++;
UpdateProgressWithInfo(OP_FILE_COPY, MSG_267, wim_proc_files, wim_nb_files);
}
// Halt on error
if (IS_ERROR(FormatStatus)) {
pbCancel = (PBOOL)lParam;
*pbCancel = TRUE;
break;
}
break;
case WIM_MSG_FILEINFO:
pFileData = (PWIN32_FIND_DATA)lParam;
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
uprintf("Creating: %S", (PWSTR)wParam);
} else {
size = (((uint64_t)pFileData->nFileSizeHigh) << 32) + pFileData->nFileSizeLow;
uprintf("Extracting: %S (%s)", (PWSTR)wParam, SizeToHumanReadable(size, FALSE, FALSE));
}
break;
case WIM_MSG_RETRY:
level = "retry";
// fall through
case WIM_MSG_INFO:
if (level == NULL) level = "info";
// fall through
case WIM_MSG_WARNING:
if (level == NULL) level = "warning";
// fall through
case WIM_MSG_ERROR:
if (level == NULL) level = "error";
SetLastError((DWORD)lParam);
uprintf("Apply %s: %S [err = %d]\n", level, (PWSTR)wParam, WindowsErrorString());
break;
}
return IS_ERROR(FormatStatus)?WIM_MSG_ABORT_IMAGE:WIM_MSG_SUCCESS;
}
// Apply a WIM image using wimgapi.dll (Windows 7 or later) // Apply a WIM image using wimgapi.dll (Windows 7 or later)
// https://docs.microsoft.com/en-us/previous-versions/msdn10/dd851944(v=msdn.10) // https://docs.microsoft.com/en-us/previous-versions/msdn10/dd851944(v=msdn.10)
// To get progress, we must run this call within its own thread // To get progress, we must run this call within its own thread
@ -780,6 +829,11 @@ static DWORD WINAPI WimApplyImageThread(LPVOID param)
uprintf("Opening: %s:[%d]", _image, _index); uprintf("Opening: %s:[%d]", _image, _index);
progress_report_mask = WIM_REPORT_PROCESS | WIM_REPORT_FILEINFO;
progress_op = OP_FILE_COPY;
progress_msg = MSG_267;
progress_offset = 0;
progress_total = 100;
if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) { if (pfWIMRegisterMessageCallback(NULL, (FARPROC)WimProgressCallback, NULL) == INVALID_CALLBACK_VALUE) {
uprintf(" Could not set progress callback: %s", WindowsErrorString()); uprintf(" Could not set progress callback: %s", WindowsErrorString());
goto out; goto out;