/* * Rufus: The Reliable USB Formatting Utility * Copyright © 2011-2024 Pete Batard * * 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 3 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, see . */ /* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */ #ifdef _CRTDBG_MAP_ALLOC #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rufus.h" #include "format.h" #include "missing.h" #include "resource.h" #include "msapi_utf8.h" #include "localization.h" #include "ui.h" #include "re.h" #include "cpu.h" #include "vhd.h" #include "wue.h" #include "drive.h" #include "settings.h" #include "bled/bled.h" #include "cdio/logging.h" #include "../res/grub/grub_version.h" #include "../res/grub2/grub2_version.h" enum bootcheck_return { BOOTCHECK_PROCEED = 0, BOOTCHECK_CANCEL = -1, BOOTCHECK_DOWNLOAD_ERROR = -2, BOOTCHECK_GENERAL_ERROR = -3, }; static const char* cmdline_hogger = "rufus.com"; static const char* ep_reg = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"; static const char* vs_reg = "Software\\Microsoft\\VisualStudio"; static const char* arch_name[ARCH_MAX] = { "unknown", "x86_32", "x86_64", "ARM", "ARM64", "Itanic", "RISC-V 32", "RISC-V 64", "RISC-V 128", "EBC" }; static BOOL existing_key = FALSE; // For LGP set/restore static BOOL size_check = TRUE; static BOOL log_displayed = FALSE; static BOOL img_provided = FALSE; static BOOL user_notified = FALSE; static BOOL relaunch = FALSE; static BOOL dont_display_image_name = FALSE; static BOOL dont_process_dbt_devnodes = FALSE; static BOOL user_changed_label = FALSE; static BOOL user_deleted_rufus_dir = FALSE; static BOOL app_changed_label = FALSE; static BOOL allowed_filesystem[FS_MAX] = { 0 }; static int64_t last_iso_blocking_status; static int selected_pt = -1, selected_fs = FS_UNKNOWN, preselected_fs = FS_UNKNOWN; static int image_index = 0, select_index = 0; static RECT relaunch_rc = { -65536, -65536, 0, 0}; static HWND hSelectImage = NULL, hStart = NULL; static char szTimer[12] = "00:00:00"; static unsigned int timer; static char uppercase_select[2][64], uppercase_start[64], uppercase_close[64], uppercase_cancel[64]; extern HANDLE update_check_thread, wim_thread; extern BOOL enable_iso, enable_joliet, enable_rockridge, enable_extra_hashes, is_bootloader_revoked; extern BOOL validate_md5sum; extern BYTE* fido_script; extern HWND hFidoDlg; extern uint8_t* grub2_buf; extern long grub2_len; extern char* szStatusMessage; extern const char* old_c32_name[NB_OLD_C32]; extern const char* cert_name[3]; extern const char* FileSystemLabel[FS_MAX]; extern const char *bootmgr_efi_name, *efi_dirname, *efi_bootname[ARCH_MAX]; /* * Globals */ OPENED_LIBRARIES_VARS; RUFUS_UPDATE update = { { 0,0,0 },{ 0,0 }, NULL, NULL }; HINSTANCE hMainInstance; HWND hMainDialog, hMultiToolbar, hSaveToolbar, hHashToolbar, hAdvancedDeviceToolbar, hAdvancedFormatToolbar, hUpdatesDlg = NULL; HFONT hInfoFont; uint8_t image_options = IMOP_WINTOGO; uint16_t rufus_version[3], embedded_sl_version[2]; uint32_t dur_mins, dur_secs; loc_cmd* selected_locale = NULL; WORD selected_langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); DWORD MainThreadId; HWND hDeviceList, hPartitionScheme, hTargetSystem, hFileSystem, hClusterSize, hLabel, hBootType, hNBPasses, hLog = NULL; HWND hImageOption, hLogDialog = NULL, hProgress = NULL; HANDLE dialog_handle = NULL, format_thread = NULL; BOOL is_x86_64, use_own_c32[NB_OLD_C32] = { FALSE, FALSE }, mbr_selected_by_user = FALSE, lock_drive = TRUE; BOOL op_in_progress = TRUE, right_to_left_mode = FALSE, has_uefi_csm = FALSE, its_a_me_mario = FALSE; BOOL enable_HDDs = FALSE, enable_VHDs = TRUE, enable_ntfs_compression = FALSE, no_confirmation_on_cancel = FALSE; BOOL advanced_mode_device, advanced_mode_format, allow_dual_uefi_bios, detect_fakes, enable_vmdk, force_large_fat32; BOOL usb_debug, use_fake_units, preserve_timestamps = FALSE, fast_zeroing = FALSE, app_changed_size = FALSE; BOOL zero_drive = FALSE, list_non_usb_removable_drives = FALSE, enable_file_indexing, large_drive = FALSE; BOOL write_as_image = FALSE, write_as_esp = FALSE, use_vds = FALSE, ignore_boot_marker = FALSE; BOOL appstore_version = FALSE, is_vds_available = TRUE, persistent_log = FALSE, has_ffu_support = FALSE; BOOL expert_mode = FALSE, use_rufus_mbr = TRUE; float fScale = 1.0f; int dialog_showing = 0, selection_default = BT_IMAGE, persistence_unit_selection = -1, imop_win_sel = 0; int default_fs, fs_type, boot_type, partition_type, target_type; int force_update = 0, default_thread_priority = THREAD_PRIORITY_ABOVE_NORMAL; char szFolderPath[MAX_PATH], app_dir[MAX_PATH], system_dir[MAX_PATH], temp_dir[MAX_PATH], sysnative_dir[MAX_PATH]; char app_data_dir[MAX_PATH], user_dir[MAX_PATH], cur_dir[MAX_PATH]; char embedded_sl_version_str[2][12] = { "?.??", "?.??" }; char embedded_sl_version_ext[2][32]; char ClusterSizeLabel[MAX_CLUSTER_SIZES][64]; char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path; char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *save_image_type = NULL; StrArray BlockingProcessList, ImageList; // Number of steps for each FS for FCC_STRUCTURE_PROGRESS const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 }; const char* flash_type[BADLOCKS_PATTERN_TYPES] = { "SLC", "MLC", "TLC" }; RUFUS_DRIVE rufus_drive[MAX_DRIVES] = { 0 }; // TODO: Remember to update copyright year in stdlg's AboutCallback() WM_INITDIALOG, // localization_data.sh and the .rc when the year changes! // Fill in the cluster size names static void SetClusterSizeLabels(void) { unsigned int i, j, msg_id; safe_sprintf(ClusterSizeLabel[0], 64, "%s", lmprintf(MSG_029)); for (i=512, j=1, msg_id=MSG_026; j 8192) { i /= 1024; msg_id++; } safe_sprintf(ClusterSizeLabel[j], 64, "%d %s", i, lmprintf(msg_id)); } } static void SetAllowedFileSystems(void) { int i; memset(allowed_filesystem, 0, sizeof(allowed_filesystem)); // Nothing is allowed if we don't have a drive if (ComboBox_GetCurSel(hDeviceList) < 0) return; switch (selection_default) { case BT_NON_BOOTABLE: for (i = 0; i < FS_MAX; i++) allowed_filesystem[i] = TRUE; break; case BT_MSDOS: case BT_FREEDOS: allowed_filesystem[FS_FAT16] = TRUE; allowed_filesystem[FS_FAT32] = TRUE; break; case BT_IMAGE: allowed_filesystem[FS_NTFS] = TRUE; // Don't allow anything besides NTFS if the image has a >4GB file or explicitly requires NTFS if ((image_path != NULL) && (img_report.has_4GB_file || img_report.needs_ntfs)) break; if (!HAS_WINDOWS(img_report) || (target_type != TT_BIOS) || allow_dual_uefi_bios) { if (!HAS_WINTOGO(img_report) || (ComboBox_GetCurItemData(hImageOption) != IMOP_WIN_TO_GO)) { allowed_filesystem[FS_FAT16] = TRUE; allowed_filesystem[FS_FAT32] = TRUE; } } break; case BT_GRUB2: allowed_filesystem[FS_EXT2] = TRUE; allowed_filesystem[FS_EXT3] = TRUE; allowed_filesystem[FS_EXT4] = TRUE; // Fall through case BT_SYSLINUX_V6: case BT_GRUB4DOS: allowed_filesystem[FS_NTFS] = TRUE; // Fall through case BT_SYSLINUX_V4: case BT_REACTOS: allowed_filesystem[FS_FAT16] = TRUE; allowed_filesystem[FS_FAT32] = TRUE; break; case BT_UEFI_NTFS: allowed_filesystem[FS_NTFS] = TRUE; allowed_filesystem[FS_EXFAT] = TRUE; break; } } // Populate the Boot selection dropdown static void SetBootOptions(void) { char tmp[32]; IGNORE_RETVAL(ComboBox_ResetContent(hBootType)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, lmprintf(MSG_279)), BT_NON_BOOTABLE)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, (image_path == NULL) ? lmprintf(MSG_281, lmprintf(MSG_280)) : short_image_path), BT_IMAGE)); image_index = 1; IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, "MS-DOS"), BT_MSDOS)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, "FreeDOS"), BT_FREEDOS)); if (advanced_mode_device) { static_sprintf(tmp, "Syslinux %s", embedded_sl_version_str[0]); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, tmp), BT_SYSLINUX_V4)); static_sprintf(tmp, "Syslinux %s", embedded_sl_version_str[1]); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, tmp), BT_SYSLINUX_V6)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, "ReactOS"), BT_REACTOS)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, "Grub " GRUB2_PACKAGE_VERSION), BT_GRUB2)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, "Grub4DOS " GRUB4DOS_VERSION), BT_GRUB4DOS)); IGNORE_RETVAL(ComboBox_SetItemData(hBootType, ComboBox_AddStringU(hBootType, "UEFI:NTFS"), BT_UEFI_NTFS)); } if ((!advanced_mode_device) && (selection_default >= BT_SYSLINUX_V4)) selection_default = BT_IMAGE; SetComboEntry(hBootType, selection_default); } static void SetPartitionSchemeAndTargetSystem(BOOL only_target) { // MBR, GPT, SFD BOOL allowed_partition_scheme[3] = { TRUE, TRUE, FALSE }; // BIOS, UEFI, DUAL BOOL allowed_target_system[3] = { TRUE, TRUE, FALSE }; BOOL is_windows_to_go_selected; if (!only_target) IGNORE_RETVAL(ComboBox_ResetContent(hPartitionScheme)); IGNORE_RETVAL(ComboBox_ResetContent(hTargetSystem)); boot_type = (int)ComboBox_GetCurItemData(hBootType); is_windows_to_go_selected = (boot_type == BT_IMAGE) && (image_path != NULL) && HAS_WINTOGO(img_report) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO); // If no device is selected, don't populate anything if (ComboBox_GetCurSel(hDeviceList) < 0) return; switch (boot_type) { case BT_NON_BOOTABLE: allowed_partition_scheme[PARTITION_STYLE_SFD] = TRUE; allowed_target_system[0] = FALSE; allowed_target_system[1] = FALSE; allowed_target_system[2] = TRUE; break; case BT_IMAGE: if (image_path == NULL) break; // Check if image is EFI bootable if (!IS_EFI_BOOTABLE(img_report)) { allowed_partition_scheme[PARTITION_STYLE_GPT] = FALSE; allowed_target_system[1] = FALSE; break; } // Image is EFI bootable => set dual BIOS + UEFI and so on... if (IS_BIOS_BOOTABLE(img_report)) { if (!HAS_WINDOWS(img_report) || allow_dual_uefi_bios || is_windows_to_go_selected) { allowed_target_system[0] = FALSE; allowed_target_system[1] = TRUE; allowed_target_system[2] = TRUE; } // Syslinux 4.x or earlier has no support for NTFS so if an image is using Syslinux 4.x only // and has a 4 GB file (which forces us to use NTFS) then disable MBR altogether as we won't // be able to make a working MBR install of Syslinux. if (HAS_SYSLINUX(img_report) && (SL_MAJOR(img_report.sl_version) < 5) && img_report.has_4GB_file && !HAS_BOOTMGR(img_report) && !HAS_WINPE(img_report) && !HAS_GRUB(img_report)) allowed_partition_scheme[PARTITION_STYLE_MBR] = FALSE; } else { allowed_target_system[0] = FALSE; } break; case BT_MSDOS: case BT_FREEDOS: case BT_SYSLINUX_V4: case BT_SYSLINUX_V6: case BT_REACTOS: case BT_GRUB4DOS: case BT_GRUB2: allowed_partition_scheme[PARTITION_STYLE_GPT] = FALSE; allowed_target_system[1] = FALSE; break; case BT_UEFI_NTFS: allowed_target_system[0] = FALSE; break; } if (!only_target) { // Override partition type selection to GPT for drives larger than 2TB if (SelectedDrive.DiskSize > 2 * TB) selected_pt = PARTITION_STYLE_GPT; // Try to reselect the current drive's partition scheme int preferred_pt = SelectedDrive.PartitionStyle; if (allowed_partition_scheme[PARTITION_STYLE_MBR]) IGNORE_RETVAL(ComboBox_SetItemData(hPartitionScheme, ComboBox_AddStringU(hPartitionScheme, "MBR"), PARTITION_STYLE_MBR)); if (allowed_partition_scheme[PARTITION_STYLE_GPT]) IGNORE_RETVAL(ComboBox_SetItemData(hPartitionScheme, ComboBox_AddStringU(hPartitionScheme, "GPT"), PARTITION_STYLE_GPT)); if (allowed_partition_scheme[PARTITION_STYLE_SFD]) IGNORE_RETVAL(ComboBox_SetItemData(hPartitionScheme, ComboBox_AddStringU(hPartitionScheme, sfd_name), PARTITION_STYLE_SFD)); // Override the partition scheme according to the current if (boot_type == BT_NON_BOOTABLE) preferred_pt = (selected_pt >= 0) ? selected_pt : PARTITION_STYLE_MBR; else if (boot_type == BT_UEFI_NTFS) preferred_pt = (selected_pt >= 0) ? selected_pt : PARTITION_STYLE_GPT; else if ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso || img_report.is_windows_img)) { if (HAS_WINDOWS(img_report) && img_report.has_efi) preferred_pt = allow_dual_uefi_bios? PARTITION_STYLE_MBR : ((selected_pt >= 0) ? selected_pt : PARTITION_STYLE_GPT); if (IS_DD_BOOTABLE(img_report)) preferred_pt = (selected_pt >= 0) ? selected_pt : PARTITION_STYLE_MBR; } SetComboEntry(hPartitionScheme, preferred_pt); partition_type = (int)ComboBox_GetCurItemData(hPartitionScheme); } has_uefi_csm = FALSE; if (allowed_target_system[0] && (partition_type != PARTITION_STYLE_GPT)) { IGNORE_RETVAL(ComboBox_SetItemData(hTargetSystem, ComboBox_AddStringU(hTargetSystem, lmprintf(MSG_031)), TT_BIOS)); has_uefi_csm = TRUE; } if (allowed_target_system[1] && !((partition_type == PARTITION_STYLE_MBR) && (boot_type == BT_IMAGE) && IS_BIOS_BOOTABLE(img_report) && IS_EFI_BOOTABLE(img_report)) ) IGNORE_RETVAL(ComboBox_SetItemData(hTargetSystem, ComboBox_AddStringU(hTargetSystem, lmprintf(MSG_032)), TT_UEFI)); if (allowed_target_system[2] && ((partition_type != PARTITION_STYLE_GPT) || (boot_type == BT_NON_BOOTABLE))) IGNORE_RETVAL(ComboBox_SetItemData(hTargetSystem, ComboBox_AddStringU(hTargetSystem, lmprintf(MSG_033)), TT_BIOS)); IGNORE_RETVAL(ComboBox_SetCurSel(hTargetSystem, 0)); target_type = (int)ComboBox_GetCurItemData(hTargetSystem); // Can't update a tooltip from a thread, so we send a message instead SendMessage(hMainDialog, UM_UPDATE_CSM_TOOLTIP, 0, 0); } // Populate the Allocation unit size field static BOOL SetClusterSizes(int FSType) { char* szClustSize; int i, k, default_index = 0; ULONG j; IGNORE_RETVAL(ComboBox_ResetContent(hClusterSize)); if ((FSType < 0) || (FSType >= FS_MAX)) { return FALSE; } if ((SelectedDrive.ClusterSize[FSType].Allowed == 0) || (SelectedDrive.ClusterSize[FSType].Default == 0)) { return FALSE; } for (i = 0, j = 0x100, k = 0; j < 0x10000000; i++, j <<= 1) { if (j & SelectedDrive.ClusterSize[FSType].Allowed) { if (j == SelectedDrive.ClusterSize[FSType].Default) { szClustSize = lmprintf(MSG_030, ClusterSizeLabel[i]); default_index = k; } else { szClustSize = ClusterSizeLabel[i]; } IGNORE_RETVAL(ComboBox_SetItemData(hClusterSize, ComboBox_AddStringU(hClusterSize, szClustSize), j)); k++; } } IGNORE_RETVAL(ComboBox_SetCurSel(hClusterSize, default_index)); return TRUE; } static BOOL IsRefsAvailable(MEDIA_TYPE MediaType) { // The creation of ReFS drives was added in Windows 8.1... but then removed by // Microsoft in Windows 10 1709, except for the Enterprise and Pro Workstation // versions. Oh and VdsService::QueryFileSystemTypes() is *USELESS* to detect // if ReFS is available on the system. Oh, and it only applies to fixed media. if (MediaType != FixedMedia) return FALSE; if (WindowsVersion.Version < WINDOWS_8_1 || WindowsVersion.BuildNumber <= 0) return FALSE; // Per https://gist.github.com/0xbadfca11/da0598e47dd643d933dc if (WindowsVersion.BuildNumber < 16226) return TRUE; switch (WindowsVersion.Edition) { case 0x0000000A: // Enterprise Server case 0x0000001B: // Enterprise N case 0x00000046: // Enterprise E case 0x00000048: // Enterprise Eval case 0x00000054: // Enterprise N Eval case 0x0000007D: // Enterprise S case 0x0000007E: // Enterprise S N case 0x00000081: // Enterprise S Eval case 0x00000082: // Enterprise S N Eval case 0x0000008C: // Enterprise Subscription case 0x0000008D: // Enterprise Subscription N case 0x000000A1: // Pro Workstation case 0x000000A2: // Pro Workstation N case 0x000000AB: // Enterprise G case 0x000000AC: // Enterprise G N return TRUE; default: return FALSE; } } // Populate the File System and Cluster Size dropdowns static BOOL SetFileSystemAndClusterSize(char* fs_name) { int fs_index; LONGLONG i; char tmp[128] = "", *entry; IGNORE_RETVAL(ComboBox_ResetContent(hFileSystem)); IGNORE_RETVAL(ComboBox_ResetContent(hClusterSize)); default_fs = FS_UNKNOWN; memset(&SelectedDrive.ClusterSize, 0, sizeof(SelectedDrive.ClusterSize)); /* * See https://support.microsoft.com/en-gb/help/140365/default-cluster-size-for-ntfs--fat--and-exfat * The following are MS's allowed cluster sizes for FAT16 and FAT32: * * FAT16 * 31M : 512 - 4096 * 63M : 1024 - 8192 * 127M : 2048 - 16k * 255M : 4096 - 32k * 511M : 8192 - 64k * 1023M: 16k - 64k * 2047M: 32k - 64k * 4095M: 64k * 4GB+ : N/A * * FAT32 * 31M : N/A * 63M : N/A (NB unlike MS, we're allowing 512-512 here) * 127M : 512 - 1024 * 255M : 512 - 2048 * 511M : 512 - 4096 * 1023M: 512 - 8192 * 2047M: 512 - 16k * 4095M: 1024 - 32k * 7GB : 2048 - 64k * 15GB : 4096 - 64k * 31GB : 8192 - 64k This is as far as Microsoft's FormatEx goes... * 63GB : 16k - 64k ...but we can go higher using fat32format from RidgeCrop. * 2TB+ : N/A * */ // FAT 16 if (SelectedDrive.DiskSize < 4 * GB) { SelectedDrive.ClusterSize[FS_FAT16].Allowed = 0x00001E00; for (i = 32; i <= 4096; i <<= 1) { // 8 MB -> 4 GB if (SelectedDrive.DiskSize < i * MB) { SelectedDrive.ClusterSize[FS_FAT16].Default = 16 * (ULONG)i; break; } SelectedDrive.ClusterSize[FS_FAT16].Allowed <<= 1; } SelectedDrive.ClusterSize[FS_FAT16].Allowed &= 0x0001FE00; } // FAT 32 // > 32GB FAT32 is not supported by MS and FormatEx but is achieved using fat32format // See: http://ridgecrop.co.uk/index.htm?fat32format.htm // < 32 MB FAT32 is not allowed by FormatEx, so we don't bother if ((SelectedDrive.DiskSize >= 32 * MB) && (SelectedDrive.DiskSize < MAX_FAT32_SIZE)) { SelectedDrive.ClusterSize[FS_FAT32].Allowed = 0x000001F8; for (i = 32; i <= (32 * 1024); i <<= 1) { // 32 MB -> 32 GB if (SelectedDrive.DiskSize*1.0f < i * MB * FAT32_CLUSTER_THRESHOLD) { // MS SelectedDrive.ClusterSize[FS_FAT32].Default = 8*(ULONG)i; break; } SelectedDrive.ClusterSize[FS_FAT32].Allowed <<= 1; } SelectedDrive.ClusterSize[FS_FAT32].Allowed &= 0x0001FE00; // Default cluster sizes in the 256MB to 32 GB range do not follow the rule above if ((SelectedDrive.DiskSize >= 256 * MB) && (SelectedDrive.DiskSize < 32 * GB)) { for (i = 8; i <= 32; i <<= 1) { // 256 MB -> 32 GB if (SelectedDrive.DiskSize * 1.0f < i * GB * FAT32_CLUSTER_THRESHOLD) { SelectedDrive.ClusterSize[FS_FAT32].Default = ((ULONG)i / 2) * KB; break; } } } // More adjustments for large drives if (SelectedDrive.DiskSize >= 32 * GB) { SelectedDrive.ClusterSize[FS_FAT32].Allowed &= 0x0001C000; SelectedDrive.ClusterSize[FS_FAT32].Default = 0x00008000; } } if (SelectedDrive.DiskSize < 256 * TB) { // NTFS SelectedDrive.ClusterSize[FS_NTFS].Allowed = 0x0001FE00; for (i = 16; i <= 256; i <<= 1) { // 7 MB -> 256 TB if (SelectedDrive.DiskSize < i * TB) { SelectedDrive.ClusterSize[FS_NTFS].Default = ((ULONG)i / 4) * KB; break; } } // exFAT SelectedDrive.ClusterSize[FS_EXFAT].Allowed = 0x03FFFE00; if (SelectedDrive.DiskSize < 256 * MB) // < 256 MB SelectedDrive.ClusterSize[FS_EXFAT].Default = 4 * KB; else if (SelectedDrive.DiskSize < 32 * GB) // < 32 GB SelectedDrive.ClusterSize[FS_EXFAT].Default = 32 * KB; else SelectedDrive.ClusterSize[FS_EXFAT].Default = 128 * KB; // UDF SelectedDrive.ClusterSize[FS_UDF].Allowed = SINGLE_CLUSTERSIZE_DEFAULT; SelectedDrive.ClusterSize[FS_UDF].Default = 1; // ext2/ext3/ext4 if (advanced_mode_format && (SelectedDrive.DiskSize >= MIN_EXT_SIZE)) { SelectedDrive.ClusterSize[FS_EXT2].Allowed = SINGLE_CLUSTERSIZE_DEFAULT; SelectedDrive.ClusterSize[FS_EXT2].Default = 1; SelectedDrive.ClusterSize[FS_EXT3].Allowed = SINGLE_CLUSTERSIZE_DEFAULT; SelectedDrive.ClusterSize[FS_EXT3].Default = 1; } // ReFS (only applicable for a select number of Windows platforms and editions) if ((SelectedDrive.DiskSize >= 512 * MB) && (IsRefsAvailable(SelectedDrive.MediaType))) { if (SelectedDrive.DiskSize < 16 * TB) { // < 16 TB SelectedDrive.ClusterSize[FS_REFS].Allowed = 64 * KB + 4 * KB; SelectedDrive.ClusterSize[FS_REFS].Default = 4 * KB; } else { SelectedDrive.ClusterSize[FS_REFS].Allowed = 64 * KB; SelectedDrive.ClusterSize[FS_REFS].Default = 64 * KB; } } } // Only add the filesystems we can service SetAllowedFileSystems(); SetClusterSizeLabels(); for (fs_index = 0; fs_index < FS_MAX; fs_index++) { // Remove all cluster sizes that are below the sector size if (SelectedDrive.ClusterSize[fs_index].Allowed != SINGLE_CLUSTERSIZE_DEFAULT) { SelectedDrive.ClusterSize[fs_index].Allowed &= ~(SelectedDrive.SectorSize - 1); if ((SelectedDrive.ClusterSize[fs_index].Default & SelectedDrive.ClusterSize[fs_index].Allowed) == 0) // We lost our default => Use rightmost bit to select the new one SelectedDrive.ClusterSize[fs_index].Default = SelectedDrive.ClusterSize[fs_index].Allowed & (-(LONG)SelectedDrive.ClusterSize[fs_index].Allowed); } if (SelectedDrive.ClusterSize[fs_index].Allowed != 0) { tmp[0] = 0; // Tell the user if we're going to use Large FAT32 or regular if ((fs_index == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32))) static_strcat(tmp, "Large "); static_strcat(tmp, FileSystemLabel[fs_index]); if (default_fs == FS_UNKNOWN) { entry = lmprintf(MSG_030, tmp); default_fs = fs_index; } else { entry = tmp; } if (allowed_filesystem[fs_index]) { IGNORE_RETVAL(ComboBox_SetItemData(hFileSystem, ComboBox_AddStringU(hFileSystem, entry), fs_index)); } } } // re-select existing FS if it's one we know SelectedDrive.FSType = FS_UNKNOWN; if (safe_strlen(fs_name) != 0) { for (SelectedDrive.FSType = FS_MAX - 1; SelectedDrive.FSType >= 0; SelectedDrive.FSType--) { if (safe_strcmp(fs_name, FileSystemLabel[SelectedDrive.FSType]) == 0) { break; } } } else { // Re-select last user-selected FS SelectedDrive.FSType = selected_fs; } for (i = 0; i < ComboBox_GetCount(hFileSystem); i++) { if (ComboBox_GetItemData(hFileSystem, i) == SelectedDrive.FSType) { IGNORE_RETVAL(ComboBox_SetCurSel(hFileSystem, i)); break; } } if (i == ComboBox_GetCount(hFileSystem)) { // failed to reselect => pick default SetComboEntry(hFileSystem, default_fs); } return SetClusterSizes((int)ComboBox_GetCurItemData(hFileSystem)); } static void SetFSFromISO(void) { int i, fs_tmp, preferred_fs = FS_UNKNOWN; uint32_t fs_mask = FS_NTFS | (img_report.has_4GB_file ? 0 : FS_FAT32); BOOL windows_to_go = (image_options & IMOP_WINTOGO) && (boot_type == BT_IMAGE) && HAS_WINTOGO(img_report) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO); if (image_path == NULL) return; // Create a mask of all the FS's available for (i = 0; i < ComboBox_GetCount(hFileSystem); i++) { fs_tmp = (int)ComboBox_GetItemData(hFileSystem, i); fs_mask |= 1 << fs_tmp; } // If the FS requested from the command line is valid use it if ((preselected_fs != FS_UNKNOWN) && (fs_mask & (1 << preselected_fs))) { preferred_fs = preselected_fs; } else { // Syslinux and EFI have precedence over bootmgr (unless the user selected BIOS as target type) if ((HAS_SYSLINUX(img_report)) || (HAS_REACTOS(img_report)) || HAS_KOLIBRIOS(img_report) || (IS_EFI_BOOTABLE(img_report) && (target_type == TT_UEFI) && (!windows_to_go) && (!img_report.has_4GB_file))) { if (fs_mask & (1 << FS_FAT32)) preferred_fs = FS_FAT32; else if ((fs_mask & (1 << FS_FAT16)) && !HAS_KOLIBRIOS(img_report)) preferred_fs = FS_FAT16; } else if ((windows_to_go) || HAS_BOOTMGR(img_report) || HAS_WINPE(img_report)) { if ((fs_mask & (1 << FS_FAT32)) && (!img_report.has_4GB_file) && (allow_dual_uefi_bios)) preferred_fs = FS_FAT32; else if (fs_mask & (1 << FS_NTFS)) preferred_fs = FS_NTFS; } } // Try to select the FS for (i = 0; i < ComboBox_GetCount(hFileSystem); i++) { fs_tmp = (int)ComboBox_GetItemData(hFileSystem, i); if (fs_tmp == preferred_fs) { IGNORE_RETVAL(ComboBox_SetCurSel(hFileSystem, i)); break; } } if (selected_fs == FS_UNKNOWN) selected_fs = preferred_fs; SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL << 16) | IDC_FILE_SYSTEM, ComboBox_GetCurSel(hFileSystem)); } static void SetProposedLabel(int ComboIndex) { const char no_label[] = STR_NO_LABEL, empty[] = ""; // If the user manually changed the label, try to preserve it if (user_changed_label) return; app_changed_label = TRUE; // If bootable ISO creation is selected, and we have an ISO selected with a valid name, use that // Also some distros (eg. Arch) require the USB to have the same label as the ISO if ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.label[0] != 0)) { SetWindowTextU(hLabel, img_report.label); return; } // Empty the label if no device is currently selected if (ComboIndex < 0) { SetWindowTextU(hLabel, ""); return; } // Else if no existing label is available, propose one according to the size (eg: "256MB", "8GB") if ((_stricmp(no_label, rufus_drive[ComboIndex].label) == 0) || (_stricmp(no_label, empty) == 0) || (safe_stricmp(lmprintf(MSG_207), rufus_drive[ComboIndex].label) == 0)) { SetWindowTextU(hLabel, SelectedDrive.proposed_label); } else { SetWindowTextU(hLabel, rufus_drive[ComboIndex].label); } } static void EnableOldBiosFixes(BOOL enable, BOOL remove_checkboxes) { static UINT checked, state = 0; HWND hCtrl = GetDlgItem(hMainDialog, IDC_OLD_BIOS_FIXES); // The fix for old BIOSes option cannot apply if we aren't targetting BIOS, or are using an image that isn't BIOS bootable if ((partition_type != PARTITION_STYLE_MBR) || (target_type != TT_BIOS) || (boot_type == BT_NON_BOOTABLE) || ((boot_type == BT_IMAGE) && (!IS_BIOS_BOOTABLE(img_report) || IS_DD_ONLY(img_report)))) { enable = FALSE; } if (remove_checkboxes) { if (!enable && (state != 1)) { checked = IsChecked(IDC_OLD_BIOS_FIXES); CheckDlgButton(hMainDialog, IDC_OLD_BIOS_FIXES, BST_UNCHECKED); state = 1; } else if (enable && !IsWindowEnabled(hCtrl) && (state != 2)) { if (state != 0) CheckDlgButton(hMainDialog, IDC_OLD_BIOS_FIXES, checked); state = 2; } } EnableWindow(hCtrl, enable); } static void EnableUefiValidation(BOOL enable, BOOL remove_checkboxes) { UINT checked = validate_md5sum ? BST_CHECKED : BST_UNCHECKED; HWND hCtrl = GetDlgItem(hMainDialog, IDC_UEFI_MEDIA_VALIDATION); // The UEFI validation bootloader cannot apply if we don't write an ISO, or if the ISO is not UEFI bootable // or if it's a Windows To Go installation or if DD or BIOS/CSM only are enforced. if ((boot_type != BT_IMAGE) || (!IS_EFI_BOOTABLE(img_report)) || IS_DD_ONLY(img_report) || ((image_options & IMOP_WINTOGO) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO)) || ((target_type == TT_BIOS) && HAS_WINDOWS(img_report) && (!allow_dual_uefi_bios))) { enable = FALSE; } if (!enable && remove_checkboxes) CheckDlgButton(hMainDialog, IDC_UEFI_MEDIA_VALIDATION, BST_UNCHECKED); else CheckDlgButton(hMainDialog, IDC_UEFI_MEDIA_VALIDATION, checked); EnableWindow(hCtrl, enable); } static void EnableExtendedLabel(BOOL enable, BOOL remove_checkboxes) { static UINT checked, state = 0; HWND hCtrl = GetDlgItem(hMainDialog, IDC_EXTENDED_LABEL); if (IS_EXT(fs_type) || ((boot_type == BT_IMAGE) && IS_DD_ONLY(img_report))) enable = FALSE; if (remove_checkboxes) { if (!enable && (state != 1)) { checked = IsChecked(IDC_EXTENDED_LABEL); CheckDlgButton(hMainDialog, IDC_EXTENDED_LABEL, BST_UNCHECKED); state = 1; } else if (enable && !IsWindowEnabled(hCtrl) && (state != 2)) { if (state != 0) CheckDlgButton(hMainDialog, IDC_EXTENDED_LABEL, checked); state = 2; } } EnableWindow(hCtrl, enable); } static void EnableQuickFormat(BOOL enable, BOOL remove_checkboxes) { static UINT checked, state = 0; HWND hCtrl = GetDlgItem(hMainDialog, IDC_QUICK_FORMAT); if ((boot_type == BT_IMAGE) && IS_DD_ONLY(img_report)) enable = FALSE; // Disable/restore the quick format control depending on large FAT32 or ReFS if (((fs_type == FS_FAT32) && ((SelectedDrive.DiskSize > LARGE_FAT32_SIZE) || (force_large_fat32))) || (fs_type == FS_REFS)) { enable = FALSE; // Quick Format is the only option for the above remove_checkboxes = FALSE; CheckDlgButton(hMainDialog, IDC_QUICK_FORMAT, BST_CHECKED); } if (remove_checkboxes) { if (!enable && (state != 1)) { checked = IsChecked(IDC_QUICK_FORMAT); CheckDlgButton(hMainDialog, IDC_QUICK_FORMAT, BST_UNCHECKED); state = 1; } else if (enable && !IsWindowEnabled(hCtrl) && (state != 2)) { if (state != 0) CheckDlgButton(hMainDialog, IDC_QUICK_FORMAT, checked); state = 2; } } EnableWindow(hCtrl, enable); } static void EnableBootOptions(BOOL enable, BOOL remove_checkboxes) { BOOL actual_enable_bb, actual_enable = enable; // If no device is selected, don't enable anything and also don't remove the checkboxes if (ComboBox_GetCurSel(hDeviceList) < 0) { actual_enable = FALSE; remove_checkboxes = FALSE; } // If boot selection is set to image, but no image is currently selected, don't enable anything if ((boot_type == BT_IMAGE) && (image_path == NULL)) actual_enable = FALSE; actual_enable_bb = actual_enable; // If we are dealing with a pure DD image, remove all options except Bad Blocks check if ((boot_type == BT_IMAGE) && IS_DD_BOOTABLE(img_report) && (!img_report.is_iso)) actual_enable = FALSE; EnableWindow(hImageOption, actual_enable); EnableWindow(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER), actual_enable); // Make sure we set the range if we have persistence if ((image_path != NULL) && HAS_PERSISTENCE(img_report)) SetPersistenceSize(); EnableWindow(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), (persistence_size != 0) && actual_enable); EnableWindow(GetDlgItem(hMainDialog, IDC_PERSISTENCE_UNITS), (persistence_size != 0) && actual_enable); EnableOldBiosFixes(actual_enable, remove_checkboxes); EnableUefiValidation(actual_enable, remove_checkboxes); EnableWindow(GetDlgItem(hMainDialog, IDC_LABEL), actual_enable); if (boot_type == BT_IMAGE) { if (IS_DD_ONLY(img_report)) remove_checkboxes = TRUE; else if (image_path == NULL) remove_checkboxes = FALSE; } EnableQuickFormat(actual_enable, remove_checkboxes); EnableExtendedLabel(actual_enable, remove_checkboxes); EnableWindow(GetDlgItem(hMainDialog, IDC_BAD_BLOCKS), actual_enable_bb); EnableWindow(GetDlgItem(hMainDialog, IDC_NB_PASSES), actual_enable_bb); } // Toggle controls according to operation void EnableControls(BOOL enable, BOOL remove_checkboxes) { op_in_progress = !enable; // The following only get disabled on format/hash and otherwise remain enabled, // even if no device or image are selected EnableWindow(hDeviceList, enable); EnableWindow(hBootType, enable); EnableWindow(hSelectImage, enable); EnableWindow(GetDlgItem(hMainDialog, IDC_LIST_USB_HDD), enable); EnableWindow(hAdvancedDeviceToolbar, enable); EnableWindow(hAdvancedFormatToolbar, enable); SendMessage(hMultiToolbar, TB_ENABLEBUTTON, (WPARAM)IDC_LANG, (LPARAM)enable); SendMessage(hMultiToolbar, TB_ENABLEBUTTON, (WPARAM)IDC_ABOUT, (LPARAM)enable); SendMessage(hMultiToolbar, TB_ENABLEBUTTON, (WPARAM)IDC_SETTINGS, (LPARAM)enable); // Hash button is enabled if an image has been selected EnableWindow(hHashToolbar, enable && (boot_type == BT_IMAGE) && (image_path != NULL)); // Toggle CLOSE/CANCEL SetDlgItemTextU(hMainDialog, IDCANCEL, enable ? uppercase_close : uppercase_cancel); // Only enable the following controls if a device is active enable = (ComboBox_GetCurSel(hDeviceList) < 0) ? FALSE : enable; EnableWindow(hImageOption, enable); EnableWindow(hSaveToolbar, enable); // Enable or disable the Start button and the other boot options enable = ((boot_type == BT_IMAGE) && (image_path == NULL)) ? FALSE : enable; EnableWindow(hStart, enable); EnableBootOptions(enable, remove_checkboxes); // Finally, only enable the half-size dropdowns if we aren't dealing with a pure DD image enable = ((boot_type == BT_IMAGE) && (image_path != NULL) && (!(img_report.is_iso || img_report.is_windows_img))) ? FALSE : enable; EnableWindow(hPartitionScheme, enable); EnableWindow(hTargetSystem, enable); EnableWindow(GetDlgItem(hMainDialog, IDS_CSM_HELP_TXT), enable); EnableWindow(hFileSystem, enable); EnableWindow(hClusterSize, enable); } // Populate the UI main dropdown properties. // This should be called on device or boot type change. static BOOL PopulateProperties(void) { char* device_tooltip; int device_index = ComboBox_GetCurSel(hDeviceList); char fs_name[32]; memset(&SelectedDrive, 0, sizeof(SelectedDrive)); EnableWindow(hStart, FALSE); if (device_index < 0) goto out; persistence_unit_selection = -1; // Get data from the currently selected drive SelectedDrive.DeviceNumber = (DWORD)ComboBox_GetItemData(hDeviceList, device_index); // This fills the SelectedDrive properties GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_name, sizeof(fs_name), FALSE); SetPartitionSchemeAndTargetSystem(FALSE); // Attempt to reselect the last file system explicitly set by the user if (!SetFileSystemAndClusterSize((selected_fs == FS_UNKNOWN) ? fs_name : NULL)) { SetProposedLabel(-1); uprintf("No file system is selectable for this drive\n"); return FALSE; } EnableControls(TRUE, FALSE); // Set a proposed label according to the size (eg: "256MB", "8GB") static_sprintf(SelectedDrive.proposed_label, "%s", SizeToHumanReadable(SelectedDrive.DiskSize, FALSE, TRUE)); // Add a tooltip (with the size of the device in parenthesis) device_tooltip = (char*) malloc(safe_strlen(rufus_drive[device_index].name) + 32); if (device_tooltip != NULL) { if (right_to_left_mode) safe_sprintf(device_tooltip, safe_strlen(rufus_drive[device_index].name) + 32, "(%s) %s", SizeToHumanReadable(SelectedDrive.DiskSize, FALSE, FALSE), rufus_drive[device_index].name); else safe_sprintf(device_tooltip, safe_strlen(rufus_drive[device_index].name) + 32, "%s (%s)", rufus_drive[device_index].name, SizeToHumanReadable(SelectedDrive.DiskSize, FALSE, FALSE)); CreateTooltip(hDeviceList, device_tooltip, -1); free(device_tooltip); } out: SetProposedLabel(device_index); return TRUE; } // Callback for the log window BOOL CALLBACK LogCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { HDC hDC; HFONT hf; LONG lfHeight; LONG_PTR style; DWORD log_size; char *log_buffer = NULL, *filepath; EXT_DECL(log_ext, "rufus.log", __VA_GROUP__("*.log"), __VA_GROUP__("Rufus log")); switch (message) { case WM_INITDIALOG: apply_localization(IDD_LOG, hDlg); hLog = GetDlgItem(hDlg, IDC_LOG_EDIT); // Increase the size of our log textbox to MAX_LOG_SIZE (unsigned word) PostMessage(hLog, EM_LIMITTEXT, MAX_LOG_SIZE , 0); // Set the font to Unicode so that we can display anything hDC = GetDC(NULL); lfHeight = -MulDiv(9, GetDeviceCaps(hDC, LOGPIXELSY), 72); safe_release_dc(NULL, hDC); hf = CreateFontA(lfHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, PROOF_QUALITY, 0, "Consolas"); SendDlgItemMessageA(hDlg, IDC_LOG_EDIT, WM_SETFONT, (WPARAM)hf, TRUE); // Set 'Close Log' as the selected button SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDCANCEL), TRUE); // Suppress any inherited RTL flags from our edit control's style. Otherwise // the displayed text becomes a mess due to Windows trying to interpret // dots, parenthesis, columns and so on in an RTL context... // We also take this opportunity to fix the scroll bar and text alignment. style = GetWindowLongPtr(hLog, GWL_EXSTYLE); style &= ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR); SetWindowLongPtr(hLog, GWL_EXSTYLE, style); style = GetWindowLongPtr(hLog, GWL_STYLE); style &= ~(ES_RIGHT); SetWindowLongPtr(hLog, GWL_STYLE, style); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: ShowWindow(hDlg, SW_HIDE); log_displayed = FALSE; // Set focus to the Cancel button on the main dialog // This avoids intempestive tooltip display from the log toolbar button SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDCANCEL), TRUE); return TRUE; case IDC_LOG_CLEAR: SetWindowTextA(hLog, ""); return TRUE; case IDC_LOG_SAVE: log_size = GetWindowTextLengthU(hLog); if (log_size <= 0) break; log_buffer = (char*)malloc(log_size); if (log_buffer != NULL) { log_size = GetDlgItemTextU(hDlg, IDC_LOG_EDIT, log_buffer, log_size); if (log_size != 0) { log_size--; // remove NUL terminator filepath = FileDialog(TRUE, user_dir, &log_ext, NULL); if (filepath != NULL) FileIO(FILE_IO_WRITE, filepath, &log_buffer, &log_size); safe_free(filepath); } safe_free(log_buffer); } break; } break; case WM_CLOSE: ShowWindow(hDlg, SW_HIDE); reset_localization(IDD_LOG); log_displayed = FALSE; // Set focus to the Cancel button on the main dialog // This avoids intempestive tooltip display from the log toolbar button SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDCANCEL), TRUE); return TRUE; case UM_RESIZE_BUTTONS: // Resize our buttons for low scaling factors ResizeButtonHeight(hDlg, IDCANCEL); ResizeButtonHeight(hDlg, IDC_LOG_SAVE); ResizeButtonHeight(hDlg, IDC_LOG_CLEAR); return TRUE; } return FALSE; } // Timer in the right part of the status area static void CALLBACK ClockTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { timer++; static_sprintf(szTimer, "%02d:%02d:%02d", timer/3600, (timer%3600)/60, timer%60); SendMessageA(hStatus, SB_SETTEXTA, SBT_OWNERDRAW | SB_SECTION_RIGHT, (LPARAM)szTimer); } // Device Refresh Timer static void CALLBACK RefreshTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { // DO NOT USE WM_DEVICECHANGE - IT MAY BE FILTERED OUT BY WINDOWS! SendMessage(hWnd, UM_MEDIA_CHANGE, 0, 0); } // Detect and notify about a blocking operation during ISO extraction cancellation static void CALLBACK BlockingTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { if (iso_blocking_status < 0) { KillTimer(hMainDialog, TID_BLOCKING_TIMER); user_notified = FALSE; uprintf("Killed blocking I/O timer\n"); } else if(!user_notified) { if (last_iso_blocking_status == iso_blocking_status) { // A write or close operation hasn't made any progress since our last check user_notified = TRUE; uprintf("Blocking I/O operation detected\n"); MessageBoxExU(hMainDialog, lmprintf(MSG_080), lmprintf(MSG_048), MB_OK|MB_ICONINFORMATION|MB_IS_RTL, selected_langid); } else { last_iso_blocking_status = iso_blocking_status; } } } // Report the features of the selected ISO images #define PRINT_ISO_PROP(b, ...) do {if (b) uprintf(__VA_ARGS__);} while(0) static void DisplayISOProps(void) { static char inst_str[] = " [1/#]"; int i; uprintf("ISO label: '%s'", img_report.label); if (img_report.win_version.major != 0) { if (img_report.win_version.minor == 0) uprintf(" Detected: Windows %d ISO (Build %d.%d)", img_report.win_version.major, img_report.win_version.build, img_report.win_version.revision); else uprintf(" Detected: Windows %d.%d ISO (Build %d.%d)", img_report.win_version.major, img_report.win_version.minor, img_report.win_version.build, img_report.win_version.revision); } uprintf(" Size: %s (Projected)", SizeToHumanReadable(img_report.projected_size, FALSE, FALSE)); if (img_report.mismatch_size > 0) { uprintf(" ERROR: Detected that file on disk has been truncated by %s!", SizeToHumanReadable(img_report.mismatch_size, FALSE, FALSE)); MessageBoxExU(hMainDialog, lmprintf(MSG_298, SizeToHumanReadable(img_report.mismatch_size, FALSE, FALSE)), lmprintf(MSG_297), MB_ICONWARNING | MB_IS_RTL, selected_langid); } else if (img_report.mismatch_size < 0) { // Not an error (ISOHybrid?), but we report it just in case uprintf(" Note: File on disk is larger than reported ISO size by %s...", SizeToHumanReadable(-img_report.mismatch_size, FALSE, FALSE)); } PRINT_ISO_PROP(img_report.has_4GB_file, " Has a >4GB file"); PRINT_ISO_PROP(img_report.has_long_filename, " Has a >64 chars filename"); PRINT_ISO_PROP(img_report.has_deep_directories, " Has a Rock Ridge deep directory"); PRINT_ISO_PROP(HAS_SYSLINUX(img_report), " Uses: Syslinux/Isolinux v%s", img_report.sl_version_str); if (HAS_SYSLINUX(img_report) && (SL_MAJOR(img_report.sl_version) < 5)) { for (i = 0; i= 3); uprintf(" Uses: Install.%s%s (version %d.%d.%d%s)", &img_report.wininst_path[0][strlen(img_report.wininst_path[0]) - 3], (img_report.wininst_index > 1) ? inst_str : "", (img_report.wininst_version >> 24) & 0xff, (img_report.wininst_version >> 16) & 0xff, (img_report.wininst_version >> 8) & 0xff, (img_report.wininst_version >= SPECIAL_WIM_VERSION) ? "+": ""); } if (img_report.needs_ntfs) { uprintf(" Note: This ISO uses symbolic links and was not designed to work without them.\r\n" " Because of this, only NTFS will be allowed as the target file system."); } else { PRINT_ISO_PROP(img_report.has_symlinks, " Note: This ISO uses symbolic links, which may not be replicated due to file system"); PRINT_ISO_PROP((img_report.has_symlinks == SYMLINKS_RR), " limitations. Because of this, some features from this image may not work..."); PRINT_ISO_PROP((img_report.has_symlinks == SYMLINKS_UDF), " limitations. Because of this, the size required for the target media may be much\r\n" " larger than size of the ISO..."); } } // Insert the image name into the Boot selection dropdown and (re)populate the Image option dropdown static void UpdateImage(BOOL update_image_option_only) { assert(image_index != 0); if (!update_image_option_only) { if (ComboBox_GetItemData(hBootType, image_index) == BT_IMAGE) ComboBox_DeleteString(hBootType, image_index); ComboBox_InsertStringU(hBootType, image_index, (image_path == NULL) ? lmprintf(MSG_281, lmprintf(MSG_280)) : short_image_path); ComboBox_SetItemData(hBootType, image_index, BT_IMAGE); IGNORE_RETVAL(ComboBox_SetCurSel(hBootType, image_index)); boot_type = (int)ComboBox_GetCurItemData(hBootType); SetBootTypeDropdownWidth(); } ComboBox_ResetContent(hImageOption); if (!img_report.is_windows_img) // Straight install.wim/install.esd only have Windows To Go option IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_117)), IMOP_WIN_STANDARD)); IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_118)), IMOP_WIN_TO_GO)); IGNORE_RETVAL(ComboBox_SetCurSel(hImageOption, imop_win_sel)); } /// /// Parse a PE executable file and return its CPU architecture. /// /// The path of the PE executable to parse. /// An enum ArchType value (as defined in rufus.h) static uint8_t FindArch(const char* path) { uint8_t ret = ARCH_UNKNOWN; HANDLE hFile = NULL, hFileMapping = NULL; PIMAGE_DOS_HEADER pImageDOSHeader = NULL; // NB: The field we are after is at the same location for 32 and 64-bit // PE headers, so we don't need to care about using PIMAGE_NT_HEADERS[32|64] PIMAGE_NT_HEADERS pImageNTHeader = NULL; hFile = CreateFileU(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == NULL) { uprintf("FindArch: Could not open file '%s': %s", path, WindowsErrorString()); return 0; } hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (hFileMapping == NULL) { uprintf("FindArch: Could not create file mapping: %s", WindowsErrorString()); goto out; } pImageDOSHeader = (PIMAGE_DOS_HEADER)MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0); if (pImageDOSHeader == NULL) { uprintf("FindArch: Could not get mapped view address: %s", WindowsErrorString()); goto out; } if (pImageDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) { uprintf("FindArch: DOS header not found"); goto out; } pImageNTHeader = (PIMAGE_NT_HEADERS)((uintptr_t)pImageDOSHeader + pImageDOSHeader->e_lfanew); if (pImageNTHeader->Signature != IMAGE_NT_SIGNATURE) { uprintf("FindArch: NT header not found"); goto out; } switch (pImageNTHeader->FileHeader.Machine) { case IMAGE_FILE_MACHINE_I386: ret = ARCH_X86_32; break; case IMAGE_FILE_MACHINE_AMD64: ret = ARCH_X86_64; break; case IMAGE_FILE_MACHINE_ARM: ret = ARCH_ARM_32; break; case IMAGE_FILE_MACHINE_ARM64: ret = ARCH_ARM_64; break; case IMAGE_FILE_MACHINE_IA64: ret = ARCH_IA_64; break; case IMAGE_FILE_MACHINE_RISCV32: ret = ARCH_RISCV_32; break; case IMAGE_FILE_MACHINE_RISCV64: ret = ARCH_RISCV_64; break; case IMAGE_FILE_MACHINE_RISCV128: ret = ARCH_RISCV_128; break; case IMAGE_FILE_MACHINE_EBC: ret = ARCH_EBC; break; } out: if (pImageDOSHeader != NULL) UnmapViewOfFile(pImageDOSHeader); safe_closehandle(hFileMapping); safe_closehandle(hFile); assert(ret < ARCH_MAX); return ret; } // The scanning process can be blocking for message processing => use a thread DWORD WINAPI ImageScanThread(LPVOID param) { // Regexp patterns used to match ISO labels for the Red Hat 8 derivatives // where we should apply an inst.stage2 ➔ inst.repo workaround for ISO // mode (per: https://github.com/rhinstaller/anaconda/pull/3529). const char* redhat8_derivative[] = { "^AlmaLinux-[8-9].*", // AlmaLinux 8.x and 9.x "^Fedora.*-3[3-9].*", // Fedora 33-39 "^CentOS.*-[8-9].*", // CentOS and CentOS Stream 8.x and 9.x "^Circle.*-[8-9].*", // Circle Linux 8.x.and 9.x "^OL-[8-9].*", // Oracle Linux 8.x and 9.x "^RHEL-[8-9].*", // Red Hat 8.x and 9.x "^Rocky-[8-9].*", // Rocky Linux 8.x and 9.x "^MIRACLE-LINUX-[8-9].*", // MIRACLE LINUX 8.x and 9.x }; int i, len; uint8_t arch; char tmp_path[MAX_PATH], tmp_str[64]; // We may mount an ISO during the lookup of the Windows version, which // produces DBT_DEVNODES_CHANGED messages that lead to unwanted device // refreshes. So make sure to ignore DBT_DEVNODES_CHANGED while scanning. dont_process_dbt_devnodes = TRUE; if (image_path == NULL) goto out; PrintInfoDebug(0, MSG_202); user_notified = FALSE; EnableControls(FALSE, FALSE); memset(&img_report, 0, sizeof(img_report)); img_report.is_iso = (BOOLEAN)ExtractISO(image_path, "", TRUE); img_report.is_bootable_img = IsBootableImage(image_path); if (img_report.wininst_index > 0 || img_report.is_windows_img) PopulateWindowsVersion(); ComboBox_ResetContent(hImageOption); imop_win_sel = 0; if ((ErrorStatus == RUFUS_ERROR(ERROR_CANCELLED)) || (img_report.image_size == 0) || (!img_report.is_iso && (img_report.is_bootable_img <= 0) && !img_report.is_windows_img)) { // Failed to scan image if (img_report.is_bootable_img < 0) MessageBoxExU(hMainDialog, lmprintf(MSG_322, image_path), lmprintf(MSG_042), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); else MessageBoxExU(hMainDialog, lmprintf(MSG_082), lmprintf(MSG_081), MB_OK | MB_ICONINFORMATION | MB_IS_RTL, selected_langid); // Make sure to relinquish image_path before we call UpdateImage // otherwise the boot selection dropdown won't be properly reset. safe_free(image_path); SendMessage(hMainDialog, UM_PROGRESS_EXIT, 0, 0); UpdateImage(FALSE); PopulateProperties(); PrintInfoDebug(0, MSG_203); PrintStatus(0, MSG_203); EnableControls(TRUE, FALSE); goto out; } if (img_report.is_windows_img) { selection_default = BT_IMAGE; // coverity[swapped_arguments] if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, tmp_path) != 0) { // Only look at index 1 for now. If people complain, we may look for more. if (WimExtractFile(image_path, 1, "Windows\\Boot\\EFI\\bootmgr.efi", tmp_path, TRUE)) { arch = FindArch(tmp_path); if (arch != 0) { uprintf(" Image contains a%s %s EFI boot manager", (arch >= ARCH_RISCV_32 && arch <= ARCH_RISCV_128) ? "" : "n", arch_name[arch]); img_report.has_efi = 1 | (1 << arch); img_report.has_bootmgr_efi = TRUE; img_report.wininst_index = 1; } else { uprintf(" Image does not contain an EFI boot manager"); } } DeleteFileU(tmp_path); } tmp_str[0] = 0; if (img_report.win_version.major != 0) { if (img_report.win_version.minor == 0) static_sprintf(tmp_str, " %d (Build %d.%d)", img_report.win_version.major, img_report.win_version.build, img_report.win_version.revision); else static_sprintf(tmp_str, " %d.%d (Build %d.%d)", img_report.win_version.major, img_report.win_version.minor, img_report.win_version.build, img_report.win_version.revision); } uprintf(" Image is a %sUEFI bootable Windows%s installation image", img_report.has_efi ? "" : "NON-", tmp_str); } else if (IS_DD_BOOTABLE(img_report)) { if (img_report.is_bootable_img == 2) uprintf(" Image is a FORCED non-bootable image"); else uprintf(" Image is a %sbootable %s image", (img_report.compression_type != BLED_COMPRESSION_NONE && img_report.compression_type < BLED_COMPRESSION_MAX) ? "compressed " : "", img_report.is_vhd ? "VHD" : "disk"); selection_default = BT_IMAGE; } if (img_report.is_iso) { DisplayISOProps(); for (i = 0; i < ARRAYSIZE(redhat8_derivative); i++) { if (re_match(redhat8_derivative[i], img_report.label, &len) >= 0) { img_report.rh8_derivative = TRUE; break; } } // If we have an ISOHybrid, but without an ISO method we support, disable ISO support altogether if (IS_DD_BOOTABLE(img_report) && (img_report.disable_iso || (!IS_BIOS_BOOTABLE(img_report) && !IS_EFI_BOOTABLE(img_report)))) { MessageBoxExU(hMainDialog, lmprintf(MSG_321), lmprintf(MSG_274, "ISOHybrid"), MB_OK | MB_ICONINFORMATION | MB_IS_RTL, selected_langid); uprintf("Note: DD image mode enforced since this ISOHybrid is not ISO mode compatible."); img_report.is_iso = FALSE; } selection_default = BT_IMAGE; } if (!IS_DD_BOOTABLE(img_report) && !IS_BIOS_BOOTABLE(img_report) && !IS_EFI_BOOTABLE(img_report)) { // No boot method that we support PrintInfo(0, MSG_081); safe_free(image_path); MessageBoxExU(hMainDialog, lmprintf(MSG_082), lmprintf(MSG_081), MB_OK | MB_ICONINFORMATION | MB_IS_RTL, selected_langid); PrintStatus(0, MSG_086); EnableControls(TRUE, FALSE); } else { if (!dont_display_image_name) { for (i = (int)safe_strlen(image_path); (i > 0) && (image_path[i] != '\\'); i--); if (i != 0) i++; short_image_path = &image_path[i]; PrintStatus(0, MSG_205, short_image_path); uprintf("Using image: %s (%s)", short_image_path, SizeToHumanReadable(img_report.image_size, FALSE, FALSE)); } UpdateImage(dont_display_image_name); ToggleImageOptions(); EnableControls(TRUE, FALSE); // Set Target and FS accordingly if (img_report.is_iso || img_report.is_windows_img) { IGNORE_RETVAL(ComboBox_SetCurSel(hBootType, image_index)); SetPartitionSchemeAndTargetSystem(FALSE); SetFileSystemAndClusterSize(NULL); SetFSFromISO(); user_changed_label = FALSE; SetProposedLabel(ComboBox_GetCurSel(hDeviceList)); } else { SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL << 16) | IDC_FILE_SYSTEM, ComboBox_GetCurSel(hFileSystem)); } // Lose the focus on the select ISO (but place it on Close) SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)FALSE, 0); // Lose the focus from Close and set it back to Start SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)hStart, TRUE); } // Need to invalidate as we may have changed the UI and may get artifacts if we don't // Oh and we need to invoke BOTH RedrawWindow() and InvalidateRect() because UI refresh // in the Microsoft worlds SUCKS!!!! (we may lose the disabled "Start" button otherwise) RedrawWindow(hMainDialog, NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); InvalidateRect(hMainDialog, NULL, TRUE); out: dont_display_image_name = FALSE; dont_process_dbt_devnodes = FALSE; PrintInfo(0, MSG_210); ExitThread(0); } #define MAP_BIT(bit) do { map[_log2(bit)] = b; b <<= 1; } while(0) // Likewise, boot check will block message processing => use a thread static DWORD WINAPI BootCheckThread(LPVOID param) { int i, r, username_index = -1; FILE *fd; DWORD len; WPARAM ret = BOOTCHECK_CANCEL; BOOL in_files_dir = FALSE, esp_already_asked = FALSE; BOOL is_windows_to_go = ((image_options & IMOP_WINTOGO) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO)); const char* grub = "grub"; const char* core_img = "core.img"; const char* ldlinux = "ldlinux"; const char* syslinux = "syslinux"; const char* ldlinux_ext[3] = { "sys", "bss", "c32" }; char tmp[MAX_PATH], tmp2[MAX_PATH], c; syslinux_ldlinux_len[0] = 0; syslinux_ldlinux_len[1] = 0; is_bootloader_revoked = FALSE; safe_free(grub2_buf); if (ComboBox_GetCurSel(hDeviceList) == CB_ERR) goto out; if ((zero_drive) || (boot_type == BT_NON_BOOTABLE)) { // Nothing to check ret = BOOTCHECK_PROCEED; goto out; } if (boot_type == BT_IMAGE) { assert(image_path != NULL); if (image_path == NULL) goto out; if ((size_check) && (img_report.projected_size > (uint64_t)SelectedDrive.DiskSize)) { // This ISO image is too big for the selected target MessageBoxExU(hMainDialog, lmprintf(MSG_089), lmprintf(MSG_088), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } if (IS_DD_BOOTABLE(img_report)) { if (!img_report.is_iso) { // Pure DD images are fine at this stage write_as_image = TRUE; } else if (persistence_size == 0) { // Ask users how they want to write ISOHybrid images, // but only do so if persistence has not been selected. char* iso_image = lmprintf(MSG_036); char* dd_image = lmprintf(MSG_095); // If the ISO is small enough to be written as an ESP and we are using GPT add the ISO → ESP option if ((img_report.projected_size < MAX_ISO_TO_ESP_SIZE) && HAS_REGULAR_EFI(img_report) && (partition_type == PARTITION_STYLE_GPT) && IS_FAT(fs_type)) { char* choices[3] = { lmprintf(MSG_276, iso_image), lmprintf(MSG_277, "ISO → ESP"), lmprintf(MSG_277, dd_image) }; i = SelectionDialog(lmprintf(MSG_274, "ISOHybrid"), lmprintf(MSG_275, iso_image, dd_image, iso_image, dd_image), choices, 3); if (i < 0) // Cancel goto out; write_as_esp = (i & 2); write_as_image = (i & 4); esp_already_asked = TRUE; } else { char* choices[2] = { lmprintf(MSG_276, iso_image), lmprintf(MSG_277, dd_image) }; i = SelectionDialog(lmprintf(MSG_274, "ISOHybrid"), lmprintf(MSG_275, iso_image, dd_image, iso_image, dd_image), choices, 2); if (i < 0) // Cancel goto out; write_as_image = (i & 2); } } } if (write_as_image) { ret = BOOTCHECK_PROCEED; goto out; } if (is_windows_to_go) { if (fs_type != FS_NTFS) { // Windows To Go only works for NTFS MessageBoxExU(hMainDialog, lmprintf(MSG_097, "Windows To Go"), lmprintf(MSG_092), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } if (SelectedDrive.MediaType != FixedMedia) { if ((target_type == TT_UEFI) && (partition_type == PARTITION_STYLE_GPT) && (WindowsVersion.BuildNumber < 15000)) { // Up to Windows 10 Creators Update (1703), we were screwed, since we need access to 2 partitions at the same time. // Thankfully, the newer Windows allow mounting multiple partitions on the same REMOVABLE drive. MessageBoxExU(hMainDialog, lmprintf(MSG_198), lmprintf(MSG_190), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } } // If multiple versions are available, asks the user to select one before we commit to format the drive switch(SetWinToGoIndex()) { case -1: MessageBoxExU(hMainDialog, lmprintf(MSG_073), lmprintf(MSG_291), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); // fall through case -2: goto out; default: break; } if ((WindowsVersion.Version >= WINDOWS_8) && IS_WINDOWS_1X(img_report)) { StrArray options; int arch = _log2(img_report.has_efi >> 1); uint16_t map[16] = { 0 }, b = 1; StrArrayCreate(&options, 8); StrArrayAdd(&options, lmprintf(MSG_332), TRUE); MAP_BIT(UNATTEND_OFFLINE_INTERNAL_DRIVES); if (img_report.win_version.build >= 22500) { StrArrayAdd(&options, lmprintf(MSG_330), TRUE); MAP_BIT(UNATTEND_NO_ONLINE_ACCOUNT); } StrArrayAdd(&options, lmprintf(MSG_333), TRUE); username_index = _log2(b); MAP_BIT(UNATTEND_SET_USER); StrArrayAdd(&options, lmprintf(MSG_334), TRUE); MAP_BIT(UNATTEND_DUPLICATE_LOCALE); StrArrayAdd(&options, lmprintf(MSG_331), TRUE); MAP_BIT(UNATTEND_NO_DATA_COLLECTION); if (expert_mode) { StrArrayAdd(&options, lmprintf(MSG_346), TRUE); MAP_BIT(UNATTEND_FORCE_S_MODE); } i = CustomSelectionDialog(BS_AUTOCHECKBOX, lmprintf(MSG_327), lmprintf(MSG_328), options.String, options.Index, remap16(unattend_xml_mask, map, FALSE), username_index); StrArrayDestroy(&options); if (i < 0) goto out; // Remap i to the correct bit positions before calling CreateUnattendXml() i = remap16(i, map, TRUE); unattend_xml_path = CreateUnattendXml(arch, i | UNATTEND_WINDOWS_TO_GO); // Keep the bits we didn't process unattend_xml_mask &= ~(remap16(0x1ff, map, TRUE)); // And add back the bits we did process unattend_xml_mask |= i; WriteSetting32(SETTING_WUE_OPTIONS, (UNATTEND_DEFAULT_MASK << 16) | unattend_xml_mask); } } else if (target_type == TT_UEFI) { if (!IS_EFI_BOOTABLE(img_report)) { // Unsupported ISO MessageBoxExU(hMainDialog, lmprintf(MSG_091), lmprintf(MSG_090), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } if (HAS_WIN7_EFI(img_report) && (!WimExtractCheck(FALSE))) { // Your platform cannot extract files from WIM archives => download 7-zip? if (MessageBoxExU(hMainDialog, lmprintf(MSG_102), lmprintf(MSG_101), MB_YESNO | MB_ICONERROR | MB_IS_RTL, selected_langid) == IDYES) ShellExecuteA(hMainDialog, "open", SEVENZIP_URL, NULL, NULL, SW_SHOWNORMAL); goto out; } } else if ( ((fs_type == FS_NTFS) && !HAS_WINDOWS(img_report) && !HAS_GRUB(img_report) && (!HAS_SYSLINUX(img_report) || (SL_MAJOR(img_report.sl_version) <= 5))) || ((IS_FAT(fs_type)) && (!HAS_SYSLINUX(img_report)) && (!allow_dual_uefi_bios) && !IS_EFI_BOOTABLE(img_report) && (!HAS_REACTOS(img_report)) && !HAS_KOLIBRIOS(img_report) && (!HAS_GRUB(img_report))) || ((IS_FAT(fs_type)) && (HAS_WINDOWS(img_report) || HAS_WININST(img_report)) && (!allow_dual_uefi_bios)) ) { // Incompatible FS and ISO MessageBoxExU(hMainDialog, lmprintf(MSG_096), lmprintf(MSG_092), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } else if ((fs_type == FS_FAT16) && HAS_KOLIBRIOS(img_report)) { // KolibriOS doesn't support FAT16 MessageBoxExU(hMainDialog, lmprintf(MSG_189), lmprintf(MSG_099), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } if ((IS_FAT(fs_type)) && (img_report.has_4GB_file)) { // This ISO image contains a file larger than 4GB file (FAT32) MessageBoxExU(hMainDialog, lmprintf(MSG_100), lmprintf(MSG_099), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } if ((WindowsVersion.Version >= WINDOWS_8) && IS_WINDOWS_1X(img_report) && (!is_windows_to_go)) { if (img_report.has_panther_unattend) { uprintf("NOTICE: A '/sources/$OEM$/$$/Panther/unattend.xml' was detected on the ISO."); uprintf("As a result, the 'Windows User Experience dialog' will not be displayed."); } else { StrArray options; int arch = _log2(img_report.has_efi >> 1); uint16_t map[16] = { 0 }, b = 1; StrArrayCreate(&options, 10); if (IS_WINDOWS_11(img_report)) { StrArrayAdd(&options, lmprintf(MSG_329), TRUE); MAP_BIT(UNATTEND_SECUREBOOT_TPM_MINRAM); } if (img_report.win_version.build >= 22500) { StrArrayAdd(&options, lmprintf(MSG_330), TRUE); MAP_BIT(UNATTEND_NO_ONLINE_ACCOUNT); } StrArrayAdd(&options, lmprintf(MSG_333), TRUE); username_index = _log2(b); MAP_BIT(UNATTEND_SET_USER); StrArrayAdd(&options, lmprintf(MSG_334), TRUE); MAP_BIT(UNATTEND_DUPLICATE_LOCALE); StrArrayAdd(&options, lmprintf(MSG_331), TRUE); MAP_BIT(UNATTEND_NO_DATA_COLLECTION); StrArrayAdd(&options, lmprintf(MSG_335), TRUE); MAP_BIT(UNATTEND_DISABLE_BITLOCKER); if (expert_mode) { StrArrayAdd(&options, lmprintf(MSG_346), TRUE); MAP_BIT(UNATTEND_FORCE_S_MODE); } i = CustomSelectionDialog(BS_AUTOCHECKBOX, lmprintf(MSG_327), lmprintf(MSG_328), options.String, options.Index, remap16(unattend_xml_mask, map, FALSE), username_index); StrArrayDestroy(&options); if (i < 0) goto out; i = remap16(i, map, TRUE); unattend_xml_path = CreateUnattendXml(arch, i); // Remember the user preferences for the current session. unattend_xml_mask &= ~(remap16(0x1ff, map, TRUE)); unattend_xml_mask |= i; WriteSetting32(SETTING_WUE_OPTIONS, (UNATTEND_DEFAULT_MASK << 16) | unattend_xml_mask); } } // Check UEFI bootloaders for revocation if (IS_EFI_BOOTABLE(img_report)) { // coverity[swapped_arguments] if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, tmp) != 0) { // ExtractISOFile() is case sensitive, so we must use for (i = 0; i < ARRAYSIZE(img_report.efi_boot_path) && img_report.efi_boot_path[i][0] != 0; i++) { if (ExtractISOFile(image_path, img_report.efi_boot_path[i], tmp, FILE_ATTRIBUTE_NORMAL) == 0) { uprintf("Warning: Failed to extract '%s' to check for UEFI revocation", img_report.efi_boot_path[i]); continue; } r = IsBootloaderRevoked(tmp); if (r > 0) { is_bootloader_revoked = TRUE; r = MessageBoxExU(hMainDialog, lmprintf(MSG_339, (r == 1) ? lmprintf(MSG_340) : lmprintf(MSG_341, "Error code: 0xc0000428")), lmprintf(MSG_338), MB_OKCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid); if (r == IDCANCEL) goto out; break; } } DeleteFileU(tmp); } } if ((img_report.projected_size < MAX_ISO_TO_ESP_SIZE) && HAS_REGULAR_EFI(img_report) && (partition_type == PARTITION_STYLE_GPT) && IS_FAT(fs_type) && !esp_already_asked) { // The ISO is small enough to be written as an ESP and we are using GPT // so ask the users if they want to write it as an ESP. char* iso_image = lmprintf(MSG_036); char* choices[2] = { lmprintf(MSG_276, iso_image), lmprintf(MSG_277, "ISO → ESP") }; i = SelectionDialog(lmprintf(MSG_274, "ESP"), lmprintf(MSG_310), choices, 2); if (i < 0) // Cancel goto out; write_as_esp = (i & 2); } // If the selected target doesn't include BIOS, skip file downloads for GRUB/Syslinux if (target_type != TT_BIOS) goto uefi_target; if ((partition_type == PARTITION_STYLE_MBR) && (img_report.has_grub2) && (img_report.grub2_version[0] != 0) && (strcmp(img_report.grub2_version, GRUB2_PACKAGE_VERSION) != 0)) { // We may have to download a different Grub2 version if we can find one IGNORE_RETVAL(_chdirU(app_data_dir)); IGNORE_RETVAL(_mkdir(FILES_DIR)); IGNORE_RETVAL(_chdir(FILES_DIR)); static_sprintf(tmp, "%s-%s/%s", grub, img_report.grub2_version, core_img); fd = fopen(tmp, "rb"); if (fd != NULL) { // If a file already exists in the current directory, use that one to_windows_path(tmp); uprintf("Will reuse '%s' from '%s\\%s\\%s-%s\\' for Grub 2.x installation", core_img, app_data_dir, FILES_DIR, grub, img_report.grub2_version); fseek(fd, 0, SEEK_END); grub2_len = ftell(fd); fseek(fd, 0, SEEK_SET); if (grub2_len > 0) grub2_buf = malloc(grub2_len); // grub2_buf was set to NULL at the beginning of this call if ((grub2_buf == NULL) || (fread(grub2_buf, 1, (size_t)grub2_len, fd) != (size_t)grub2_len)) { uprintf("Failed to read existing '%s' data - will use embedded version", core_img); safe_free(grub2_buf); } fclose(fd); } else { r = MessageBoxExU(hMainDialog, lmprintf(MSG_116, img_report.grub2_version, GRUB2_PACKAGE_VERSION), lmprintf(MSG_115), MB_YESNOCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid); if (r == IDCANCEL) goto out; else if (r == IDYES) { static_sprintf(tmp, "%s-%s", grub, img_report.grub2_version); IGNORE_RETVAL(_mkdir(tmp)); IGNORE_RETVAL(_chdir(tmp)); // The following loops through the grub2 version (which may have the ISO label appended) // and breaks it according to '.' or '-' until it finds a match on the server. // for (i = (int)strlen(img_report.grub2_version), grub2_len = 0; i > 0 && grub2_len <= 0; i--) { c = img_report.grub2_version[i]; if (c != 0 && c != '.' && c != '-') continue; img_report.grub2_version[i] = 0; static_sprintf(tmp, "%s/%s-%s/%s", FILES_URL, grub, img_report.grub2_version, core_img); grub2_len = (long)DownloadSignedFile(tmp, core_img, hMainDialog, FALSE); img_report.grub2_version[i] = c; } if (grub2_len <= 0) { PrintInfo(0, MSG_195, "Grub2"); uprintf("%s was not found - will use embedded version", tmp); } else { PrintInfo(0, MSG_193, tmp); fd = fopen(core_img, "rb"); grub2_buf = malloc(grub2_len); if ((fd == NULL) || (grub2_buf == NULL) || (fread(grub2_buf, 1, (size_t)grub2_len, fd) != (size_t)grub2_len)) { uprintf("Failed to read '%s' data - will use embedded version", core_img); safe_free(grub2_buf); } if (fd != NULL) fclose(fd); } } } } if ((partition_type == PARTITION_STYLE_MBR) && HAS_SYSLINUX(img_report)) { if (SL_MAJOR(img_report.sl_version) < 5) { IGNORE_RETVAL(_chdirU(app_data_dir)); for (i = 0; i try to download without the version extra uprintf("Extended version was not found, trying main version..."); static_sprintf(tmp, "%s/%s-%s/%s.%s", FILES_URL, syslinux, img_report.sl_version_str, ldlinux, ldlinux_ext[i]); syslinux_ldlinux_len[i] = DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog, (*img_report.sl_version_ext == 0)); if (syslinux_ldlinux_len[i] != 0) { // Duplicate the file so that the user won't be prompted to download again static_sprintf(tmp, "%s-%s\\%s.%s", syslinux, img_report.sl_version_str, ldlinux, ldlinux_ext[i]); static_sprintf(tmp2, "%s-%s\\%s\\%s.%s", syslinux, img_report.sl_version_str, &img_report.sl_version_ext[1], ldlinux, ldlinux_ext[i]); CopyFileA(tmp, tmp2, FALSE); } } if (syslinux_ldlinux_len[i] == 0) { // If the version matches our embedded one, try to use that as a last ditch effort if (img_report.sl_version == embedded_sl_version[1]) { uprintf("Could not download the file - will try to use embedded %s version instead", img_report.sl_version_str); } else { uprintf("Could not download the file - cancelling"); ret = BOOTCHECK_DOWNLOAD_ERROR; goto out; } } } } } } } else if (boot_type == BT_SYSLINUX_V6) { IGNORE_RETVAL(_chdirU(app_data_dir)); IGNORE_RETVAL(_mkdir(FILES_DIR)); IGNORE_RETVAL(_chdir(FILES_DIR)); static_sprintf(tmp, "%s-%s/%s.%s", syslinux, embedded_sl_version_str[1], ldlinux, ldlinux_ext[2]); fd = fopenU(tmp, "rb"); if (fd != NULL) { to_windows_path(tmp); uprintf("Will reuse '%s\\%s\\%s' for Syslinux installation", app_data_dir, FILES_DIR, tmp); fclose(fd); } else { static_sprintf(tmp, "%s.%s", ldlinux, ldlinux_ext[2]); PrintInfo(0, MSG_206, tmp); // MSG_104: "Syslinux v5.0 or later requires a '%s' file to be installed" r = MessageBoxExU(hMainDialog, lmprintf(MSG_104, "Syslinux v5.0", tmp, "Syslinux v5+", tmp), lmprintf(MSG_103, tmp), MB_YESNOCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid); if (r == IDCANCEL) goto out; if (r == IDYES) { static_sprintf(tmp, "%s-%s", syslinux, embedded_sl_version_str[1]); IGNORE_RETVAL(_mkdir(tmp)); static_sprintf(tmp, "%s/%s-%s/%s.%s", FILES_URL, syslinux, embedded_sl_version_str[1], ldlinux, ldlinux_ext[2]); if (DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog, TRUE) == 0) { ret = BOOTCHECK_DOWNLOAD_ERROR; goto out; } } } } else if (boot_type == BT_MSDOS) { if ((size_check) && (ComboBox_GetCurItemData(hClusterSize) >= 65536)) { // MS-DOS cannot boot from a drive using a 64 kilobytes Cluster size MessageBoxExU(hMainDialog, lmprintf(MSG_110), lmprintf(MSG_111), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid); goto out; } static_sprintf(tmp, "%s\\%s\\diskcopy.dll", app_data_dir, FILES_DIR); if (_accessU(tmp, 0) != -1) { uprintf("Will reuse '%s' for MS-DOS installation", tmp); } else { r = MessageBoxExU(hMainDialog, lmprintf(MSG_337), lmprintf(MSG_115), MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid); if (r != IDYES) goto out; IGNORE_RETVAL(_chdirU(app_data_dir)); IGNORE_RETVAL(_mkdir(FILES_DIR)); IGNORE_RETVAL(_chdir(FILES_DIR)); if (DownloadToFileOrBufferEx(DISKCOPY_URL, tmp, SYMBOL_SERVER_USER_AGENT, NULL, hMainDialog, FALSE) != DISKCOPY_SIZE) { ret = BOOTCHECK_DOWNLOAD_ERROR; goto out; } } } else if (boot_type == BT_GRUB4DOS) { IGNORE_RETVAL(_chdirU(app_data_dir)); IGNORE_RETVAL(_mkdir(FILES_DIR)); IGNORE_RETVAL(_chdir(FILES_DIR)); static_sprintf(tmp, "grub4dos-%s/grldr", GRUB4DOS_VERSION); fd = fopenU(tmp, "rb"); if (fd != NULL) { to_windows_path(tmp); uprintf("Will reuse '%s\\%s\\%s' for Grub4DOS installation", app_data_dir, FILES_DIR, tmp); fclose(fd); } else { static_sprintf(tmp, "grldr"); PrintInfo(0, MSG_206, tmp); r = MessageBoxExU(hMainDialog, lmprintf(MSG_104, "Grub4DOS 0.4", tmp, "Grub4DOS", tmp), lmprintf(MSG_103, tmp), MB_YESNOCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid); if (r == IDCANCEL) goto out; if (r == IDYES) { static_sprintf(tmp, "grub4dos-%s", GRUB4DOS_VERSION); IGNORE_RETVAL(_mkdir(tmp)); static_sprintf(tmp, "%s/grub4dos-%s/grldr", FILES_URL, GRUB4DOS_VERSION); if (DownloadSignedFile(tmp, &tmp[sizeof(FILES_URL)], hMainDialog, TRUE) == 0) { ret = BOOTCHECK_DOWNLOAD_ERROR; goto out; } } } } uefi_target: if (boot_type == BT_UEFI_NTFS) { fs_type = (int)ComboBox_GetCurItemData(hFileSystem); if (fs_type != FS_NTFS && fs_type != FS_EXFAT) { MessageBoxExU(hMainDialog, lmprintf(MSG_097, "UEFI:NTFS"), lmprintf(MSG_092), MB_OK|MB_ICONERROR|MB_IS_RTL, selected_langid); goto out; } } ret = BOOTCHECK_PROCEED; out: PostMessage(hMainDialog, UM_FORMAT_START, ret, 0); ExitThread((DWORD)ret); } static __inline const char* IsAlphaOrBeta(void) { #if defined(ALPHA) return " (Alpha) "; #elif defined(BETA) return " (Beta) "; #elif defined(TEST) # define TEST_STR(x) " (Test" STRINGIFY(x) ") " return TEST_STR(TEST); #else return " "; #endif } static void InitDialog(HWND hDlg) { DWORD len; HDC hDC; USHORT ProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN, NativeMachine = IMAGE_FILE_MACHINE_UNKNOWN; int i, lfHeight; char tmp[128], *token, *buf, *ext, *msg; static char* resource[2] = { MAKEINTRESOURCEA(IDR_SL_LDLINUX_V4_SYS), MAKEINTRESOURCEA(IDR_SL_LDLINUX_V6_SYS) }; PF_TYPE_DECL(WINAPI, BOOL, IsWow64Process2, (HANDLE, USHORT*, USHORT*)); PF_INIT(IsWow64Process2, Kernel32); #ifdef RUFUS_TEST ShowWindow(GetDlgItem(hDlg, IDC_TEST), SW_SHOW); #endif hDC = GetDC(hDlg); lfHeight = -MulDiv(9, GetDeviceCaps(hDC, LOGPIXELSY), 72); safe_release_dc(hDlg, hDC); // Quite a burden to carry around as parameters hMainDialog = hDlg; MainThreadId = GetCurrentThreadId(); hDeviceList = GetDlgItem(hDlg, IDC_DEVICE); hPartitionScheme = GetDlgItem(hDlg, IDC_PARTITION_TYPE); hTargetSystem = GetDlgItem(hDlg, IDC_TARGET_SYSTEM); hFileSystem = GetDlgItem(hDlg, IDC_FILE_SYSTEM); hClusterSize = GetDlgItem(hDlg, IDC_CLUSTER_SIZE); hLabel = GetDlgItem(hDlg, IDC_LABEL); hProgress = GetDlgItem(hDlg, IDC_PROGRESS); hBootType = GetDlgItem(hDlg, IDC_BOOT_SELECTION); hImageOption = GetDlgItem(hDlg, IDC_IMAGE_OPTION); hSelectImage = GetDlgItem(hDlg, IDC_SELECT); hNBPasses = GetDlgItem(hDlg, IDC_NB_PASSES); hStart = GetDlgItem(hDlg, IDC_START); // Convert the main button labels to uppercase GetWindowTextU(hStart, uppercase_start, sizeof(uppercase_start)); CharUpperBuffU(uppercase_start, sizeof(uppercase_start)); SetWindowTextU(hStart, uppercase_start); GetWindowTextU(GetDlgItem(hDlg, IDCANCEL), uppercase_close, sizeof(uppercase_close)); CharUpperBuffU(uppercase_close, sizeof(uppercase_close)); // Hardcoded exception for German if (strcmp("SCHLIEßEN", uppercase_close) == 0) strcpy(uppercase_close, "SCHLIESSEN"); SetWindowTextU(GetDlgItem(hDlg, IDCANCEL), uppercase_close); GetWindowTextU(GetDlgItem(hDlg, IDC_SELECT), uppercase_select[0], sizeof(uppercase_select[0])); static_strcpy(uppercase_select[1], lmprintf(MSG_040)); CharUpperBuffU(uppercase_select[0], sizeof(uppercase_select[0])); CharUpperBuffU(uppercase_select[1], sizeof(uppercase_select[1])); SetWindowTextU(GetDlgItem(hDlg, IDC_SELECT), uppercase_select[0]); static_strcpy(uppercase_cancel, lmprintf(MSG_007)); CharUpperBuffU(uppercase_cancel, sizeof(uppercase_cancel)); CreateSmallButtons(hDlg); GetBasicControlsWidth(hDlg); GetMainButtonsWidth(hDlg); GetHalfDropwdownWidth(hDlg); GetFullWidth(hDlg); // Set some missing labels SetAccessibleName(hNBPasses, lmprintf(MSG_316)); // Create the font and brush for the progress messages hInfoFont = CreateFontA(lfHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, PROOF_QUALITY, 0, "Segoe UI"); // Create the title bar icon SetTitleBarIcon(hDlg); GetWindowTextA(hDlg, tmp, sizeof(tmp)); // Count on Microsoft for making it more attractive to read a // version using strtok() than using GetFileVersionInfo() token = strtok(tmp, " "); for (i = 0; (i < 3) && ((token = strtok(NULL, ".")) != NULL); i++) rufus_version[i] = (uint16_t)atoi(token); // Redefine the title to be able to add "Alpha" or "Beta" static_sprintf(tmp, APPLICATION_NAME " %d.%d.%d%s%s", rufus_version[0], rufus_version[1], rufus_version[2], IsAlphaOrBeta(), (ini_file != NULL)?"(Portable)":""); SetWindowTextU(hDlg, tmp); // Now that we have a title, we can find the handle of our Dialog dialog_handle = FindWindowA(NULL, tmp); // Add a timestamp in persistent log mode if (persistent_log) { __time64_t ltime; char timestamp[32] = "["; _time64(<ime); if (_ctime64_s(×tamp[1], sizeof(timestamp) - 2, <ime) == 0) { // Windows' _ctime64_s adds a \n at the end - replace it timestamp[strlen(timestamp) - 1] = ']'; uprintf(timestamp); } } uprintf(APPLICATION_NAME " " APPLICATION_ARCH " v%d.%d.%d%s%s", rufus_version[0], rufus_version[1], rufus_version[2], IsAlphaOrBeta(), (ini_file != NULL)?"(Portable)": (appstore_version ? "(AppStore version)" : "")); // Display a notice if running x86 emulation on ARM // Oh, and https://devblogs.microsoft.com/oldnewthing/20220209-00/?p=106239 is *WRONG*: // Get­Native­System­Info() will not tell you what the native system architecture is when // running x86 code on ARM64. Instead you have to use IsWow64Process2(), which is only // available on Windows 10 1709 or later... if ((pfIsWow64Process2 != NULL) && pfIsWow64Process2(GetCurrentProcess(), &ProcessMachine, &NativeMachine)) { // x64 running emulated on ARM64 returns IMAGE_FILE_MACHINE_UNKNOWN for ProcessMachine // because Microsoft does not consider it a WOW64 process. So we need to fix // ProcessMachine ourselves to *ACTUALLY* return the bloody process machine... if (ProcessMachine == IMAGE_FILE_MACHINE_UNKNOWN) ProcessMachine = GetApplicationArch(); if ((NativeMachine == IMAGE_FILE_MACHINE_ARM || NativeMachine == IMAGE_FILE_MACHINE_ARM64) && (ProcessMachine == IMAGE_FILE_MACHINE_I386 || ProcessMachine == IMAGE_FILE_MACHINE_AMD64)) uprintf("Notice: Running emulated on %s platform", GetArchName(NativeMachine)); } for (i = 0; i < ARRAYSIZE(resource); i++) { len = 0; buf = (char*)GetResource(hMainInstance, resource[i], _RT_RCDATA, "ldlinux_sys", &len, TRUE); if (buf == NULL) { uprintf("Warning: could not read embedded Syslinux v%d version", i + 4); } else { embedded_sl_version[i] = GetSyslinuxVersion(buf, len, &ext); static_sprintf(embedded_sl_version_str[i], "%d.%02d", SL_MAJOR(embedded_sl_version[i]), SL_MINOR(embedded_sl_version[i])); static_strcpy(embedded_sl_version_ext[i], ext); free(buf); } } uprintf("Windows version: %s", WindowsVersion.VersionStr); uprintf("Syslinux versions: %s%s, %s%s", embedded_sl_version_str[0], embedded_sl_version_ext[0], embedded_sl_version_str[1], embedded_sl_version_ext[1]); uprintf("Grub versions: %s, %s", GRUB4DOS_VERSION, GRUB2_PACKAGE_VERSION); uprintf("System locale ID: 0x%04X (%s)", GetUserDefaultUILanguage(), ToLocaleName(GetUserDefaultUILanguage())); ubflush(); if (selected_locale->ctrl_id & LOC_NEEDS_UPDATE) { uprintf("NOTE: The %s translation requires an update, but the current translator hasn't submitted " "one. Because of this, some messages will only be displayed in English.", selected_locale->txt[1]); uprintf("If you think you can help update this translation, please e-mail the author of this application"); } PrintRevokedBootloaderInfo(); // Detect and report system limitations if (ReadRegistryKeyBool(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Policies\\Microsoft\\FVE")) uprintf("WARNING: This system has a policy set to prevent write access to FIXED drives not using BitLocker"); if (ReadRegistryKeyBool(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\StorageDevicePolicies\\WriteProtect")) uprintf("WARNING: This system has a policy set to prevent write access to storage devices"); if (!is_vds_available) uprintf("Notice: Windows VDS is unavailable"); CreateTaskbarList(); SetTaskbarProgressState(TASKBAR_NORMAL); // Use maximum granularity for the progress bar SendMessage(hProgress, PBM_SETRANGE, 0, (MAX_PROGRESS<<16) & 0xFFFF0000); // Fill up the passes for (i = 1; i <= 5; i++) { msg = (i == 1) ? lmprintf(MSG_034, 1) : lmprintf(MSG_035, (i == 2) ? 2 : 4, (i == 2) ? "" : lmprintf(MSG_087, flash_type[i - 3])); IGNORE_RETVAL(ComboBox_AddStringU(hNBPasses, msg)); } IGNORE_RETVAL(ComboBox_SetCurSel(hNBPasses, 0)); SetPassesTooltip(); // Fill up the boot options dropdown SetBootOptions(); // Create the string arrays StrArrayCreate(&BlockingProcessList, 16); StrArrayCreate(&ImageList, 16); // Set various checkboxes CheckDlgButton(hDlg, IDC_QUICK_FORMAT, BST_CHECKED); CheckDlgButton(hDlg, IDC_EXTENDED_LABEL, BST_CHECKED); CreateAdditionalControls(hDlg); SetSectionHeaders(hDlg); PositionMainControls(hDlg); AdjustForLowDPI(hDlg); // Because we created the log dialog before we computed our sizes, we need to send a custom message SendMessage(hLogDialog, UM_RESIZE_BUTTONS, 0, 0); // Limit the amount of characters for the Persistence size field SendMessage(GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE), EM_LIMITTEXT, 7, 0); // Create the status line and initialize the taskbar icon for progress overlay CreateStatusBar(); // Set the various tooltips CreateTooltip(hFileSystem, lmprintf(MSG_157), -1); CreateTooltip(hClusterSize, lmprintf(MSG_158), -1); CreateTooltip(hLabel, lmprintf(MSG_159), -1); CreateTooltip(hAdvancedDeviceToolbar, lmprintf(MSG_160), -1); CreateTooltip(hAdvancedFormatToolbar, lmprintf(MSG_160), -1); CreateTooltip(GetDlgItem(hDlg, IDC_BAD_BLOCKS), lmprintf(MSG_161), -1); CreateTooltip(GetDlgItem(hDlg, IDC_QUICK_FORMAT), lmprintf(MSG_162), -1); CreateTooltip(hBootType, lmprintf(MSG_164), -1); CreateTooltip(hSelectImage, lmprintf(MSG_165), -1); CreateTooltip(GetDlgItem(hDlg, IDC_EXTENDED_LABEL), lmprintf(MSG_166), 10000); CreateTooltip(GetDlgItem(hDlg, IDC_UEFI_MEDIA_VALIDATION), lmprintf(MSG_167), 10000); CreateTooltip(GetDlgItem(hDlg, IDC_OLD_BIOS_FIXES), lmprintf(MSG_169), -1); CreateTooltip(GetDlgItem(hDlg, IDC_LIST_USB_HDD), lmprintf(MSG_170), -1); CreateTooltip(hStart, lmprintf(MSG_171), -1); CreateTooltip(hPartitionScheme, lmprintf(MSG_163), -1); CreateTooltip(hTargetSystem, lmprintf(MSG_150), 30000); CreateTooltip(GetDlgItem(hDlg, IDS_CSM_HELP_TXT), lmprintf(MSG_151), 30000); CreateTooltip(hImageOption, lmprintf(MSG_305), 30000); CreateTooltip(GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER), lmprintf(MSG_125), 30000); CreateTooltip(GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE), lmprintf(MSG_125), 30000); CreateTooltip(GetDlgItem(hDlg, IDC_PERSISTENCE_UNITS), lmprintf(MSG_126), 30000); if (!advanced_mode_device) // Hide as needed, since we display the advanced controls by default ToggleAdvancedDeviceOptions(FALSE); if (!advanced_mode_format) ToggleAdvancedFormatOptions(FALSE); ToggleImageOptions(); // Process commandline parameters if (img_provided) { // Simulate a button click for image selection PostMessage(hDlg, WM_COMMAND, IDC_SELECT, 0); } SetBootTypeDropdownWidth(); CheckDlgButton(hMainDialog, IDC_LIST_USB_HDD, enable_HDDs ? BST_CHECKED : BST_UNCHECKED); PrintInfo(0, MSG_210); } static void PrintStatusTimeout(const char* str, BOOL val) { PrintStatus(STATUS_MSG_TIMEOUT, (val)?MSG_250:MSG_251, str); } /* * Main dialog callback */ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { static DWORD DeviceNum = 0; static uint64_t LastRefresh = 0; static BOOL first_log_display = TRUE, isMarquee = FALSE, queued_hotplug_event = FALSE; static ULONG ulRegister = 0; static LPITEMIDLIST pidlDesktop = NULL; static SHChangeNotifyEntry NotifyEntry; static DWORD_PTR thread_affinity[HASH_MAX + 1]; static HFONT hyperlink_font = NULL; static wchar_t wtooltip[128]; LONG lPos; BOOL set_selected_fs; DRAWITEMSTRUCT* pDI; LPTOOLTIPTEXT lpttt; NMBCDROPDOWN* pDropDown; HDROP droppedFileInfo; HMENU hMenu; POINT Point; RECT rc, DialogRect, DesktopRect; HDC hDC; PAINTSTRUCT ps; DWORD log_size; int nDeviceIndex, i, nWidth, nHeight, nb_devices, selected_language, offset, tb_state, tb_flags; char tmp[MAX_PATH], *log_buffer = NULL; wchar_t* wbuffer = NULL; loc_cmd* lcmd = NULL; switch (message) { case WM_COMMAND: #ifdef RUFUS_TEST if (LOWORD(wParam) == IDC_TEST) { break; } #endif if ((LOWORD(wParam) >= UM_LANGUAGE_MENU) && (LOWORD(wParam) < UM_LANGUAGE_MENU_MAX)) { selected_language = LOWORD(wParam) - UM_LANGUAGE_MENU; i = 0; list_for_each_entry(lcmd, &locale_list, loc_cmd, list) { if (i++ == selected_language) { if (selected_locale != lcmd) { selected_locale = lcmd; selected_langid = get_language_id(lcmd); // Avoid the FS being reset on language change selected_fs = (int)ComboBox_GetCurItemData(hFileSystem); relaunch = TRUE; PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0); } break; } } } switch(LOWORD(wParam)) { case IDOK: // close application case IDCANCEL: EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE); if (format_thread != NULL) { if ((no_confirmation_on_cancel) || (MessageBoxExU(hMainDialog, lmprintf(MSG_105), lmprintf(MSG_049), MB_YESNO|MB_ICONWARNING|MB_IS_RTL, selected_langid) == IDYES)) { // Operation may have completed in the meantime if (format_thread != NULL) { ErrorStatus = RUFUS_ERROR(ERROR_CANCELLED); PrintInfo(0, MSG_201); uprintf("Cancelling"); // Start a timer to detect blocking operations during ISO file extraction if (iso_blocking_status >= 0) { last_iso_blocking_status = iso_blocking_status; SetTimer(hMainDialog, TID_BLOCKING_TIMER, 3000, BlockingTimer); } } } else { EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE); } no_confirmation_on_cancel = FALSE; return (INT_PTR)TRUE; } else if (op_in_progress) { // User might be trying to cancel during preliminary checks ErrorStatus = RUFUS_ERROR(ERROR_CANCELLED); PrintInfo(0, MSG_201); EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE); return (INT_PTR)TRUE; } // Save or append the current log to %LocalAppData%\Rufus\rufus.log log_size = GetWindowTextLengthU(hLog); if ((!user_deleted_rufus_dir) && (log_size > 0) && ((log_buffer = (char*)malloc(log_size + 2)) != NULL)) { log_size = GetDlgItemTextU(hLogDialog, IDC_LOG_EDIT, log_buffer, log_size); if (log_size-- > 1) { if (persistent_log) { // Add an extra line for persistent logs log_buffer[log_size - 1] = '\r'; log_buffer[log_size] = '\n'; } IGNORE_RETVAL(_chdirU(app_data_dir)); IGNORE_RETVAL(_mkdir(FILES_DIR)); IGNORE_RETVAL(_chdir(FILES_DIR)); FileIO(persistent_log ? FILE_IO_APPEND : FILE_IO_WRITE, "rufus.log", &log_buffer, &log_size); } safe_free(log_buffer); } if (ulRegister != 0) SHChangeNotifyDeregister(ulRegister); PostQuitMessage(0); ClearDrives(); StopProcessSearch(); StrArrayDestroy(&BlockingProcessList); StrArrayDestroy(&ImageList); DestroyAllTooltips(); DestroyWindow(hLogDialog); GetWindowRect(hDlg, &relaunch_rc); EndDialog(hDlg, 0); break; case IDC_ABOUT: CreateAboutBox(); break; case IDC_LOG: // Place the log Window to the right (or left for RTL) of our dialog on first display if (first_log_display) { GetClientRect(GetDesktopWindow(), &DesktopRect); GetWindowRect(hLogDialog, &DialogRect); nWidth = DialogRect.right - DialogRect.left; nHeight = DialogRect.bottom - DialogRect.top; GetWindowRect(hDlg, &DialogRect); offset = GetSystemMetrics(SM_CXBORDER); if (WindowsVersion.Version >= WINDOWS_10) { // See https://stackoverflow.com/a/42491227/1069307 // I agree with Stephen Hazel: Whoever at Microsoft thought it would be a great idea to // add a *FRIGGING INVISIBLE BORDER* in Windows 10 should face the harshest punishment! // Also calling this API will create DLL sideloading issues through 'dwmapi.dll' so make // sure you delay-load it in your application. DwmGetWindowAttribute(hDlg, DWMWA_EXTENDED_FRAME_BOUNDS, &rc, sizeof(RECT)); offset += 2 * (DialogRect.left - rc.left); } if (right_to_left_mode) Point.x = max(DialogRect.left - offset - nWidth, 0); else Point.x = min(DialogRect.right + offset, DesktopRect.right - nWidth); Point.y = max(DialogRect.top, DesktopRect.top - nHeight); MoveWindow(hLogDialog, Point.x, Point.y, nWidth, nHeight, FALSE); // The log may have been recentered to fit the screen, in which case, try to shift our main dialog left (or right for RTL) nWidth = DialogRect.right - DialogRect.left; nHeight = DialogRect.bottom - DialogRect.top; if (right_to_left_mode) { Point.x = DialogRect.left; GetWindowRect(hLogDialog, &DialogRect); Point.x = max(Point.x, DialogRect.right - DialogRect.left + offset); } else { Point.x = max((DialogRect.left<0)?DialogRect.left:0, Point.x - offset - nWidth); } MoveWindow(hDlg, Point.x, Point.y, nWidth, nHeight, TRUE); first_log_display = FALSE; } // Display the log Window log_displayed = !log_displayed; // Set focus on the start button SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)FALSE, 0); SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)hStart, TRUE); // Must come last for the log window to get focus ShowWindow(hLogDialog, log_displayed?SW_SHOW:SW_HIDE); break; case IDC_ADVANCED_DRIVE_PROPERTIES: advanced_mode_device = !advanced_mode_device; WriteSettingBool(SETTING_ADVANCED_MODE_DEVICE, advanced_mode_device); ToggleAdvancedDeviceOptions(advanced_mode_device); SetBootOptions(); boot_type = (int)ComboBox_GetCurItemData(hBootType); EnableControls(TRUE, FALSE); SetFileSystemAndClusterSize(NULL); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL<<16) | IDC_FILE_SYSTEM, ComboBox_GetCurSel(hFileSystem)); break; case IDC_ADVANCED_FORMAT_OPTIONS: advanced_mode_format = !advanced_mode_format; WriteSettingBool(SETTING_ADVANCED_MODE_FORMAT, advanced_mode_format); ToggleAdvancedFormatOptions(advanced_mode_format); if (selected_fs == FS_UNKNOWN) selected_fs = (int)ComboBox_GetCurItemData(hFileSystem); SetFileSystemAndClusterSize(NULL); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL << 16) | IDC_FILE_SYSTEM, ComboBox_GetCurSel(hFileSystem)); break; case IDC_LABEL: if (HIWORD(wParam) == EN_CHANGE) { // We will get EN_CHANGE when we change the label automatically, so we need to detect that if (!app_changed_label) user_changed_label = TRUE; app_changed_label = FALSE; } break; case IDC_DEVICE: if (HIWORD(wParam) != CBN_SELCHANGE) break; nb_devices = ComboBox_GetCount(hDeviceList); PrintStatusDebug(0, (nb_devices == 1) ? MSG_208 : MSG_209, nb_devices); PopulateProperties(); nDeviceIndex = ComboBox_GetCurSel(hDeviceList); DeviceNum = (nDeviceIndex == CB_ERR) ? 0 : (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex); SendMessage(hMainDialog, WM_COMMAND, (CBN_SELCHANGE_INTERNAL << 16) | IDC_FILE_SYSTEM, ComboBox_GetCurSel(hFileSystem)); if (nb_devices == 0) { // No need to run the process search if no device is selected StopProcessSearch(); } else if (!StartProcessSearch() || !SetProcessSearch(DeviceNum)) { uprintf("Failed to start conflicting process search"); StopProcessSearch(); } break; case IDC_IMAGE_OPTION: if (HIWORD(wParam) != CBN_SELCHANGE) break; SetFileSystemAndClusterSize(NULL); imop_win_sel = ComboBox_GetCurSel(hImageOption); EnableUefiValidation((imop_win_sel == 0), TRUE); break; case IDC_PERSISTENCE_SIZE: if (HIWORD(wParam) == EN_CHANGE) { uint64_t pos; // We get EN_CHANGE when we change the size automatically, so we need to detect that if (app_changed_size) { app_changed_size = FALSE; break; } GetWindowTextA(GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE), tmp, sizeof(tmp)); lPos = strtol(tmp, NULL, 10); persistence_unit_selection = ComboBox_GetCurSel(GetDlgItem(hDlg, IDC_PERSISTENCE_UNITS)); persistence_size = lPos * MB; for (i = 0; i < persistence_unit_selection; i++) persistence_size *= 1024; if (persistence_size > SelectedDrive.DiskSize - PERCENTAGE(PROJECTED_SIZE_RATIO, img_report.projected_size)) persistence_size = SelectedDrive.DiskSize - PERCENTAGE(PROJECTED_SIZE_RATIO, img_report.projected_size); pos = persistence_size / MB; for (i = 0; i < persistence_unit_selection; i++) pos /= 1024; lPos = (LONG)pos; SendMessage(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER), TBM_SETPOS, TRUE, lPos); if (persistence_size >= (SelectedDrive.DiskSize - PERCENTAGE(PROJECTED_SIZE_RATIO, img_report.projected_size))) { static_sprintf(tmp, "%ld", lPos); app_changed_size = TRUE; SetWindowTextU(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), tmp); } } else if (HIWORD(wParam) == EN_KILLFOCUS) { if (persistence_size == 0) { TogglePersistenceControls(FALSE); static_sprintf(tmp, "0 (%s)", lmprintf(MSG_124)); app_changed_size = TRUE; SetWindowTextU(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), tmp); } else if (persistence_size < MIN_EXT_SIZE) { persistence_size = MIN_EXT_SIZE; uint64_t pos = persistence_size / MB; for (i = 0; i < persistence_unit_selection; i++) pos /= 1024; lPos = (LONG)pos; SendMessage(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER), TBM_SETPOS, TRUE, lPos); static_sprintf(tmp, "%ld", lPos); app_changed_size = TRUE; SetWindowTextU(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), tmp); } } break; case IDC_PERSISTENCE_UNITS: if (HIWORD(wParam) != CBN_SELCHANGE) break; if (ComboBox_GetCurSel(GetDlgItem(hDlg, IDC_PERSISTENCE_UNITS)) == persistence_unit_selection) break; GetWindowTextA(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), tmp, sizeof(tmp)); persistence_size = strtol(tmp, NULL, 10) * MB; for (i = 0; i < persistence_unit_selection; i++) persistence_size *= 1024; persistence_unit_selection = ComboBox_GetCurSel(GetDlgItem(hDlg, IDC_PERSISTENCE_UNITS)); SetPersistenceSize(); break; case IDC_NB_PASSES: if (HIWORD(wParam) != CBN_SELCHANGE) break; SetPassesTooltip(); break; case IDC_TARGET_SYSTEM: if (HIWORD(wParam) != CBN_SELCHANGE) break; target_type = (int)ComboBox_GetCurItemData(hTargetSystem); SendMessage(hMainDialog, UM_UPDATE_CSM_TOOLTIP, 0, 0); SetFileSystemAndClusterSize(NULL); EnableUefiValidation(TRUE, TRUE); break; case IDC_PARTITION_TYPE: if (HIWORD(wParam) != CBN_SELCHANGE) break; partition_type = (int)ComboBox_GetCurItemData(hPartitionScheme); SetPartitionSchemeAndTargetSystem(TRUE); SetFileSystemAndClusterSize(NULL); EnableOldBiosFixes(TRUE, TRUE); EnableUefiValidation(TRUE, TRUE); selected_pt = partition_type; break; case IDC_FILE_SYSTEM: if ((HIWORD(wParam) != CBN_SELCHANGE) && (HIWORD(wParam) != CBN_SELCHANGE_INTERNAL)) break; set_selected_fs = (HIWORD(wParam) == CBN_SELCHANGE); fs_type = (int)ComboBox_GetCurItemData(hFileSystem); SetClusterSizes(fs_type); if (set_selected_fs && (fs_type > 0)) selected_fs = fs_type; // Some FS's (such as ReFS or Large FAT32) only have QuickFormat so make sure we reflect that EnableQuickFormat(TRUE, TRUE); EnableOldBiosFixes(TRUE, TRUE); EnableExtendedLabel(TRUE, TRUE); break; case IDC_BOOT_SELECTION: if (HIWORD(wParam) != CBN_SELCHANGE) break; boot_type = (int)ComboBox_GetCurItemData(hBootType); if ((HIWORD(wParam) != CBN_SELCHANGE) || (boot_type == selection_default)) break; selection_default = boot_type; SetPartitionSchemeAndTargetSystem(FALSE); // Try to reselect current FS from the drive for non-bootable tmp[0] = 0; if ((selected_fs == FS_UNKNOWN) && (SelectedDrive.DeviceNumber != 0)) GetDrivePartitionData(SelectedDrive.DeviceNumber, tmp, sizeof(tmp), !usb_debug); SetFileSystemAndClusterSize(tmp); ToggleImageOptions(); SetProposedLabel(ComboBox_GetCurSel(hDeviceList)); EnableControls(TRUE, TRUE); target_type = (int)ComboBox_GetCurItemData(hPartitionScheme); partition_type = (int)ComboBox_GetCurItemData(hTargetSystem); return (INT_PTR)TRUE; case IDC_SELECT: // Ctrl-SELECT is used to select an additional archive of files to extract // For now only zip archives are supported. if (GetKeyState(VK_CONTROL) & 0x8000) { EXT_DECL(arch_ext, NULL, __VA_GROUP__("*.zip"), __VA_GROUP__(lmprintf(MSG_309))); archive_path = FileDialog(FALSE, NULL, &arch_ext, NULL); if (archive_path != NULL) { uprintf("Using archive: %s (%s)", _filenameU(archive_path), SizeToHumanReadable(_filesizeU(archive_path), FALSE, FALSE)); } break; } if (select_index == 1) { EnableControls(FALSE, FALSE); DownloadISO(); } else { if (img_provided) { uprintf("\r\nImage provided: '%s'", image_path); img_provided = FALSE; // One off thing... } else { char* old_image_path = image_path; char extensions[128] = "*.iso;*.img;*.vhd;*.vhdx;*.usb;*.bz2;*.bzip2;*.gz;*.lzma;*.xz;*.Z;*.zip;*.wic;*.wim;*.esd;*.vtsi"; if (has_ffu_support) strcat(extensions, ";*.ffu"); // If declared globaly, lmprintf(MSG_280) would be called on each message... EXT_DECL(img_ext, NULL, __VA_GROUP__(extensions), __VA_GROUP__(lmprintf(MSG_280))); image_path = FileDialog(FALSE, NULL, &img_ext, NULL); if (image_path == NULL) { if (old_image_path != NULL) { // Reselect previous image image_path = old_image_path; } else { CreateTooltip(hSelectImage, lmprintf(MSG_173), -1); PrintStatus(0, MSG_086); } break; } else { safe_free(archive_path); free(old_image_path); } } ErrorStatus = 0; if (CreateThread(NULL, 0, ImageScanThread, NULL, 0, NULL) == NULL) { uprintf("Unable to start ISO scanning thread"); ErrorStatus = RUFUS_ERROR(APPERR(ERROR_CANT_START_THREAD)); } } break; case IDC_UEFI_MEDIA_VALIDATION: if ((HIWORD(wParam)) == BN_CLICKED) { validate_md5sum = IsChecked(IDC_UEFI_MEDIA_VALIDATION); WriteSettingBool(SETTING_ENABLE_RUNTIME_VALIDATION, validate_md5sum); } break; case IDC_LIST_USB_HDD: if ((HIWORD(wParam)) == BN_CLICKED) { enable_HDDs = !enable_HDDs; PrintStatusTimeout(lmprintf(MSG_253), enable_HDDs); GetDevices(0); } break; case IDC_START: if (format_thread != NULL) return (INT_PTR)TRUE; // Just in case boot_type = (int)ComboBox_GetCurItemData(hBootType); partition_type = (int)ComboBox_GetCurItemData(hPartitionScheme); target_type = (int)ComboBox_GetCurItemData(hTargetSystem); fs_type = (int)ComboBox_GetCurItemData(hFileSystem); write_as_image = FALSE; write_as_esp = FALSE; unattend_xml_flags = 0; // Disable all controls except Cancel EnableControls(FALSE, FALSE); ErrorStatus = 0; LastWriteError = 0; StrArrayClear(&BlockingProcessList); no_confirmation_on_cancel = FALSE; SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0); selection_default = (int)ComboBox_GetCurItemData(hBootType); // Create a thread to validate options and download files as needed (so that we can update the UI). // On exit, this thread sends message UM_FORMAT_START back to this dialog. if (CreateThread(NULL, 0, BootCheckThread, NULL, 0, NULL) == NULL) { uprintf("Unable to start boot check thread"); ErrorStatus = RUFUS_ERROR(APPERR(ERROR_CANT_START_THREAD)); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); } break; case IDC_LANG: // Show the language menu such that it doesn't overlap the button SendMessage(hMultiToolbar, TB_GETRECT, (WPARAM)IDC_ABOUT, (LPARAM)&rc); MapWindowPoints(hDlg, NULL, (POINT*)&rc, 2); rc.left += cbw / 2; ShowLanguageMenu(rc); break; case IDC_SETTINGS: MyDialogBox(hMainInstance, IDD_UPDATE_POLICY, hDlg, UpdateCallback); break; case IDC_HASH: // TODO: Move this as a fn call in hash.c? if ((format_thread == NULL) && (image_path != NULL)) { ErrorStatus = 0; no_confirmation_on_cancel = TRUE; SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0); // Disable all controls except cancel EnableControls(FALSE, FALSE); InitProgress(FALSE); SetThreadAffinity(thread_affinity, HASH_MAX + 1); format_thread = CreateThread(NULL, 0, HashThread, (LPVOID)thread_affinity, 0, NULL); if (format_thread != NULL) { SetThreadPriority(format_thread, default_thread_priority); PrintInfo(0, -1); SendMessage(hMainDialog, UM_TIMER_START, 0, 0); } else { uprintf("Unable to start hash thread"); ErrorStatus = RUFUS_ERROR(APPERR(ERROR_CANT_START_THREAD)); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); } } break; case IDC_SAVE: VhdSaveImage(); break; case IDM_SELECT: case IDM_DOWNLOAD: select_index = LOWORD(wParam) - IDM_SELECT; SetWindowTextU(GetDlgItem(hDlg, IDC_SELECT), uppercase_select[select_index]); break; default: return (INT_PTR)FALSE; } return (INT_PTR)TRUE; case UM_UPDATE_CSM_TOOLTIP: ShowWindow(GetDlgItem(hMainDialog, IDS_CSM_HELP_TXT), ((target_type == TT_UEFI) || has_uefi_csm) ? SW_SHOW : SW_HIDE); CreateTooltip(GetDlgItem(hMainDialog, IDS_CSM_HELP_TXT), lmprintf((target_type == TT_UEFI) ? MSG_152 : MSG_151), 30000); break; case UM_ENABLE_CONTROLS: KillTimer(hMainDialog, TID_APP_TIMER); if (!IS_ERROR(ErrorStatus)) PrintInfo(0, MSG_210); else switch (SCODE_CODE(ErrorStatus)) { case ERROR_CANCELLED: PrintInfo(0, MSG_211); break; case ERROR_BAD_SIGNATURE: PrintInfo(0, MSG_283); break; default: PrintInfo(0, MSG_212); break; } EnableControls(TRUE, FALSE); break; case UM_TIMER_START: PrintInfo(0, -1); timer = 0; static_sprintf(szTimer, "00:00:00"); SendMessageA(hStatus, SB_SETTEXTA, SBT_OWNERDRAW | SB_SECTION_RIGHT, (LPARAM)szTimer); SetTimer(hMainDialog, TID_APP_TIMER, 1000, ClockTimer); break; case UM_SELECT_ISO: select_index = 0; img_provided = TRUE; SetWindowTextU(GetDlgItem(hDlg, IDC_SELECT), uppercase_select[0]); SendMessage(hDlg, WM_COMMAND, IDC_SELECT, 0); break; case UM_MEDIA_CHANGE: wParam = DBT_CUSTOMEVENT; // Fall through case WM_DEVICECHANGE: // The Windows hotplug subsystem sucks. Among other things, if you insert a GPT partitioned // USB drive with zero partitions, the only device messages you will get are a stream of // DBT_DEVNODES_CHANGED and that's it. But those messages are also issued when you get a // DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE, and there's a whole slew of them so we // can't really issue a refresh for each one we receive // What we do then is arm a timer on DBT_DEVNODES_CHANGED, if it's been more than 1 second // since last refresh/arm timer, and have that timer send DBT_CUSTOMEVENT when it expires. // DO *NOT* USE WM_DEVICECHANGE AS THE MESSAGE FROM THE TIMER PROC, as it may be filtered! // For instance filtering will occur when (un)plugging in a FreeBSD UFD on Windows 8. // Instead, use a custom user message, such as UM_MEDIA_CHANGE, to set DBT_CUSTOMEVENT. if (format_thread == NULL) { switch (wParam) { case DBT_DEVICEARRIVAL: case DBT_DEVICEREMOVECOMPLETE: case DBT_CUSTOMEVENT: // Sent by our timer refresh function or for card reader media change LastRefresh = GetTickCount64(); KillTimer(hMainDialog, TID_REFRESH_TIMER); if (!op_in_progress) { queued_hotplug_event = FALSE; GetDevices((DWORD)ComboBox_GetCurItemData(hDeviceList)); EnableControls(TRUE, FALSE); if (ComboBox_GetCurSel(hDeviceList) < 0) { SetPartitionSchemeAndTargetSystem(FALSE); SetFileSystemAndClusterSize(NULL); ShowWindow(GetDlgItem(hDlg, IDS_CSM_HELP_TXT), SW_HIDE); persistence_unit_selection = -1; } } else { queued_hotplug_event = TRUE; } return (INT_PTR)TRUE; case DBT_DEVNODES_CHANGED: // If it's been more than a second since last device refresh, arm a refresh timer if (dont_process_dbt_devnodes) { // This ensures we don't get unwanted refreshes while scanning an image. LastRefresh = GetTickCount64(); } else if (GetTickCount64() > LastRefresh + 1000) { LastRefresh = GetTickCount64(); SetTimer(hMainDialog, TID_REFRESH_TIMER, 1000, RefreshTimer); } break; default: break; } } break; case WM_INITDIALOG: // Make sure fScale is set before the first call to apply localization, so that move/resize scale appropriately hDC = GetDC(hDlg); fScale = GetDeviceCaps(hDC, LOGPIXELSX) / 96.0f; safe_release_dc(hDlg, hDC); apply_localization(IDD_DIALOG, hDlg); // The AppStore version always enables Fido if (appstore_version) SetFidoCheck(); else SetUpdateCheck(); first_log_display = TRUE; log_displayed = FALSE; hLogDialog = MyCreateDialog(hMainInstance, IDD_LOG, hDlg, (DLGPROC)LogCallback); InitDialog(hDlg); GetDevices(0); EnableControls(TRUE, FALSE); UpdateImage(FALSE); // The AppStore version does not need the internal check for updates if (!appstore_version) CheckForUpdates(FALSE); // Register MEDIA_INSERTED/MEDIA_REMOVED notifications for card readers if (SUCCEEDED(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &pidlDesktop))) { NotifyEntry.pidl = pidlDesktop; NotifyEntry.fRecursive = TRUE; // NB: The following only works if the media is already formatted. // If you insert a blank card, notifications will not be sent... :( ulRegister = SHChangeNotifyRegister(hDlg, 0x0001 | 0x0002 | 0x8000, SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED, UM_MEDIA_CHANGE, 1, &NotifyEntry); } // Bring our Window on top. We have to go through all *THREE* of these, or Far Manager hides our window :( SetWindowPos(hMainDialog, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); SetWindowPos(hMainDialog, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); SetWindowPos(hMainDialog, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); // Set 'START' as the selected button if it's enabled, otherwise use 'SELECT', instead SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)(IsWindowEnabled(hStart) ? hStart : hSelectImage), TRUE); #if defined(ALPHA) // Add a VERY ANNOYING popup for Alpha releases, so that people don't start redistributing them MessageBoxA(NULL, "This is an Alpha version of " APPLICATION_NAME " - It is meant to be used for " "testing ONLY and should NOT be distributed as a release.", "ALPHA VERSION", MSG_INFO); #elif defined(TEST) // Same thing for Test releases MessageBoxA(NULL, "This is a Test version of " APPLICATION_NAME " - It is meant to be used for " "testing ONLY and should NOT be distributed as a release.", "TEST VERSION", MSG_INFO); #endif // Let's not take any risk: Ask Windows to redraw the whole dialog before we exit init RedrawWindow(hMainDialog, NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); InvalidateRect(hMainDialog, NULL, TRUE); return (INT_PTR)FALSE; case WM_DRAWITEM: // The things one must do to get an ellipsis and text alignment on the status bar... if (wParam == IDC_STATUS) { pDI = (DRAWITEMSTRUCT*)lParam; if (WindowsVersion.Version >= WINDOWS_10) pDI->rcItem.top += (int)(1.0f * fScale); else if (fScale >= 1.49f) pDI->rcItem.top -= (int)(1.5f * fScale); pDI->rcItem.left += (int)(4.0f * fScale); SetBkMode(pDI->hDC, TRANSPARENT); switch (pDI->itemID) { case SB_SECTION_LEFT: SetTextColor(pDI->hDC, GetSysColor(COLOR_BTNTEXT)); DrawTextExU(pDI->hDC, szStatusMessage, -1, &pDI->rcItem, DT_LEFT | DT_END_ELLIPSIS | DT_PATH_ELLIPSIS, NULL); return (INT_PTR)TRUE; case SB_SECTION_RIGHT: SetTextColor(pDI->hDC, GetSysColor(COLOR_3DSHADOW)); DrawTextExA(pDI->hDC, szTimer, -1, &pDI->rcItem, DT_LEFT, NULL); return (INT_PTR)TRUE; } } break; case WM_PAINT: hDC = BeginPaint(hDlg, &ps); OnPaint(hDC); EndPaint(hDlg, &ps); break; case WM_CTLCOLORSTATIC: if ((HWND)lParam != GetDlgItem(hDlg, IDS_CSM_HELP_TXT)) return FALSE; SetBkMode((HDC)wParam, TRANSPARENT); CreateStaticFont((HDC)wParam, &hyperlink_font, FALSE); SelectObject((HDC)wParam, hyperlink_font); SetTextColor((HDC)wParam, TOOLBAR_ICON_COLOR); return (INT_PTR)CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); case WM_NOTIFY: switch (((LPNMHDR)lParam)->code) { case TTN_GETDISPINFO: lpttt = (LPTOOLTIPTEXT)lParam; switch (lpttt->hdr.idFrom) { case IDC_ABOUT: utf8_to_wchar_no_alloc(lmprintf(MSG_302), wtooltip, ARRAYSIZE(wtooltip)); lpttt->lpszText = wtooltip; break; case IDC_SETTINGS: utf8_to_wchar_no_alloc(lmprintf(MSG_301), wtooltip, ARRAYSIZE(wtooltip)); lpttt->lpszText = wtooltip; break; case IDC_LANG: utf8_to_wchar_no_alloc(lmprintf(MSG_273), wtooltip, ARRAYSIZE(wtooltip)); lpttt->lpszText = wtooltip; break; case IDC_LOG: utf8_to_wchar_no_alloc(lmprintf(MSG_303), wtooltip, ARRAYSIZE(wtooltip)); lpttt->lpszText = wtooltip; break; case IDC_SAVE: utf8_to_wchar_no_alloc(lmprintf(MSG_304), wtooltip, ARRAYSIZE(wtooltip)); lpttt->lpszText = wtooltip; break; case IDC_HASH: utf8_to_wchar_no_alloc(lmprintf(MSG_272), wtooltip, ARRAYSIZE(wtooltip)); lpttt->lpszText = wtooltip; break; } break; case BCN_DROPDOWN: pDropDown = (LPNMBCDROPDOWN)lParam; Point.x = pDropDown->rcButton.left; Point.y = pDropDown->rcButton.bottom; ClientToScreen(pDropDown->hdr.hwndFrom, &Point); hMenu = CreatePopupMenu(); InsertMenuU(hMenu, -1, MF_BYPOSITION | ((select_index == 0) ? MF_CHECKED : 0), IDM_SELECT, uppercase_select[0]); InsertMenuU(hMenu, -1, MF_BYPOSITION | ((select_index == 1) ? MF_CHECKED : 0), IDM_DOWNLOAD, uppercase_select[1]); TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN, Point.x, Point.y, hMainDialog, NULL); DestroyMenu(hMenu); break; } break; case WM_HSCROLL: lPos = (LONG)SendMessage(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER), TBM_GETPOS, 0, 0); SetPersistencePos(lPos); persistence_size = lPos * MB; for (i = 0; i < persistence_unit_selection; i++) persistence_size *= 1024; break; case WM_DROPFILES: if (format_thread == NULL) { droppedFileInfo = (HDROP)wParam; wbuffer = calloc(MAX_PATH, sizeof(wchar_t)); if (wbuffer == NULL) { uprintf("Failed to alloc buffer for drag-n-drop"); break; } DragQueryFileW(droppedFileInfo, 0, wbuffer, MAX_PATH); safe_free(image_path); image_path = wchar_to_utf8(wbuffer); safe_free(wbuffer); if (image_path != NULL) { img_provided = TRUE; // Simulate image selection click SendMessage(hDlg, WM_COMMAND, IDC_SELECT, 0); } } break; // This is >>>SUPER WEIRD<<<. After a successful ISO or DD write (e.g. Arch 2016.01) // we no longer receive WM_QUERYENDSESSION messages, only WM_ENDSESSION. // But if we do a FreeDOS format, WM_QUERYENDSESSION is still sent to us alright. // What the heck is going on here?!? // Also, even as we try to work around this, WM_ENDSESSION comes too late in the game // to prevent shutdown block. So we need to handle the _undocumented_ WM_CLIENTSHUTDOWN. case WM_CLOSE: case WM_CLIENTSHUTDOWN: case WM_QUERYENDSESSION: case WM_ENDSESSION: if (op_in_progress) return (INT_PTR)TRUE; if (message == WM_CLOSE) { // We must use PostQuitMessage() on WM_CLOSE, to prevent notification sound... PostQuitMessage(0); } else { // ...but we must simulate Cancel on shutdown requests, else the app freezes. SendMessage(hDlg, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); } break; case UM_PROGRESS_INIT: isMarquee = (wParam == PBS_MARQUEE); SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0); if (isMarquee) { SendMessage(hProgress, PBM_SETMARQUEE, TRUE, 0); SetTaskbarProgressState(TASKBAR_INDETERMINATE); } else { SendMessage(hProgress, PBM_SETPOS, 0, 0); SetTaskbarProgressState(TASKBAR_NORMAL); SetTaskbarProgressValue(0, MAX_PROGRESS); } break; case UM_PROGRESS_EXIT: tb_state = PBST_NORMAL; tb_flags = TASKBAR_NORMAL; if (isMarquee) { SendMessage(hProgress, PBM_SETMARQUEE, FALSE, 0); SetTaskbarProgressValue(0, MAX_PROGRESS); } else if (!IS_ERROR(ErrorStatus)) { SetTaskbarProgressValue(MAX_PROGRESS, MAX_PROGRESS); } else if (SCODE_CODE(ErrorStatus) == ERROR_CANCELLED) { tb_state = PBST_PAUSED; tb_flags = TASKBAR_PAUSED; } else { tb_state = PBST_ERROR; tb_flags = TASKBAR_ERROR; MessageBeep(MB_ICONERROR); FlashTaskbar(dialog_handle); } SendMessage(hProgress, PBM_SETSTATE, (WPARAM)tb_state, 0); SetTaskbarProgressState(tb_flags); break; case UM_NO_UPDATE: Notification(MSG_INFO, NULL, NULL, lmprintf(MSG_243), lmprintf(MSG_247)); // Need to manually set focus back to "Check Now" for tabbing to work SendMessage(hUpdatesDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hUpdatesDlg, IDC_CHECK_NOW), TRUE); break; case UM_FORMAT_START: if (wParam != BOOTCHECK_PROCEED) goto aborted_start; // All subsequent aborts below translate to a user cancellation wParam = BOOTCHECK_CANCEL; if ((partition_type == PARTITION_STYLE_MBR) && (SelectedDrive.DiskSize > 2 * TB)) { if (MessageBoxExU(hMainDialog, lmprintf(MSG_134, SizeToHumanReadable(SelectedDrive.DiskSize - 2 * TB, FALSE, FALSE)), lmprintf(MSG_128, "MBR"), MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid) != IDYES) goto aborted_start; } if (!zero_drive && (fs_type == FS_UDF)) { // Display a warning about UDF formatting times dur_secs = (uint32_t)(((double)SelectedDrive.DiskSize) / 1073741824.0f / UDF_FORMAT_SPEED); if (dur_secs > UDF_FORMAT_WARN) { dur_mins = dur_secs / 60; dur_secs -= dur_mins * 60; MessageBoxExU(hMainDialog, lmprintf(MSG_112, dur_mins, dur_secs), lmprintf(MSG_113), MB_OK | MB_ICONASTERISK | MB_IS_RTL, selected_langid); } else { dur_secs = 0; dur_mins = 0; } } // Detect processes that have write (0x2) or exec (0x4) permissions against our drive. // Ideally, exec should be no big deal, but Windows complains on USB ejection if a // process such as cmd.exe holds exec rights, so we follow suit. PrintStatus(0, MSG_278); if (GetProcessSearch(SEARCH_PROCESS_TIMEOUT, 0x06, TRUE)) { char title[128]; ComboBox_GetTextU(hDeviceList, title, sizeof(title)); if (!Notification(MSG_WARNING_QUESTION, NULL, NULL, title, lmprintf(MSG_132))) goto aborted_start; } GetWindowTextU(hDeviceList, tmp, ARRAYSIZE(tmp)); if (MessageBoxExU(hMainDialog, lmprintf(MSG_003, tmp), APPLICATION_NAME, MB_OKCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid) == IDCANCEL) goto aborted_start; if ((SelectedDrive.nPartitions > 1) && (MessageBoxExU(hMainDialog, lmprintf(MSG_093), lmprintf(MSG_094), MB_OKCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid) == IDCANCEL)) goto aborted_start; if ((!zero_drive) && (boot_type != BT_NON_BOOTABLE) && (SelectedDrive.SectorSize != 512) && (MessageBoxExU(hMainDialog, lmprintf(MSG_196, SelectedDrive.SectorSize), lmprintf(MSG_197), MB_OKCANCEL | MB_ICONWARNING | MB_IS_RTL, selected_langid) == IDCANCEL)) goto aborted_start; nDeviceIndex = ComboBox_GetCurSel(hDeviceList); DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex); InitProgress(zero_drive || write_as_image); format_thread = CreateThread(NULL, 0, FormatThread, (LPVOID)(uintptr_t)DeviceNum, 0, NULL); if (format_thread == NULL) { uprintf("Unable to start formatting thread"); ErrorStatus = RUFUS_ERROR(APPERR(ERROR_CANT_START_THREAD)); PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); } else { SetThreadPriority(format_thread, default_thread_priority); uprintf("\r\nFormat operation started"); SendMessage(hMainDialog, UM_TIMER_START, 0, 0); // Set focus to the Cancel button SendMessage(hMainDialog, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hMainDialog, IDCANCEL), TRUE); } if (format_thread != NULL) break; aborted_start: zero_drive = FALSE; if (queued_hotplug_event) SendMessage(hDlg, UM_MEDIA_CHANGE, 0, 0); if (wParam == BOOTCHECK_CANCEL) { if (unattend_xml_path != NULL) { DeleteFileU(unattend_xml_path); unattend_xml_path = NULL; } EnableControls(TRUE, FALSE); break; } ErrorStatus = RUFUS_ERROR((wParam == BOOTCHECK_DOWNLOAD_ERROR) ? APPERR(ERROR_CANT_DOWNLOAD) : ERROR_GEN_FAILURE); // Fall through case UM_FORMAT_COMPLETED: zero_drive = FALSE; format_thread = NULL; if (unattend_xml_path != NULL) { DeleteFileU(unattend_xml_path); unattend_xml_path = NULL; } // Stop the timer KillTimer(hMainDialog, TID_APP_TIMER); // Close the cancel MessageBox and Blocking notification if active SendMessage(FindWindowA(MAKEINTRESOURCEA(32770), lmprintf(MSG_049)), WM_COMMAND, IDNO, 0); SendMessage(FindWindowA(MAKEINTRESOURCEA(32770), lmprintf(MSG_049)), WM_COMMAND, IDYES, 0); EnableWindow(GetDlgItem(hMainDialog, IDCANCEL), TRUE); EnableControls(TRUE, FALSE); if (wParam) { uprintf("\r\n"); GetDevices(DeviceNum); } if (!IS_ERROR(ErrorStatus)) { SendMessage(hProgress, PBM_SETPOS, MAX_PROGRESS, 0); SetTaskbarProgressState(TASKBAR_NOPROGRESS); PrintInfo(0, MSG_210); MessageBeep(MB_OK); FlashTaskbar(dialog_handle); } else if (SCODE_CODE(ErrorStatus) == ERROR_CANCELLED) { SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_PAUSED, 0); SetTaskbarProgressState(TASKBAR_PAUSED); PrintInfo(0, MSG_211); Notification(MSG_INFO, NULL, NULL, lmprintf(MSG_211), lmprintf(MSG_041)); } else { SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_ERROR, 0); SetTaskbarProgressState(TASKBAR_ERROR); PrintInfo(0, MSG_212); MessageBeep(MB_ICONERROR); FlashTaskbar(dialog_handle); GetProcessSearch(0, 0x07, FALSE); if (BlockingProcessList.Index > 0) { ListDialog(lmprintf(MSG_042), lmprintf(MSG_055), BlockingProcessList.String, BlockingProcessList.Index); } else { if (WindowsVersion.Version >= WINDOWS_10) { // Try to detect if 'Controlled Folder Access' is enabled on Windows 10 or later. See also: // http://www.winhelponline.com/blog/use-controlled-folder-access-windows-10-windows-defender char cmdline[256]; static const char* ps_cmd[2] = { // Return 1 if the 'Get-MpPreference' PowerShell cmdlet exists "If (Get-Command -Commandtype Function Get-MpPreference -ErrorAction SilentlyContinue) { Exit 1 } Else { Exit 0 }", // Return 1 if Controlled Folder Access is enabled "Exit (Get-MpPreference).EnableControlledFolderAccess" }; switch (SCODE_CODE(ErrorStatus)) { case ERROR_PARTITION_FAILURE: case ERROR_WRITE_FAULT: // Find if PowerShell is available at its expected location static_sprintf(tmp, "%s\\WindowsPowerShell\\v1.0\\powershell.exe", system_dir); if (PathFileExistsU(tmp)) { for (i = 0; i < ARRAYSIZE(ps_cmd); i++) { // Run the PowerShell commands static_sprintf(cmdline, "%s -NonInteractive -NoProfile -Command %s", tmp, ps_cmd[i]); if (RunCommand(cmdline, app_data_dir, TRUE) != 1) break; } if (i == ARRAYSIZE(ps_cmd)) { uprintf("\r\nWARNING: 'Controlled Folder Access' appears to be enabled on this system"); uprintf("You may need to disable this feature, or add an exception, for Rufus to to work...\n"); } } break; } } if (SCODE_CODE(ErrorStatus) == ERROR_NOT_READY) { // A port cycle usually helps with a device not ready int index = ComboBox_GetCurSel(hDeviceList); if (index >= 0) { uprintf("Device not ready → Trying to cycle port..."); CyclePort(index); } } Notification(MSG_ERROR, NULL, NULL, lmprintf(MSG_042), lmprintf(MSG_043, StrError(ErrorStatus, FALSE))); } } ErrorStatus = 0; LastWriteError = 0; return (INT_PTR)TRUE; } return (INT_PTR)FALSE; } static void PrintUsage(char* appname) { char fname[_MAX_FNAME]; _splitpath(appname, NULL, NULL, fname, NULL); printf("\nUsage: %s [-x] [-g] [-h] [-f FILESYSTEM] [-i PATH] [-l LOCALE] [-w TIMEOUT]\n", fname); printf(" -x, --extra-devs\n"); printf(" List extra devices, such as USB HDDs\n"); printf(" -g, --gui\n"); printf(" Start in GUI mode (disable the 'rufus.com' commandline hogger)\n"); printf(" -i PATH, --iso=PATH\n"); printf(" Select the ISO image pointed by PATH to be used on startup\n"); printf(" -l LOCALE, --locale=LOCALE\n"); printf(" Select the locale to be used on startup\n"); printf(" -f FILESYSTEM, --filesystem=FILESYSTEM\n"); printf(" Preselect the file system to be preferred when formatting\n"); printf(" -w TIMEOUT, --wait=TIMEOUT\n"); printf(" Wait TIMEOUT tens of seconds for the global application mutex to be released.\n"); printf(" Used when launching a newer version of " APPLICATION_NAME " from a running application.\n"); printf(" -h, --help\n"); printf(" This usage guide.\n"); } static HANDLE SetHogger(void) { INPUT* input; BYTE* hog_data; DWORD hog_size, Size; HANDLE hogmutex = NULL, hFile = NULL; int i; hog_data = GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_XT_HOGGER), _RT_RCDATA, cmdline_hogger, &hog_size, FALSE); if (hog_data != NULL) { // Create our synchronisation mutex hogmutex = CreateMutexA(NULL, TRUE, "Global/Rufus_CmdLine"); // Extract the hogger resource hFile = CreateFileA(cmdline_hogger, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { // coverity[check_return] WriteFile(hFile, hog_data, hog_size, &Size, NULL); } safe_closehandle(hFile); // Now launch the file from the commandline, by simulating keypresses input = (INPUT*)calloc(strlen(cmdline_hogger)+1, sizeof(INPUT)); if (input != NULL) { for (i = 0; i < (int)strlen(cmdline_hogger); i++) { input[i].type = INPUT_KEYBOARD; input[i].ki.dwFlags = KEYEVENTF_UNICODE; input[i].ki.wScan = (wchar_t)cmdline_hogger[i]; } input[i].type = INPUT_KEYBOARD; input[i].ki.wVk = VK_RETURN; SendInput(i + 1, input, sizeof(INPUT)); free(input); } } if (hogmutex != NULL) Sleep(200); // Need to add a delay, otherwise we may get some printout before the hogger return hogmutex; } // For delay-loaded DLLs, use LOAD_LIBRARY_SEARCH_SYSTEM32 to avoid DLL search order hijacking. FARPROC WINAPI dllDelayLoadHook(unsigned dliNotify, PDelayLoadInfo pdli) { if (dliNotify == dliNotePreLoadLibrary) { // Windows 7 without KB2533623 does not support the LOAD_LIBRARY_SEARCH_SYSTEM32 flag. // That is OK, because the delay load handler will interrupt the NULL return value // to mean that it should perform a normal LoadLibrary. return (FARPROC)LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); } return NULL; } #if defined(_MSC_VER) // By default the Windows SDK headers have a `const` while MinGW does not. const #endif PfnDliHook __pfnDliNotifyHook2 = dllDelayLoadHook; /* * Application Entrypoint */ #if defined(_MSC_VER) && (_MSC_VER >= 1600) int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) #else int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) #endif { const char* rufus_loc = "rufus.loc"; int i, opt, option_index = 0, argc = 0, si = 0, lcid = GetUserDefaultUILanguage(); int wait_for_mutex = 0, forced_windows_version = 0; uint32_t wue_options; FILE* fd; BOOL attached_console = FALSE, external_loc_file = FALSE, lgp_set = FALSE, automount = TRUE; BOOL disable_hogger = FALSE, previous_enable_HDDs = FALSE, vc = IsRegistryNode(REGKEY_HKCU, vs_reg); BOOL alt_pressed = FALSE, alt_command = FALSE; BYTE *loc_data; DWORD loc_size, u = 0, size = sizeof(u); char tmp_path[MAX_PATH] = "", loc_file[MAX_PATH] = "", ini_path[MAX_PATH] = "", ini_flags[] = "rb"; char *tmp, *locale_name = NULL, **argv = NULL; wchar_t **wenv, **wargv; PF_TYPE_DECL(CDECL, int, __wgetmainargs, (int*, wchar_t***, wchar_t***, int, int*)); HANDLE mutex = NULL, hogmutex = NULL, hFile = NULL; HWND hDlg = NULL; HDC hDC; MSG msg; struct option long_options[] = { {"extra-devs", no_argument, NULL, 'x'}, {"gui", no_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, {"iso", required_argument, NULL, 'i'}, {"locale", required_argument, NULL, 'l'}, {"filesystem", required_argument, NULL, 'f'}, {"wait", required_argument, NULL, 'w'}, {0, 0, NULL, 0} }; // Disable loading system DLLs from the current directory (sideloading mitigation) // PS: You know that official MSDN documentation for SetDllDirectory() that explicitly // indicates that "If the parameter is an empty string (""), the call removes the current // directory from the default DLL search order"? Yeah, that doesn't work. At all. // Still, we invoke it, for platforms where the following call might actually work... SetDllDirectoryA(""); // For libraries on the KnownDLLs list, the system will always load them from System32. // For other DLLs we link directly to, we can delay load the DLL and use a delay load // hook to load them from System32. Note that, for this to work, something like: // 'somelib.dll;%(DelayLoadDLLs)' must be added to the 'Delay Loaded Dlls' option of // the linker properties in Visual Studio (which means this won't work with MinGW). // For all other DLLs, use SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32). SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32); uprintf("*** " APPLICATION_NAME " init ***\n"); its_a_me_mario = GetUserNameA((char*)(uintptr_t)&u, &size) && (u == 7104878); // coverity[pointless_string_compare] is_x86_64 = (strcmp(APPLICATION_ARCH, "x64") == 0); // Retrieve various app & system directories. if (GetCurrentDirectoryU(sizeof(app_dir), app_dir) == 0) { uprintf("Could not get application directory: %s", WindowsErrorString()); static_strcpy(app_dir, ".\\"); } else { // Microsoft has a bad habit of making some of its APIs (_chdir/_wchdir) break // when app_dir is a drive letter that doesn't have a trailing backslash. For // instance _chdir("F:") does not change the directory, whereas _chdir("F:\\") // does. So make sure we always have a trailing backslash. static_strcat(app_dir, "\\"); } // In the wonderful world of Microsoft Windows, GetCurrentDirectory() returns the // directory where the application resides, instead of the real current directory // so we need another method to resolve the *ACTUAL* current directory. static_strcpy(cur_dir, ".\\"); hFile = CreateFileU(cur_dir, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if ((hFile == INVALID_HANDLE_VALUE) || (GetFinalPathNameByHandleU(hFile, cur_dir, sizeof(cur_dir), FILE_NAME_OPENED) == 0) || ((strstr(cur_dir, "\\\\?\\") != cur_dir) && (strstr(cur_dir, "\\\\.\\") != cur_dir))) { uprintf("Could not get current directory from '%s': %s", cur_dir, WindowsErrorString()); static_strcpy(cur_dir, ".\\"); } else { // Need to remove the '\\?\' prefix and reappend the trailing '\' static_strcpy(cur_dir, &cur_dir[4]); static_strcat(cur_dir, "\\"); } safe_closehandle(hFile); // Per documentation, the returned string ends with a backslash if (GetTempPathU(sizeof(temp_dir), temp_dir) == 0) { uprintf("Could not get temp directory: %s", WindowsErrorString()); static_strcpy(temp_dir, cur_dir); } else { // Some folks have found nothing better than configure their Windows installation to use // a symlink for their temp dir, and it so happens that the Windows WIM mounting facility, // which we need for applying the WUE options, can't handle symlinked directories. So we // *attempt* to resolve the actual symlinked temp dir for this super limited number of // users, with the hope that doing so is not going to break stuff elsewhere... hFile = CreateFileU(temp_dir, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); // GetFinalPathNameByHandle returns a UNC path, which should be prefixed by '\\?\' or '\\.\' if ((hFile == INVALID_HANDLE_VALUE) || (GetFinalPathNameByHandleU(hFile, temp_dir, sizeof(temp_dir), FILE_NAME_OPENED) == 0) || ((strstr(temp_dir, "\\\\?\\") != temp_dir) && (strstr(temp_dir, "\\\\.\\") != temp_dir))) { uprintf("Could not get actual temp directory from '%s': %s", temp_dir, WindowsErrorString()); static_strcpy(temp_dir, cur_dir); } else { // Need to remove the '\\?\' prefix or else we'll get issues with the Fido icon static_strcpy(temp_dir, &temp_dir[4]); // And me must re-append the '\' that gets removed by GetFinalPathNameByHandle() static_strcat(temp_dir, "\\"); } safe_closehandle(hFile); } if (GetSystemDirectoryU(system_dir, sizeof(system_dir)) == 0) { uprintf("Could not get system directory: %s", WindowsErrorString()); static_strcpy(system_dir, "C:\\Windows\\System32"); } if (!SHGetSpecialFolderPathU(NULL, app_data_dir, CSIDL_LOCAL_APPDATA, FALSE)) { uprintf("Could not get app data directory: %s", WindowsErrorString()); static_strcpy(app_data_dir, temp_dir); } if (!SHGetSpecialFolderPathU(NULL, user_dir, CSIDL_PROFILE, FALSE)) { uprintf("Could not get user directory: %s", WindowsErrorString()); static_strcpy(user_dir, temp_dir); } // Construct Sysnative ourselves as there is no GetSysnativeDirectory() call // By default (64bit app running on 64 bit OS or 32 bit app running on 32 bit OS) // Sysnative and System32 are the same static_strcpy(sysnative_dir, system_dir); // But if the app is 32 bit and the OS is 64 bit, Sysnative must differ from System32 #if (!defined(_WIN64) && !defined(BUILD64)) { BOOL is_WOW64 = FALSE; IsWow64Process(GetCurrentProcess(), &is_WOW64); if (is_WOW64) { if (GetSystemWindowsDirectoryU(sysnative_dir, sizeof(sysnative_dir)) == 0) { uprintf("Could not get Windows directory: %s", WindowsErrorString()); static_strcpy(sysnative_dir, "C:\\Windows"); } static_strcat(sysnative_dir, "\\Sysnative"); } } #endif uprintf("Cur dir: '%s'", cur_dir); uprintf("App dir: '%s'", app_dir); uprintf("Sys dir: '%s'", sysnative_dir); uprintf("Usr dir: '%s'", user_dir); uprintf("Dat dir: '%s'", app_data_dir); uprintf("Tmp dir: '%s'", temp_dir); // Look for a rufus.app file in the current app directory // Since Microsoft makes it downright impossible to pass an arg in the app manifest // and the automated VS2019 package building process doesn't like renaming the .exe // right under its nose (else we would use the same trick as for portable vs regular) // we use yet another workaround to detect if we are running the AppStore version... static_sprintf(tmp_path, "%srufus.app", app_dir); if (PathFileExistsU(tmp_path)) { appstore_version = TRUE; goto skip_args_processing; } // We have to process the arguments before we acquire the lock and process the locale PF_INIT(__wgetmainargs, Msvcrt); if (pf__wgetmainargs != NULL) { BOOL list_params = FALSE; pf__wgetmainargs(&argc, &wargv, &wenv, 1, &si); argv = (char**)calloc(argc, sizeof(char*)); if (argv != NULL) { // Non getopt parameter check for (i = 0; i < argc; i++) { argv[i] = wchar_to_utf8(wargv[i]); // Check for "/W" (wait for mutex release for pre 1.3.3 versions) if (strcmp(argv[i], "/W") == 0) wait_for_mutex = 150; // Try to acquire the mutex for 15 seconds // We need to find if we need to disable the hogger BEFORE we start // processing arguments with getopt, as we may want to print messages // on the commandline then, which the hogger makes more intuitive. if ((strcmp(argv[i], "-g") == 0) || (strcmp(argv[i], "--gui") == 0)) disable_hogger = TRUE; // Check for "/InvokerPRAID", which may *STUPIDLY* be added by Microsoft // when starting an app that was installed from the Windows store... if (stricmp(argv[i], "/InvokerPRAID") == 0) { appstore_version = TRUE; goto skip_args_processing; } } // If our application name contains a 'p' (for "portable") create a 'rufus.ini' // NB: argv[0] is populated in the previous loop tmp = &argv[0][strlen(argv[0]) - 1]; while ((((uintptr_t)tmp) > ((uintptr_t)argv[0])) && (*tmp != '\\')) tmp--; // Need to take 'ALPHA' into account if ((strchr(tmp, 'p') != NULL) || ((strchr(tmp, 'P') != NULL) && (strchr(tmp, 'P')[1] != 'H'))) ini_flags[0] = 'a'; // Now enable the hogger before processing the rest of the arguments if (!disable_hogger) { // Reattach the console, if we were started from commandline if (AttachConsole(ATTACH_PARENT_PROCESS) != 0) { uprintf("Enabling console line hogger"); attached_console = TRUE; IGNORE_RETVAL(freopen("CONIN$", "r", stdin)); IGNORE_RETVAL(freopen("CONOUT$", "w", stdout)); IGNORE_RETVAL(freopen("CONOUT$", "w", stderr)); _flushall(); hogmutex = SetHogger(); } } while ((opt = getopt_long(argc, argv, "ghxf:i:l:w:z:", long_options, &option_index)) != EOF) { switch (opt) { case 'x': enable_HDDs = TRUE; break; case 'a': case 'g': // No need to reprocess that option break; case 'i': safe_free(image_path); image_path = calloc(1, MAX_PATH); if (image_path == NULL) break; IGNORE_RETVAL(GetFullPathNameU(optarg, MAX_PATH, image_path, NULL)); // FILE_ATTRIBUTE_DIRECTORY is set for both dir and access error if (GetFileAttributesU(image_path) & FILE_ATTRIBUTE_DIRECTORY) { printf("Could not find image '%s'\n", image_path); safe_free(image_path); break; } img_provided = TRUE; break; case 'l': if (isdigitU(optarg[0])) { lcid = (int)strtol(optarg, NULL, 0); } else { safe_free(locale_name); locale_name = safe_strdup(optarg); } break; case 'f': if (isdigitU(optarg[0])) { preselected_fs = (int)strtol(optarg, NULL, 0); } else { for (i = 0; i < ARRAYSIZE(FileSystemLabel); i++) { if (safe_stricmp(optarg, FileSystemLabel[i]) == 0) { preselected_fs = i; break; } } } if ((preselected_fs < FS_UNKNOWN) || (preselected_fs >= FS_MAX)) preselected_fs = FS_UNKNOWN; selected_fs = preselected_fs; break; case 'w': wait_for_mutex = atoi(optarg); break; case 'h': PrintUsage(argv[0]); goto out; case 'z': forced_windows_version = (int)strtol(optarg, NULL, 16); break; // getopt_long returns '?' for any option it doesn't recognize default: list_params = TRUE; break; } } if (list_params) { uprintf("Command line arguments:"); for (i = 1; i < argc; i++) // argv[i] may contain a '%' so don't feed it as a naked format string. uprintf("%s", argv[i]); } } } else { uprintf("Could not access UTF-16 args"); } skip_args_processing: if (appstore_version) uprintf("AppStore version detected"); // Look for a .ini file in the current app directory static_sprintf(ini_path, "%srufus.ini", app_dir); fd = fopenU(ini_path, ini_flags); // Will create the file if portable mode is requested // Using the string directly in safe_strcmp() would call GetSignatureName() twice tmp = GetSignatureName(NULL, NULL, FALSE); vc |= (safe_strcmp(tmp, cert_name[0]) == 0); if (fd != NULL) { ini_file = ini_path; // In portable mode, use the app directory for all local storage static_strcpy(app_data_dir, app_dir); fclose(fd); } uprintf("Will use settings from %s", (ini_file != NULL) ? "INI file" : "registry"); // Use the locale specified by the settings, if any tmp = ReadSettingStr(SETTING_LOCALE); if (tmp[0] != 0) { locale_name = safe_strdup(tmp); uprintf("found locale '%s'", locale_name); } // Restore user-saved settings advanced_mode_device = ReadSettingBool(SETTING_ADVANCED_MODE_DEVICE); advanced_mode_format = ReadSettingBool(SETTING_ADVANCED_MODE_FORMAT); preserve_timestamps = ReadSettingBool(SETTING_PRESERVE_TIMESTAMPS); use_fake_units = !ReadSettingBool(SETTING_USE_PROPER_SIZE_UNITS); is_vds_available = IsVdsAvailable(FALSE); use_vds = ReadSettingBool(SETTING_USE_VDS) && is_vds_available; usb_debug = ReadSettingBool(SETTING_ENABLE_USB_DEBUG); cdio_loglevel_default = usb_debug ? CDIO_LOG_DEBUG : CDIO_LOG_WARN; use_rufus_mbr = !ReadSettingBool(SETTING_DISABLE_RUFUS_MBR); validate_md5sum = ReadSettingBool(SETTING_ENABLE_RUNTIME_VALIDATION); detect_fakes = !ReadSettingBool(SETTING_DISABLE_FAKE_DRIVES_CHECK); allow_dual_uefi_bios = ReadSettingBool(SETTING_ENABLE_WIN_DUAL_EFI_BIOS); force_large_fat32 = ReadSettingBool(SETTING_FORCE_LARGE_FAT32_FORMAT); enable_vmdk = ReadSettingBool(SETTING_ENABLE_VMDK_DETECTION); enable_file_indexing = ReadSettingBool(SETTING_ENABLE_FILE_INDEXING); enable_VHDs = !ReadSettingBool(SETTING_DISABLE_VHDS); enable_extra_hashes = ReadSettingBool(SETTING_ENABLE_EXTRA_HASHES); expert_mode = ReadSettingBool(SETTING_EXPERT_MODE); ignore_boot_marker = ReadSettingBool(SETTING_IGNORE_BOOT_MARKER); persistent_log = ReadSettingBool(SETTING_PERSISTENT_LOG); save_image_type = ReadSettingStr(SETTING_PREFERRED_SAVE_IMAGE_TYPE); // This restores the Windows User Experience/unattend.xml mask from the saved user // settings, and is designed to work even if we add new options later. wue_options = ReadSetting32(SETTING_WUE_OPTIONS); if ((wue_options >> 16) != 0) { uint32_t mask = wue_options >> 16; unattend_xml_mask &= ~mask; unattend_xml_mask |= (wue_options & mask); } // We want above normal priority by default, so we offset the value. default_thread_priority = ReadSetting32(SETTING_DEFAULT_THREAD_PRIORITY) + THREAD_PRIORITY_ABOVE_NORMAL; // Initialize the global scaling, in case we need it before we initialize the dialog hDC = GetDC(NULL); fScale = GetDeviceCaps(hDC, LOGPIXELSX) / 96.0f; safe_release_dc(NULL, hDC); // Init localization init_localization(); // Seek for a loc file in the application directory static_sprintf(loc_file, "%s%s", app_dir, rufus_loc); if (GetFileAttributesU(loc_file) == INVALID_FILE_ATTRIBUTES) { uprintf("loc file not found in current directory - embedded one will be used"); loc_data = (BYTE*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_LC_RUFUS_LOC), _RT_RCDATA, "embedded.loc", &loc_size, FALSE); if ( (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, loc_file) == 0) || (loc_file[0] == 0) ) { // If we don't have a working temp API, forget it uprintf("FATAL: Unable to create temp loc file: %s", WindowsErrorString()); MessageBoxA(NULL, "Unable to create temporary localization file. This application will now exit.", "Fatal error", MB_ICONSTOP | MB_SYSTEMMODAL); goto out; } hFile = CreateFileU(loc_file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if ((hFile == INVALID_HANDLE_VALUE) || (!WriteFileWithRetry(hFile, loc_data, loc_size, &size, WRITE_RETRIES))) { uprintf("FATAL: Unable to extract loc file '%s': %s", loc_file, WindowsErrorString()); safe_closehandle(hFile); MessageBoxA(NULL, "Unable to extract localization file. This application will now exit.", "Fatal error", MB_ICONSTOP | MB_SYSTEMMODAL); goto out; } uprintf("localization: extracted data to '%s'", loc_file); safe_closehandle(hFile); } else { external_loc_file = TRUE; // We do want to report if an external loc file is being used, in the UI log ubprintf("Using external loc file '%s'", loc_file); } if ( (!get_supported_locales(loc_file)) || ((selected_locale = ((locale_name == NULL)?get_locale_from_lcid(lcid, TRUE):get_locale_from_name(locale_name, TRUE))) == NULL) ) { uprintf("FATAL: Could not access locale!"); MessageBoxA(NULL, "The locale data is missing or invalid. This application will now exit.", "Fatal error", MB_ICONSTOP|MB_SYSTEMMODAL); goto out; } selected_langid = get_language_id(selected_locale); // Set the Windows version GetWindowsVersion(&WindowsVersion); // Force a version if specified as parameter, but without allowing folks running // a version of Windows we no longer support to use the option as a bypass! if (WindowsVersion.Version > WINDOWS_7 && forced_windows_version != 0) WindowsVersion.Version = forced_windows_version; // ...and nothing of value was lost if (WindowsVersion.Version <= WINDOWS_7) { // Load the translation before we print the error get_loc_data_file(loc_file, selected_locale); right_to_left_mode = ((selected_locale->ctrl_id) & LOC_RIGHT_TO_LEFT); // Set MB_SYSTEMMODAL to prevent Far Manager from stealing focus... MessageBoxExU(NULL, lmprintf(MSG_294, (WindowsVersion.Version == WINDOWS_7) ? 3 : 2, (WindowsVersion.Version == WINDOWS_7) ? 22 : 18), lmprintf(MSG_293), MB_ICONSTOP | MB_IS_RTL | MB_SYSTEMMODAL, selected_langid); goto out; } // This is needed as there appears to be a *FLAW* in Windows allowing the app to run unelevated with some // weirdly configured user accounts, even as we explicitly set 'requireAdministrator' in the manifest... if (!IsCurrentProcessElevated()) { uprintf("FATAL: No administrative privileges!"); // Load the translation before we print the error get_loc_data_file(loc_file, selected_locale); right_to_left_mode = ((selected_locale->ctrl_id) & LOC_RIGHT_TO_LEFT); MessageBoxExU(NULL, lmprintf(MSG_289), lmprintf(MSG_288), MB_ICONSTOP | MB_IS_RTL | MB_SYSTEMMODAL, selected_langid); goto out; } // Prevent 2 applications from running at the same time, unless "/W" is passed as an option // in which case we wait for the mutex to be relinquished if ((safe_strlen(lpCmdLine) == 2) && (lpCmdLine[0] == '/') && (lpCmdLine[1] == 'W')) wait_for_mutex = 150; // Try to acquire the mutex for 15 seconds mutex = CreateMutexA(NULL, TRUE, "Global/" APPLICATION_NAME); for (;(wait_for_mutex>0) && (mutex != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS); wait_for_mutex--) { CloseHandle(mutex); Sleep(100); mutex = CreateMutexA(NULL, TRUE, "Global/" APPLICATION_NAME); } if ((mutex == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS)) { // Load the translation before we print the error get_loc_data_file(loc_file, selected_locale); right_to_left_mode = ((selected_locale->ctrl_id) & LOC_RIGHT_TO_LEFT); // Set MB_SYSTEMMODAL to prevent Far Manager from stealing focus... MessageBoxExU(NULL, lmprintf(MSG_002), lmprintf(MSG_001), MB_ICONSTOP|MB_IS_RTL|MB_SYSTEMMODAL, selected_langid); goto out; } // Save instance of the application for further reference hMainInstance = hInstance; // Initialize COM for folder selection IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); // Some dialogs have Rich Edit controls and won't display without this if (GetLibraryHandle("Riched20") == NULL) uprintf("Could not load RichEdit library - some dialogs may not display: %s", WindowsErrorString()); // Increase the application privileges (SE_DEBUG_PRIVILEGE), so that we can report // the Windows Services preventing access to the disk or volume we want to format. EnablePrivileges(); // We use local group policies rather than direct registry manipulation // 0x9e disables removable and fixed drive notifications lgp_set = SetLGP(FALSE, &existing_key, ep_reg, "NoDriveTypeAutorun", 0x9e); // Re-enable AutoMount if needed if (!GetAutoMount(&automount)) { uprintf("Could not get AutoMount status"); automount = TRUE; // So that we don't try to change its status on exit } else if (!automount) { uprintf("AutoMount was detected as disabled - temporarily re-enabling it"); if (!SetAutoMount(TRUE)) uprintf("Failed to enable AutoMount"); } // Detect CPU acceleration for SHA-1/SHA-256 cpu_has_sha1_accel = DetectSHA1Acceleration(); cpu_has_sha256_accel = DetectSHA256Acceleration(); // FFU support started with Windows 10 1709 (through FfuProvider.dll) static_sprintf(tmp_path, "%s\\dism\\FfuProvider.dll", sysnative_dir); has_ffu_support = (_accessU(tmp_path, 0) == 0); relaunch: ubprintf("Localization set to '%s'", selected_locale->txt[0]); right_to_left_mode = ((selected_locale->ctrl_id) & LOC_RIGHT_TO_LEFT); // We always launch with the image options displaying image_options = IMOP_WINTOGO; image_option_txt[0] = 0; select_index = 0; safe_free(fido_url); SetProcessDefaultLayout(right_to_left_mode ? LAYOUT_RTL : 0); if (get_loc_data_file(loc_file, selected_locale)) WriteSettingStr(SETTING_LOCALE, selected_locale->txt[0]); if (!vc) { if (MessageBoxExU(NULL, lmprintf(MSG_296), lmprintf(MSG_295), MB_YESNO | MB_ICONWARNING | MB_IS_RTL | MB_SYSTEMMODAL, selected_langid) != IDYES) goto out; vc = TRUE; } /* * Create the main Window */ if (hDlg != NULL) // Make sure any previous dialog is destroyed (e.g. when switching languages) DestroyWindow(hDlg); hDlg = MyCreateDialog(hInstance, IDD_DIALOG, NULL, MainCallback); if (hDlg == NULL) { MessageBoxExU(NULL, "Could not create Window", "DialogBox failure", MB_ICONSTOP|MB_IS_RTL|MB_SYSTEMMODAL, selected_langid); goto out; } if ((relaunch_rc.left > -65536) && (relaunch_rc.top > -65536)) SetWindowPos(hDlg, HWND_TOP, relaunch_rc.left, relaunch_rc.top, 0, 0, SWP_NOSIZE); // Enable drag-n-drop through the message filter ChangeWindowMessageFilterEx(hDlg, WM_DROPFILES, MSGFLT_ADD, NULL); ChangeWindowMessageFilterEx(hDlg, WM_COPYDATA, MSGFLT_ADD, NULL); // CopyGlobalData is needed since we are running elevated ChangeWindowMessageFilterEx(hDlg, WM_COPYGLOBALDATA, MSGFLT_ADD, NULL); // Set the hook to automatically close Windows' "You need to format the disk in drive..." prompt SetAlertPromptMessages(); if (!SetAlertPromptHook()) uprintf("Warning: Could not set alert prompt hook"); ShowWindow(hDlg, SW_SHOWNORMAL); UpdateWindow(hDlg); // Do our own event processing and process "magic" commands while(GetMessage(&msg, NULL, 0, 0)) { static BOOL ctrl_without_focus = FALSE; BOOL no_focus = (msg.message == WM_SYSKEYDOWN) && !(msg.lParam & 0x20000000); // ****************************** // .,ABCDEFGHIJKLMNOPQRSTUVWXYZ+- // Sigh... The things one need to do to detect standalone use of the 'Alt' key. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam != VK_MENU)) alt_command = TRUE; else if (GetKeyState(VK_MENU) & 0x8000) alt_pressed = TRUE; // Ctrl-A => Select the log data if ((IsWindowVisible(hLogDialog)) && (GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN) && (msg.wParam == 'A')) { // Might also need ES_NOHIDESEL property if you want to select when not active Edit_SetSel(hLog, 0, -1); continue; } // Detecting Ctrl combination keypress while an operation is in progress is a super-mess // because Windows considers that our dialog doesn't have keyboard focus (WHYYYYYY?!?!?) // and therefore first sends a WM_SYSKEYDOWN (i.e. the message that is supposed to tell // you that Alt is being pressed, because why not also use it for a COMPLETELY DIFFERENT // FRIGGING KEY!!!) with bit 29 of msg.lParam cleared, along with the code for the Ctrl // key and *THEN* a subsequent message (still using WM_SYSKEYDOWN without Alt) with the // other key. So we need to detect both the messages in sequence... if (no_focus && (msg.wParam == VK_CONTROL)) ctrl_without_focus = op_in_progress; // Ctrl-L => Open/Close the log if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN))) && (msg.wParam == 'L')) { ctrl_without_focus = FALSE; SendMessage(hMainDialog, WM_COMMAND, IDC_LOG, 0); continue; } #if defined(_DEBUG) || defined(TEST) || defined(ALPHA) extern int TestHashes(void); // Ctrl-T => Alternate Test mode that doesn't require a full rebuild if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN))) && (msg.wParam == 'T')) { TestHashes(); continue; } #endif // Ctrl-P => Persistent log if ((ctrl_without_focus || ((GetKeyState(VK_CONTROL) & 0x8000) && (msg.message == WM_KEYDOWN))) && (msg.wParam == 'P')) { persistent_log = !persistent_log; WriteSettingBool(SETTING_PERSISTENT_LOG, persistent_log); PrintStatusTimeout(lmprintf(MSG_336), persistent_log); continue; } if (no_focus && (msg.wParam != VK_CONTROL)) ctrl_without_focus = FALSE; if (no_focus) continue; // Alt +/- => Increase or decrease thread priority for format/file-copy/wim-apply operations if ((msg.message == WM_SYSKEYDOWN) && ((msg.wParam == VK_OEM_PLUS) || (msg.wParam == VK_OEM_MINUS) || (msg.wParam == VK_ADD) || (msg.wParam == VK_SUBTRACT))) { int delta = ((msg.wParam == VK_OEM_PLUS) || (msg.wParam == VK_ADD)) ? +1 : -1; if (((delta == +1) && (default_thread_priority < THREAD_PRIORITY_HIGHEST)) || ((delta == -1) && (default_thread_priority > THREAD_PRIORITY_LOWEST))) { default_thread_priority += delta; WriteSetting32(SETTING_DEFAULT_THREAD_PRIORITY, default_thread_priority - THREAD_PRIORITY_ABOVE_NORMAL); if (format_thread != NULL) SetThreadPriority(format_thread, default_thread_priority); if (wim_thread != NULL) SetThreadPriority(wim_thread, default_thread_priority); } PrintStatus(STATUS_MSG_TIMEOUT, MSG_318, default_thread_priority); continue; } // The following cheat modes should not be enacted when an operation is in progress if (!op_in_progress) { // Alt-. => Enable USB enumeration debug if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == VK_OEM_PERIOD)) { usb_debug = !usb_debug; cdio_loglevel_default = usb_debug ? CDIO_LOG_DEBUG : CDIO_LOG_WARN; WriteSettingBool(SETTING_ENABLE_USB_DEBUG, usb_debug); PrintStatusTimeout(lmprintf(MSG_270), usb_debug); GetDevices(0); continue; } // Alt-, => Disable physical drive locking if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == VK_OEM_COMMA)) { lock_drive = !lock_drive; PrintStatusTimeout(lmprintf(MSG_282), lock_drive); continue; } // Alt-A => Toggle use of Rufus MBR for Windows boot if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'A')) { use_rufus_mbr = !use_rufus_mbr; WriteSettingBool(SETTING_DISABLE_RUFUS_MBR, !use_rufus_mbr); PrintStatusTimeout(lmprintf(MSG_349), use_rufus_mbr); continue; } // Alt-B => Toggle fake drive detection during bad blocks check // By default, Rufus will check for fake USB flash drives that mistakenly present // more capacity than they already have by looping over the flash. This check which // is enabled by default is performed by writing the block number sequence and reading // it back during the bad block check. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'B')) { detect_fakes = !detect_fakes; WriteSettingBool(SETTING_DISABLE_FAKE_DRIVES_CHECK, !detect_fakes); PrintStatusTimeout(lmprintf(MSG_256), detect_fakes); continue; } // Alt-C => Cycle USB port for currently selected device if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'C')) { int index = ComboBox_GetCurSel(hDeviceList); if (index >= 0) CyclePort(index); continue; } // Alt-D => Delete the subdirectory where Rufus keeps its files if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'D')) { static_sprintf(tmp_path, "%s\\%s", app_data_dir, FILES_DIR); PrintStatusDebug(STATUS_MSG_TIMEOUT, MSG_264, tmp_path); SHDeleteDirectoryExU(NULL, tmp_path, FOF_NO_UI); user_deleted_rufus_dir = TRUE; continue; } // Alt-E => Enhanced installation mode (allow dual UEFI/BIOS mode and FAT32 for Windows) if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'E')) { allow_dual_uefi_bios = !allow_dual_uefi_bios; WriteSettingBool(SETTING_ENABLE_WIN_DUAL_EFI_BIOS, allow_dual_uefi_bios); PrintStatusTimeout(lmprintf(MSG_266), allow_dual_uefi_bios); SetPartitionSchemeAndTargetSystem(FALSE); continue; } // Alt-F => Toggle detection of USB HDDs // By default Rufus does not list USB HDDs. This is a safety feature aimed at avoiding // unintentional formatting of backup drives instead of USB keys. // When enabled, Rufus will list and allow the formatting of USB HDDs. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'F')) { enable_HDDs = !enable_HDDs; PrintStatusTimeout(lmprintf(MSG_253), enable_HDDs); GetDevices(0); CheckDlgButton(hMainDialog, IDC_LIST_USB_HDD, enable_HDDs ? BST_CHECKED : BST_UNCHECKED); continue; } // Alt-G => Toggle detection of Virtual Disks // By default Rufus list Virtual Disks but some people use them for backup. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'G')) { enable_VHDs = !enable_VHDs; WriteSettingBool(SETTING_DISABLE_VHDS, !enable_VHDs); PrintStatusTimeout(lmprintf(MSG_308), enable_VHDs); GetDevices(0); continue; } // Alt-H => Toggle computation of SHA512 digest if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'H')) { enable_extra_hashes = !enable_extra_hashes; WriteSettingBool(SETTING_ENABLE_EXTRA_HASHES, enable_extra_hashes); PrintStatusTimeout(lmprintf(MSG_312), enable_extra_hashes); continue; } // Alt-I => Toggle ISO support // This is useful if you have an ISOHybrid image and you want to force Rufus to use // DD-mode when writing the data. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'I')) { enable_iso = !enable_iso; PrintStatusTimeout(lmprintf(MSG_262), enable_iso); if (image_path != NULL) { img_provided = TRUE; dont_display_image_name = TRUE; SendMessage(hDlg, WM_COMMAND, IDC_SELECT, 0); } continue; } // Alt J => Toggle Joliet support for ISO9660 images // Some ISOs (Ubuntu) have Joliet extensions but expect applications not to use them, // due to their reliance on filenames that are > 64 chars (the Joliet max length for // a file name). This option allows users to ignore Joliet when using such images. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'J')) { enable_joliet = !enable_joliet; PrintStatusTimeout(lmprintf(MSG_257), enable_joliet); continue; } // Alt K => Toggle Rock Ridge support for ISO9660 images if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'K')) { enable_rockridge = !enable_rockridge; PrintStatusTimeout(lmprintf(MSG_258), enable_rockridge); continue; } // Alt-L => Force Large FAT32 format to be used on < 32 GB drives if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'L')) { force_large_fat32 = !force_large_fat32; WriteSettingBool(SETTING_FORCE_LARGE_FAT32_FORMAT, force_large_fat32); PrintStatusTimeout(lmprintf(MSG_254), force_large_fat32); GetDevices(0); continue; } // Alt-M => Toggle the check for the 0x55 0xAA boot marker at offset 0x1fe. // This means that Rufus treats anything selected as a writeable DD image. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'M')) { ignore_boot_marker = !ignore_boot_marker; WriteSettingBool(SETTING_IGNORE_BOOT_MARKER, ignore_boot_marker); PrintStatusTimeout(lmprintf(MSG_319), ignore_boot_marker); continue; } // Alt N => Enable NTFS compression if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'N')) { enable_ntfs_compression = !enable_ntfs_compression; PrintStatusTimeout(lmprintf(MSG_260), enable_ntfs_compression); continue; } // Alt-O => Save from Optical drive to ISO if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'O')) { IsoSaveImage(); continue; } // Alt-P => Toggle GPT ESP to and from Basic Data type (Windows 10 or later) if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'P')) { int index = ComboBox_GetCurSel(hDeviceList); DWORD DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, index); if (ToggleEsp(DeviceNum, 0)) CyclePort(index); continue; } // Alt-Q => Enable file indexing (for file systems that support it) // For multiple reasons, file indexing is disabled by default if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Q')) { enable_file_indexing = !enable_file_indexing; WriteSettingBool(SETTING_ENABLE_FILE_INDEXING, enable_file_indexing); PrintStatusTimeout(lmprintf(MSG_290), enable_file_indexing); continue; } // Alt-R => Remove all the registry keys that may have been created by Rufus if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'R')) { PrintStatus(STATUS_MSG_TIMEOUT, DeleteRegistryKey(REGKEY_HKCU, COMPANY_NAME "\\" APPLICATION_NAME) ? MSG_248 : MSG_249); // Also try to delete the upper key (company name) if it's empty (don't care about the result) DeleteRegistryKey(REGKEY_HKCU, COMPANY_NAME); continue; } // Alt-S => Disable size limit for ISOs // By default, Rufus will not copy ISOs that are larger than in size than // the target USB drive. If this is enabled, the size check is disabled. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'S')) { size_check = !size_check; PrintStatusTimeout(lmprintf(MSG_252), size_check); GetDevices(0); continue; } // Alt-T => Preserve timestamps when extracting ISO files if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'T')) { preserve_timestamps = !preserve_timestamps; WriteSettingBool(SETTING_PRESERVE_TIMESTAMPS, preserve_timestamps); PrintStatusTimeout(lmprintf(MSG_269), preserve_timestamps); continue; } // Alt-U => Use PROPER size units, instead of this whole Kibi/Gibi nonsense if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'U')) { use_fake_units = !use_fake_units; WriteSettingBool(SETTING_USE_PROPER_SIZE_UNITS, !use_fake_units); PrintStatusTimeout(lmprintf(MSG_263), !use_fake_units); GetDevices(0); continue; } // Alt-V => Use VDS facilities for formatting if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'V')) { if (is_vds_available) { use_vds = !use_vds; WriteSettingBool(SETTING_USE_VDS, use_vds); PrintStatusTimeout("VDS", use_vds); } continue; } // Alt-W => Enable VMWare disk detection if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'W')) { enable_vmdk = !enable_vmdk; WriteSettingBool(SETTING_ENABLE_VMDK_DETECTION, enable_vmdk); PrintStatusTimeout(lmprintf(MSG_265), enable_vmdk); GetDevices(0); continue; } // Alt-X => Delete the NoDriveTypeAutorun key on exit (useful if the app crashed) // This key is used to disable Windows popup messages when an USB drive is plugged in. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'X')) { PrintStatus(2000, MSG_255); existing_key = FALSE; continue; } // Alt Y => Force the update check to be successful // This will set the reported current version of Rufus to 0.0.0.0 when performing an update // check, so that it always succeeds. This is useful for translators. if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Y')) { force_update = (force_update > 0) ? 0 : 1; PrintStatusTimeout(lmprintf(MSG_259), force_update); continue; } // Alt-Z => Zero the drive if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Z')) { zero_drive = TRUE; fast_zeroing = FALSE; // Simulate a button click for Start PostMessage(hDlg, WM_COMMAND, (WPARAM)IDC_START, 0); continue; } // Ctrl-Alt-Z => Zero the drive while trying to skip empty blocks if ((msg.message == WM_KEYDOWN) && (msg.wParam == 'Z') && (GetKeyState(VK_CONTROL) & 0x8000) && (GetKeyState(VK_MENU) & 0x8000)) { zero_drive = TRUE; fast_zeroing = TRUE; // Simulate a button click for Start PostMessage(hDlg, WM_COMMAND, (WPARAM)IDC_START, 0); continue; } // Other hazardous cheat modes require Ctrl + Alt // Ctrl-Alt-E => Expert Mode if ((msg.message == WM_KEYDOWN) && (msg.wParam == 'E') && (GetKeyState(VK_CONTROL) & 0x8000) && (GetKeyState(VK_MENU) & 0x8000)) { expert_mode = !expert_mode; WriteSettingBool(SETTING_EXPERT_MODE, expert_mode); PrintStatusTimeout(lmprintf(MSG_347), expert_mode); continue; } // Ctrl-Alt-F => List non USB removable drives such as eSATA, etc - CAUTION!!! if ((msg.message == WM_KEYDOWN) && (msg.wParam == 'F') && (GetKeyState(VK_CONTROL) & 0x8000) && (GetKeyState(VK_MENU) & 0x8000)) { list_non_usb_removable_drives = !list_non_usb_removable_drives; if (list_non_usb_removable_drives) { previous_enable_HDDs = enable_HDDs; enable_HDDs = TRUE; } else { enable_HDDs = previous_enable_HDDs; } CheckDlgButton(hMainDialog, IDC_LIST_USB_HDD, enable_HDDs ? BST_CHECKED : BST_UNCHECKED); PrintStatusTimeout(lmprintf(MSG_287), list_non_usb_removable_drives); uprintf("%sListing of non-USB removable drives %s", (list_non_usb_removable_drives) ? "CAUTION: " : "", (list_non_usb_removable_drives) ? "enabled" : "disabled"); if (list_non_usb_removable_drives) uprintf("By using this unofficial cheat mode you forfeit ANY RIGHT to complain if you lose valuable data!"); GetDevices(0); continue; } // Ctrl-Alt-Y => Force update check to be successful and ignore timestamp errors if ((msg.message == WM_KEYDOWN) && (msg.wParam == 'Y') && (GetKeyState(VK_CONTROL) & 0x8000) && (GetKeyState(VK_MENU) & 0x8000)) { force_update = (force_update > 0) ? 0 : 2; PrintStatusTimeout(lmprintf(MSG_259), force_update); continue; } } // Standalone 'Alt' key toggles progress report between percent, rate (if available) // and remaining time (if available) if (alt_pressed && !(GetKeyState(VK_MENU) & 0x8000)) { alt_pressed = FALSE; if (!alt_command) update_progress_type = (update_progress_type + 1) % UPT_MAX; alt_command = FALSE; } // Let the system handle dialog messages (e.g. those from the tab key) if (!IsDialogMessage(hDlg, &msg) && !IsDialogMessage(hLogDialog, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } if (hFidoDlg != NULL) SendMessage(hFidoDlg, WM_CLOSE, 0, 0); if (relaunch) { relaunch = FALSE; reinit_localization(); goto relaunch; } out: _chdirU(app_dir); // Destroy the hogger mutex first, so that the cmdline app can exit and we can delete it if (hogmutex != NULL) { ReleaseMutex(hogmutex); safe_closehandle(hogmutex); // Unconditional delete with retry, just in case... for (i = 0; (!DeleteFileA(cmdline_hogger)) && (i <= 10); i++) Sleep(200); } // Kill the update check thread if running if (update_check_thread != NULL) TerminateThread(update_check_thread, 1); if ((!external_loc_file) && (loc_file[0] != 0)) { if (!DeleteFileU(loc_file)) uprintf("Could not delete '%s': %s", loc_file, WindowsErrorString()); } DestroyAllTooltips(); ClrAlertPromptHook(); exit_localization(); safe_free(image_path); safe_free(archive_path); safe_free(locale_name); safe_free(update.download_url); safe_free(update.release_notes); safe_free(grub2_buf); safe_free(fido_url); safe_free(fido_script); safe_free(pe256ssp); if (argv != NULL) { for (i = 0; i < argc; i++) safe_free(argv[i]); safe_free(argv); } if (lgp_set) SetLGP(TRUE, &existing_key, ep_reg, "NoDriveTypeAutorun", 0); if ((!automount) && (!SetAutoMount(FALSE))) uprintf("Failed to restore AutoMount to disabled"); ubflush(); if (attached_console) { SetWindowPos(GetConsoleWindow(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); FreeConsole(); } CoUninitialize(); CLOSE_OPENED_LIBRARIES; safe_closehandle(mutex); uprintf("*** " APPLICATION_NAME " exit ***\n"); #ifdef _CRTDBG_MAP_ALLOC _CrtDumpMemoryLeaks(); #endif return 0; }