[ui] ask user if they want proceed when conflicting processes are found

* Rufus now checks for processes with handles opened on the drives/volumes before
  starting the format operation and asks the user if they want to continue.
* This mimics Windows' behaviour when formatting drives, and actually uses the
  same message as the one from shell32.dll.mui.
* Closes #773
This commit is contained in:
Pete Batard 2017-07-16 22:42:19 +01:00
parent bed889718e
commit b4a2c06a2e
8 changed files with 127 additions and 10 deletions

View File

@ -277,7 +277,7 @@ static void fix_config(const char* psz_fullpath, const char* psz_path, const cha
if ((iso_label != NULL) && (usb_label != NULL)) {
if (replace_in_token_data(src, (props->is_grub_cfg) ? "linuxefi" : "append",
iso_label, usb_label, TRUE) != NULL)
uprintf(" Patched %s: '%s' '%s'\n", src, iso_label, usb_label);
uprintf(" Patched %s: '%s' '%s'\n", src, iso_label, usb_label);
}
safe_free(iso_label);
safe_free(usb_label);
@ -301,7 +301,7 @@ static void fix_config(const char* psz_fullpath, const char* psz_path, const cha
safe_sprintf(iso_label, MAX_PATH, "cd9660:/dev/iso9660/%s", img_report.label);
safe_sprintf(usb_label, MAX_PATH, "msdosfs:/dev/msdosfs/%s", img_report.usb_label);
if (replace_in_token_data(src, "set", iso_label, usb_label, TRUE) != NULL)
uprintf(" Patched %s: '%s' '%s'\n", src, iso_label, usb_label);
uprintf(" Patched %s: '%s' '%s'\n", src, iso_label, usb_label);
}
safe_free(iso_label);
safe_free(usb_label);
@ -896,7 +896,7 @@ out:
fclose(fd);
fd = NULL;
safe_sprintf(path2, sizeof(path2), "%s\\syslinux.org", dest_dir);
uprintf("Renaming: %s %s", path, path2);
uprintf("Renaming: %s %s", path, path2);
IGNORE_RETVAL(rename(path, path2));
}
if (fd == NULL) {

View File

@ -276,6 +276,11 @@ static __inline int LoadStringU(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, i
{
int ret;
DWORD err = ERROR_INVALID_DATA;
if (nBufferMax == 0) {
// read-only pointer to resource mode is not supported
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
// coverity[returned_null]
walloc(lpBuffer, nBufferMax);
ret = LoadStringW(hInstance, uID, wlpBuffer, nBufferMax);
@ -461,6 +466,15 @@ static __inline BOOL DeleteFileU(const char* lpFileName)
return ret;
}
static __inline BOOL PathFileExistsU(char* szPath)
{
BOOL ret;
wconvert(szPath);
ret = PathFileExistsW(wszPath);
wfree(szPath);
return ret;
}
static __inline int PathGetDriveNumberU(char* lpPath)
{
int ret = 0;

View File

@ -384,7 +384,7 @@ BYTE SearchProcess(char* HandleName, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL
// If we're switching process and found a match, print it
if (bFound) {
vuprintf("● '%s' (pid: %ld, access: %s)", exe_path, pid[cur_pid], access_rights_str[access_rights & 0x7]);
static_sprintf(tmp, "● %s (pid %ld)", exe_path, pid[cur_pid]);
static_sprintf(tmp, "● %s (%s)", exe_path, access_rights_str[access_rights & 0x7]);
StrArrayAdd(&BlockingProcess, tmp, TRUE);
bFound = FALSE;
access_rights = 0;

View File

@ -2153,6 +2153,65 @@ static void SaveISO(void)
}
}
// Check for conflicting processes accessing the drive, and if any,
// ask the user whether they want to proceed.
static BOOL CheckDriveAccess(void)
{
uint32_t i, j;
BOOL bProceed = TRUE;
BYTE access_mask;
char *PhysicalPath, DevPath[MAX_PATH];
char drive_letter[27], drive_name[] = "?:";
char *message, title[128];
// Get the current selected device
DWORD DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList));
if ((DeviceNum < 0x80) || (DeviceNum == (DWORD)-1))
return FALSE;
// Search for any blocking processes against the physical drive
PhysicalPath = GetPhysicalName(DeviceNum);
QueryDosDeviceA(&PhysicalPath[4], DevPath, sizeof(DevPath));
access_mask = SearchProcess(DevPath, TRUE, TRUE, TRUE);
if (access_mask != 0) {
bProceed = FALSE;
uprintf("Found potentially blocking process(es) against %s:", &PhysicalPath[4]);
for (j = 0; j < BlockingProcess.Index; j++)
uprintf(BlockingProcess.String[j]);
}
free(PhysicalPath);
// Search for any blocking processes against the logical volume(s)
GetDriveLetters(DeviceNum, drive_letter);
for (i = 0; drive_letter[i]; i++) {
drive_name[0] = drive_letter[i];
if (QueryDosDeviceA(drive_name, DevPath, sizeof(DevPath)) != 0) {
StrArrayClear(&BlockingProcess);
access_mask = SearchProcess(DevPath, TRUE, TRUE, TRUE);
// Ignore if all we have is read-only
if ((access_mask & 0x06) || (access_mask == 0x80)) {
bProceed = FALSE;
uprintf("Found potentially blocking process(es) against %s", drive_name);
for (j = 0; j < BlockingProcess.Index; j++)
uprintf(BlockingProcess.String[j]);
}
}
}
// Prompt the user if we detected blocking processes
if (!bProceed) {
// We'll use a system translated string instead of one from rufus.loc
message = GetMuiString("shell32.dll", 28701); // "This drive is in use (...) Do you want to format it anyway?"
if (message != NULL) {
ComboBox_GetTextU(hDeviceList, title, sizeof(title));
bProceed = Notification(MSG_WARNING_QUESTION, NULL, title, message);
free(message);
}
}
return bProceed;
}
#ifdef RUFUS_TEST
extern int SelectionDyn(char* title, char* message, char** szChoice, int nChoices);
#endif
@ -2189,7 +2248,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
case WM_COMMAND:
#ifdef RUFUS_TEST
if (LOWORD(wParam) == IDC_TEST) {
SearchProcess("\\Device\\Harddisk5\\DR5", TRUE, TRUE);
uprintf("Proceed = %s", CheckDriveAccess()?"True":"False");
// char* choices[] = { "Choice 1", "Choice 2", "Choice 3" };
// SelectionDyn("Test Choice", "Unused", choices, ARRAYSIZE(choices));
break;
@ -2509,6 +2568,13 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
}
}
if (!CheckDriveAccess()) {
format_op_in_progress = FALSE;
zero_drive = FALSE;
PROCESS_QUEUED_EVENTS;
break;
}
GetWindowTextU(hDeviceList, tmp, ARRAYSIZE(tmp));
if (MessageBoxExU(hMainDialog, lmprintf(MSG_003, tmp),
APPLICATION_NAME, MB_OKCANCEL|MB_ICONWARNING|MB_IS_RTL, selected_langid) == IDCANCEL) {

View File

@ -160,6 +160,7 @@ enum notification_type {
MSG_WARNING,
MSG_ERROR,
MSG_QUESTION,
MSG_WARNING_QUESTION
};
typedef INT_PTR (CALLBACK *Callback_t)(HWND, UINT, WPARAM, LPARAM);
typedef struct {
@ -493,6 +494,7 @@ extern BOOL IsBufferInDB(const unsigned char* buf, const size_t len);
extern char* _printbits(size_t const size, void const * const ptr, int leading_zeroes);
extern BOOL IsCurrentProcessElevated(void);
extern char* GetCurrentMUI(void);
extern char* GetMuiString(char* szModuleName, UINT uID);
extern BOOL SetFormatPromptHook(void);
extern void ClrFormatPromptHook(void);
extern BYTE SearchProcess(char* HandleName, BOOL bPartialMatch, BOOL bIgnoreSelf, BOOL bQuiet);

View File

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 242, 376
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 2.16.1126"
CAPTION "Rufus 2.16.1127"
FONT 8, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8
@ -366,8 +366,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,16,1126,0
PRODUCTVERSION 2,16,1126,0
FILEVERSION 2,16,1127,0
PRODUCTVERSION 2,16,1127,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -384,13 +384,13 @@ BEGIN
BEGIN
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "2.16.1126"
VALUE "FileVersion", "2.16.1127"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2017 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "2.16.1126"
VALUE "ProductVersion", "2.16.1127"
END
END
BLOCK "VarFileInfo"

View File

@ -909,3 +909,35 @@ char* GetCurrentMUI(void)
}
return mui_str;
}
char* GetMuiString(char* szModuleName, UINT uID)
{
HMODULE hModule;
char path[MAX_PATH], *str;
wchar_t* wstr;
void* ptr;
int len;
static_sprintf(path, "%s\\%s\\%s.mui", system_dir, GetCurrentMUI(), szModuleName);
// If the file doesn't exist, fall back to en-US
if (!PathFileExistsU(path))
static_sprintf(path, "%s\\en-US\\%s.mui", system_dir, szModuleName);
hModule = LoadLibraryExA(path, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
if (hModule == NULL) {
uprintf("Could not load '%s': %s", path, WindowsErrorString());
return NULL;
}
// Calling LoadStringW with last parameter 0 returns the length of the string (without NUL terminator)
len = LoadStringW(hModule, uID, (LPWSTR)(&ptr), 0);
if (len <= 0) {
if (GetLastError() == ERROR_SUCCESS)
SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
uprintf("Could not find string ID %d in '%s': %s", uID, path, WindowsErrorString());
return NULL;
}
len += 1;
wstr = calloc(len, sizeof(wchar_t));
len = LoadStringW(hModule, uID, wstr, len);
str = wchar_to_utf8(wstr);
free(wstr);
return str;
}

View File

@ -823,6 +823,9 @@ BOOL Notification(int type, const notification_info* more_info, char* title, cha
notification_is_question = FALSE;
switch(type) {
case MSG_WARNING_QUESTION:
notification_is_question = TRUE;
// Fall through
case MSG_WARNING:
hMessageIcon = LoadIcon(NULL, IDI_WARNING);
break;