From de04285ddf00fbcade45df4d91792ad672484c1a Mon Sep 17 00:00:00 2001 From: amail80 <261782908+amail80@users.noreply.github.com> Date: Sat, 17 Oct 2026 08:09:03 +0500 Subject: [PATCH] Windows: flush shell notifications when dismounting with /q (#1852) * Windows: flush shell notifications when dismounting with /q * Windows: limit the /q notification flush to the command line dismount ProcessExitsAfterCommand was set while parsing /q and stayed set when the process went on as the background task (/q background, and /q preferences, which is also used at logon). Later interactive dismounts, automatic dismounts through DismountAll and the hotkeys then flushed as well, which would also delay the cache wipe that the force dismount and wipe hotkeys issue after DismountAll. Set the flag only for the dismount requested on the command line and clear it right after, whatever Silent is. Rename it to FlushDriveRemovalNotifications, as /q doesn't necessarily end the process. Reword the comments: according to the documentation, SHCNF_FLUSH makes SHChangeNotify return once the notification has been delivered, which doesn't mean that Explorer has finished updating its display, and the cause of the reported ghost drive letters hasn't been established. --- src/Common/Dlgcode.c | 18 +++++++++++------- src/Common/Dlgcode.h | 1 + src/Mount/Mount.c | 12 ++++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index 94121ec7..d3a57fe6 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -162,6 +162,10 @@ HWND MainDlg = NULL; wchar_t *lpszTitle = NULL; BOOL Silent = FALSE; +/* Set by Mount.c only while it carries out a dismount requested on the command line + with /q. UnmountVolumeBase and DismountAll then send the drive removal notification + again with SHCNF_FLUSH, as they do when Silent is set. */ +BOOL FlushDriveRemovalNotifications = FALSE; BOOL bPreserveTimestamp = TRUE; BOOL bShowDisconnectedNetworkDrives = FALSE; BOOL bHideWaitingDialog = FALSE; @@ -9826,13 +9830,13 @@ retry: BroadcastDeviceChange (DBT_DEVICEREMOVECOMPLETE, nDosDriveNo, 0); - /* GH #337, GH #1426: When running in silent/CLI mode, the process may - exit immediately after unmount. BroadcastDeviceChange sends - SHChangeNotify asynchronously, so Explorer may not process the drive - removal before the process exits, leaving a ghost drive letter. - Re-send the notification with SHCNF_FLUSH to force synchronous - processing by Explorer before we return. */ - if (Silent && !DeviceChangeBroadcastDisabled) + /* GH #337, GH #1426: ghost drive letters were reported after dismounts + requested on the command line; their cause hasn't been established. + When Silent or FlushDriveRemovalNotifications is set, the notification + is sent again with SHCNF_FLUSH. According to the documentation, + SHChangeNotify then returns only once it has been delivered, which + doesn't mean that Explorer has finished updating its display. */ + if ((Silent || FlushDriveRemovalNotifications) && !DeviceChangeBroadcastDisabled) { wchar_t root[] = { (wchar_t) (nDosDriveNo + L'A'), L':', L'\\', 0 }; SHChangeNotify (SHCNE_DRIVEREMOVED, SHCNF_PATH | SHCNF_FLUSH, root, NULL); diff --git a/src/Common/Dlgcode.h b/src/Common/Dlgcode.h index 6ebb6c53..47271e8c 100644 --- a/src/Common/Dlgcode.h +++ b/src/Common/Dlgcode.h @@ -133,6 +133,7 @@ extern int ScreenDPI; extern double DlgAspectRatio; extern HWND MainDlg; extern BOOL Silent; +extern BOOL FlushDriveRemovalNotifications; extern BOOL bHistory; extern BOOL bPreserveTimestamp; extern BOOL bShowDisconnectedNetworkDrives; diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c index d99d9306..511415b5 100644 --- a/src/Mount/Mount.c +++ b/src/Mount/Mount.c @@ -6351,10 +6351,9 @@ retry: if (removedDrives) BroadcastDeviceChange (DBT_DEVICEREMOVECOMPLETE, 0, removedDrives); - /* GH #337, GH #1426: Flush shell notifications synchronously in - silent/CLI mode to prevent ghost drive letters when the process - exits immediately after dismount. */ - if (Silent && !DeviceChangeBroadcastDisabled) + /* GH #337, GH #1426: as in UnmountVolumeBase, send the notifications again + with SHCNF_FLUSH when Silent or FlushDriveRemovalNotifications is set. */ + if ((Silent || FlushDriveRemovalNotifications) && !DeviceChangeBroadcastDisabled) { for (i = 0; i < 26; i++) { @@ -8446,6 +8445,10 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa } // Dismount + /* With /q, flush the drive removal notifications of this dismount even + without /s (see UnmountVolumeBase). Only for this request: a process + that goes on as the background task doesn't flush later dismounts. */ + FlushDriveRemovalNotifications = Quit; if (cmdUnmountDrive >= 0) { MOUNT_LIST_STRUCT mountList; @@ -8467,6 +8470,7 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa if (!DismountAll (hwndDlg, bForceUnmount, !Silent, UNMOUNT_MAX_AUTO_RETRIES, UNMOUNT_AUTO_RETRY_DELAY)) exitCode = 1; } + FlushDriveRemovalNotifications = FALSE; // TaskBar icon if (bEnableBkgTask)