From 9dd06e93bc8aed921b11bed4ab65e460ed4a7b06 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 9 Jul 2016 16:20:58 +0100 Subject: [PATCH] [ui] improve Windows format prompt autoclose * Closes #794 * Also reorder some messages (so that RUFUS_TEST comes first) * Also update issue template --- .github/ISSUE_TEMPLATE.md | 4 +- src/format.c | 47 +------ src/msapi_utf8.h | 23 ++++ src/rufus.c | 274 +++++++++++++++++--------------------- src/rufus.h | 4 + src/rufus.rc | 10 +- src/stdfn.c | 60 +++++++++ src/stdlg.c | 84 ++++++++++++ 8 files changed, 299 insertions(+), 207 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 8f3cb2e4..55f26edd 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,7 +1,7 @@ + + Checklist --------- - - - [ ] I looked at https://github.com/pbatard/rufus/wiki/FAQ to see if my question has already been answered. - [ ] I performed a search in the issue tracker for similar issues, using keywords relevant to my problem. - [ ] I clicked the `Log` button in Rufus and copy/pasted the log into the line that says `` below. diff --git a/src/format.c b/src/format.c index 2093bf56..84ae97e8 100644 --- a/src/format.c +++ b/src/format.c @@ -825,7 +825,7 @@ static BOOL ClearMBRGPT(HANDLE hPhysicalDrive, LONGLONG DiskSize, DWORD SectorSi // Also, for various reasons (one of which being that Windows seems to have issues // with GPT drives that contain a lot of small partitions) we try not not to clear // sectors further than the lowest partition already residing on the disk. - num_sectors_to_clear = min(SelectedDrive.FirstDataSector, (add1MB ? 2048 : 0) + MAX_SECTORS_TO_CLEAR); + num_sectors_to_clear = min(SelectedDrive.FirstDataSector, (DWORD)((add1MB ? 2048 : 0) + MAX_SECTORS_TO_CLEAR)); uprintf("Erasing %d sectors", num_sectors_to_clear); for (i=0; i LastRefresh + MAX_REFRESH) { @@ -1687,7 +1643,6 @@ DWORD WINAPI FormatThread(void* param) FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_PARTITION_FAILURE; goto out; } - CreateThread(NULL, 0, CloseFormatPromptThread, NULL, 0, NULL); if (IsChecked(IDC_BADBLOCKS)) { do { diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 44174f79..aeca575f 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -226,6 +226,21 @@ static __inline int MessageBoxExU(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UI return ret; } +static __inline int LoadStringU(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int nBufferMax) +{ + int ret; + DWORD err = ERROR_INVALID_DATA; + walloc(lpBuffer, nBufferMax); + ret = LoadStringW(hInstance, uID, wlpBuffer, nBufferMax); + err = GetLastError(); + if ((ret > 0) && ((ret = wchar_to_utf8_no_alloc(wlpBuffer, lpBuffer, nBufferMax)) == 0)) { + err = GetLastError(); + } + wfree(lpBuffer); + SetLastError(err); + return ret; +} + static __inline int DrawTextU(HDC hDC, LPCSTR lpText, int nCount, LPRECT lpRect, UINT uFormat) { int ret; @@ -917,6 +932,14 @@ static __inline BOOL GetVolumeInformationU(LPCSTR lpRootPathName, LPSTR lpVolume return ret; } +static __inline HMODULE LoadLibraryU(LPCSTR lpFileName) +{ + HMODULE h; + wconvert(lpFileName); + h = LoadLibraryW(wlpFileName); + wfree(lpFileName); + return h; +} #ifdef __cplusplus } diff --git a/src/rufus.c b/src/rufus.c index 22eae455..973b7067 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -2076,115 +2076,13 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA switch (message) { - 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_thid == 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 (!format_op_in_progress) { - queued_hotplug_event = FALSE; - GetDevices((DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList))); - user_changed_label = FALSE; - } 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 (_GetTickCount64() > LastRefresh + 1000) { - LastRefresh = _GetTickCount64(); - SetTimer(hMainDialog, TID_REFRESH_TIMER, 1000, RefreshTimer); - } - break; - default: - break; - } - } - break; - - case WM_INITDIALOG: - PF_INIT(SHChangeNotifyRegister, shell32); - // 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; - if (hDC != NULL) - ReleaseDC(hDlg, hDC); - apply_localization(IDD_DIALOG, hDlg); - SetUpdateCheck(); - togo_mode = TRUE; // We display the ToGo controls by default and need to hide them - // Create the log window (hidden) - first_log_display = TRUE; - log_displayed = FALSE; - hLogDlg = MyCreateDialog(hMainInstance, IDD_LOG, hDlg, (DLGPROC)LogProc); - InitDialog(hDlg); - GetDevices(0); - CheckForUpdates(FALSE); - // Register MEDIA_INSERTED/MEDIA_REMOVED notifications for card readers - if ((pfSHChangeNotifyRegister != NULL) && (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 = pfSHChangeNotifyRegister(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 ISO', instead - SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)(IsWindowEnabled(hStart) ? hStart : hSelectISO), 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 - return (INT_PTR)FALSE; - - // The things one must do to get an ellipsis and text alignment on the status bar... - case WM_DRAWITEM: - if (wParam == IDC_STATUS) { - pDI = (DRAWITEMSTRUCT*)lParam; - pDI->rcItem.top -= (int)((4.0f * fScale) - 6.0f); - pDI->rcItem.left += (int)(((pDI->itemID == SB_SECTION_MIDDLE)?-2.0f: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_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; @@ -2277,12 +2175,6 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA // Must come last for the log window to get focus ShowWindow(hLogDlg, log_displayed?SW_SHOW:SW_HIDE); break; -#ifdef RUFUS_TEST - case IDC_TEST: - { - break; - } -#endif case IDC_ADVANCED: advanced_mode = !advanced_mode; WriteSettingBool(SETTING_ADVANCED_MODE, advanced_mode); @@ -2578,6 +2470,115 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA } return (INT_PTR)TRUE; + 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_thid == 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 (!format_op_in_progress) { + queued_hotplug_event = FALSE; + GetDevices((DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList))); + user_changed_label = FALSE; + } + 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 (_GetTickCount64() > LastRefresh + 1000) { + LastRefresh = _GetTickCount64(); + SetTimer(hMainDialog, TID_REFRESH_TIMER, 1000, RefreshTimer); + } + break; + default: + break; + } + } + break; + + case WM_INITDIALOG: + PF_INIT(SHChangeNotifyRegister, shell32); + // 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; + if (hDC != NULL) + ReleaseDC(hDlg, hDC); + apply_localization(IDD_DIALOG, hDlg); + SetUpdateCheck(); + togo_mode = TRUE; // We display the ToGo controls by default and need to hide them + // Create the log window (hidden) + first_log_display = TRUE; + log_displayed = FALSE; + hLogDlg = MyCreateDialog(hMainInstance, IDD_LOG, hDlg, (DLGPROC)LogProc); + InitDialog(hDlg); + GetDevices(0); + CheckForUpdates(FALSE); + // Register MEDIA_INSERTED/MEDIA_REMOVED notifications for card readers + if ((pfSHChangeNotifyRegister != NULL) && (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 = pfSHChangeNotifyRegister(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 ISO', instead + SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)(IsWindowEnabled(hStart) ? hStart : hSelectISO), 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 + return (INT_PTR)FALSE; + + // The things one must do to get an ellipsis and text alignment on the status bar... + case WM_DRAWITEM: + if (wParam == IDC_STATUS) { + pDI = (DRAWITEMSTRUCT*)lParam; + pDI->rcItem.top -= (int)((4.0f * fScale) - 6.0f); + pDI->rcItem.left += (int)(((pDI->itemID == SB_SECTION_MIDDLE) ? -2.0f : 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_NOTIFY: switch (((LPNMHDR)lParam)->code) { case TBN_DROPDOWN: @@ -2784,46 +2785,6 @@ static HANDLE SetHogger(BOOL attached_console, BOOL disable_hogger) return hogmutex; } -/* - * Returns true if: - * 1. The OS supports UAC, UAC is on, and the current process runs elevated, or - * 2. The OS doesn't support UAC or UAC is off, and the process is being run by a member of the admin group - */ -static BOOL IsCurrentProcessElevated(void) -{ - BOOL r = FALSE; - DWORD size; - HANDLE token = INVALID_HANDLE_VALUE; - TOKEN_ELEVATION te; - SID_IDENTIFIER_AUTHORITY auth = { SECURITY_NT_AUTHORITY }; - PSID psid; - - if (ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\EnableLUA") == 1) { - uprintf("NOTE: UAC is on"); - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { - uprintf("Could not get current process token: %s", WindowsErrorString()); - goto out; - } - if (!GetTokenInformation(token, TokenElevation, &te, sizeof(te), &size)) { - uprintf("Could not get token information: %s", WindowsErrorString()); - goto out; - } - r = (te.TokenIsElevated != 0); - } else { - uprintf("NOTE: UAC is either disabled or not available"); - if (!AllocateAndInitializeSid(&auth, 2, SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psid)) - goto out; - if (!CheckTokenMembership(NULL, psid, &r)) - r = FALSE; - FreeSid(psid); - } - -out: - safe_closehandle(token); - return r; -} - /* * Application Entrypoint @@ -3135,6 +3096,10 @@ relaunch: } } + // Set the hook to automatically close Windows' "You need to format the disk in drive..." prompt + if (!SetFormatPromptHook()) + uprintf("Warning:Could not set 'Format Disk' prompt auto-close"); + ShowWindow(hDlg, SW_SHOWNORMAL); UpdateWindow(hDlg); @@ -3348,6 +3313,7 @@ out: if ((!external_loc_file) && (loc_file[0] != 0)) DeleteFileU(loc_file); DestroyAllTooltips(); + ClrFormatPromptHook(); exit_localization(); safe_free(image_path); safe_free(locale_name); diff --git a/src/rufus.h b/src/rufus.h index 7f26ee60..59e42c68 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -465,6 +465,10 @@ extern BOOL IsBufferInDB(const unsigned char* buf, const size_t len); #define printbits(x) _printbits(sizeof(x), &x, 0) #define printbitslz(x) _printbits(sizeof(x), &x, 1) extern char* _printbits(size_t const size, void const * const ptr, int leading_zeroes); +extern BOOL IsCurrentProcessElevated(void); +extern char* GetCurrentMUI(void); +extern BOOL SetFormatPromptHook(void); +extern void ClrFormatPromptHook(void); DWORD WINAPI FormatThread(void* param); DWORD WINAPI SaveImageThread(void* param); diff --git a/src/rufus.rc b/src/rufus.rc index b75591e4..7e65d9a5 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -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.10.968" +CAPTION "Rufus 2.10.969" FONT 8, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8 @@ -320,8 +320,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,10,968,0 - PRODUCTVERSION 2,10,968,0 + FILEVERSION 2,10,969,0 + PRODUCTVERSION 2,10,969,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -338,13 +338,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "2.10.968" + VALUE "FileVersion", "2.10.969" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2016 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "2.10.968" + VALUE "ProductVersion", "2.10.969" END END BLOCK "VarFileInfo" diff --git a/src/stdfn.c b/src/stdfn.c index 52a3f4be..2d45db56 100644 --- a/src/stdfn.c +++ b/src/stdfn.c @@ -36,6 +36,8 @@ extern BOOL usb_debug; // For uuprintf int nWindowsVersion = WINDOWS_UNDEFINED; char WindowsVersionStr[128] = "Windows "; +PF_TYPE_DECL(WINAPI, int, LCIDToLocaleName, (LCID, LPWSTR, int, DWORD)); + /* * Hash table functions - modified From glibc 2.3.2: * [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 @@ -847,3 +849,61 @@ BOOL SetThreadAffinity(DWORD_PTR* thread_affinity, size_t num_threads) uuprintf(" thr_%d:\t%s", i, printbitslz(thread_affinity[i])); return TRUE; } + +/* + * Returns true if: + * 1. The OS supports UAC, UAC is on, and the current process runs elevated, or + * 2. The OS doesn't support UAC or UAC is off, and the process is being run by a member of the admin group + */ +BOOL IsCurrentProcessElevated(void) +{ + BOOL r = FALSE; + DWORD size; + HANDLE token = INVALID_HANDLE_VALUE; + TOKEN_ELEVATION te; + SID_IDENTIFIER_AUTHORITY auth = { SECURITY_NT_AUTHORITY }; + PSID psid; + + if (ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\EnableLUA") == 1) { + uprintf("Note: UAC is active"); + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) { + uprintf("Could not get current process token: %s", WindowsErrorString()); + goto out; + } + if (!GetTokenInformation(token, TokenElevation, &te, sizeof(te), &size)) { + uprintf("Could not get token information: %s", WindowsErrorString()); + goto out; + } + r = (te.TokenIsElevated != 0); + } + else { + uprintf("Note: UAC is either disabled or not available"); + if (!AllocateAndInitializeSid(&auth, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psid)) + goto out; + if (!CheckTokenMembership(NULL, psid, &r)) + r = FALSE; + FreeSid(psid); + } + +out: + safe_closehandle(token); + return r; +} + +char* GetCurrentMUI(void) +{ + static char mui_str[LOCALE_NAME_MAX_LENGTH]; + wchar_t wmui_str[LOCALE_NAME_MAX_LENGTH]; + + // Of course LCIDToLocaleName() is not available on XP... grrrr! + PF_INIT(LCIDToLocaleName, kernel32); + + if ( (pfLCIDToLocaleName != NULL) && + (pfLCIDToLocaleName(GetUserDefaultUILanguage(), wmui_str, LOCALE_NAME_MAX_LENGTH, 0) > 0) ) { + wchar_to_utf8_no_alloc(wmui_str, mui_str, LOCALE_NAME_MAX_LENGTH); + } else { + safe_strcpy(mui_str, LOCALE_NAME_MAX_LENGTH, "en-US"); + } + return mui_str; +} diff --git a/src/stdlg.c b/src/stdlg.c index 5cabc896..fcaaf38b 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -63,6 +63,8 @@ static BOOL notification_is_question; static const notification_info* notification_more_info; static BOOL settings_commcheck = FALSE; static WNDPROC update_original_proc = NULL; +static HWINEVENTHOOK fp_weh = NULL; +static char *fp_title_str = "Microsoft Windows", *fp_button_str = "Format disk"; extern loc_cmd* selected_locale; @@ -1725,3 +1727,85 @@ INT_PTR MyDialogBox(HINSTANCE hInstance, int Dialog_ID, HWND hWndParent, DLGPROC safe_free(rcTemplate); return ret; } + +/* + * The following function calls are used to automatically detect and close the native + * Windows format prompt "You must format the disk in drive X:". To do that, we use an + * event hook that gets triggered whenever a window is placed in the foreground. + * In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant + * title. However, because the title in itself is too generic (the expectation is that + * it will be "Microsoft Windows") we also enumerate all the child controls from that + * prompt, using another callback, until we find one that contains the text we expect + * for the "Format disk" button. + * Oh, and since all of these strings are localized, we must first pick them up from + * the relevant mui (something like "C:\Windows\System32\en-GB\shell32.dll.mui") + */ +static BOOL CALLBACK FormatPromptCallback(HWND hWnd, LPARAM lParam) +{ + char str[128]; + BOOL *found = (BOOL*)lParam; + + if (GetWindowTextU(hWnd, str, sizeof(str)) == 0) + return TRUE; + if (safe_strcmp(str, fp_button_str) == 0) + *found = TRUE; + return TRUE; +} + +static void CALLBACK FormatPromptHook(HWINEVENTHOOK hWinEventHook, DWORD Event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) +{ + char str[128]; + BOOL found; + + if (Event == EVENT_SYSTEM_FOREGROUND) { + if (GetWindowLong(hWnd, GWL_STYLE) & WS_POPUPWINDOW) { + str[0] = 0; + GetWindowTextU(hWnd, str, sizeof(str)); + if (safe_strcmp(str, fp_title_str) == 0) { + found = FALSE; + EnumChildWindows(hWnd, FormatPromptCallback, (LPARAM)&found); + if (found) { + SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); + uprintf("Closed Windows format prompt"); + } + } + } + } +} + +BOOL SetFormatPromptHook(void) +{ + HMODULE mui_lib; + char mui_path[MAX_PATH]; + static char title_str[128], button_str[128]; + + if (fp_weh != NULL) + return TRUE; // No need to set again if active + + // Fetch the localized strings in the relevant + static_sprintf(mui_path, "%s\\%s\\shell32.dll.mui", system_dir, GetCurrentMUI()); + mui_lib = LoadLibraryU(mui_path); + if (mui_lib != NULL) { + // 4097 = "You need to format the disk in drive %c: before you can use it." (dialog text) + // 4125 = "Microsoft Windows" (dialog title) + // 4126 = "Format disk" (button) + if (LoadStringU(mui_lib, 4125, title_str, sizeof(title_str)) > 0) + fp_title_str = title_str; + else + uprintf("Warning: Could not locate localized format prompt title string in '%s': %s", mui_path, WindowsErrorString()); + if (LoadStringU(mui_lib, 4126, button_str, sizeof(button_str)) > 0) + fp_button_str = button_str; + else + uprintf("Warning: Could not locate localized format prompt button string in '%s': %s", mui_path, WindowsErrorString()); + FreeLibrary(mui_lib); + } + + fp_weh = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL, + FormatPromptHook, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); + return (fp_weh != NULL); +} + +void ClrFormatPromptHook(void) { + UnhookWinEvent(fp_weh); + fp_weh = NULL; +}