fix(hotkeys): attach to the foreground thread to focus the buried dialog
The autofill helper attaches the caller to the TARGET thread, but the dialog runs on our own thread, so the attach was a no-op and Windows kept refusing the foreground change - the dialog rose without focus. Attaching to the current foreground thread instead is what grants the call.
This commit is contained in:
@@ -272,12 +272,6 @@ type
|
||||
read FOnQuickSearchHotkey write FOnQuickSearchHotkey;
|
||||
end;
|
||||
|
||||
// Bring a window to the foreground despite the Win10/11 anti-focus-stealing
|
||||
// rules (AttachThreadInput + poll). Exposed because the host needs it to raise
|
||||
// a native dialog of ours that got buried — plain SetForegroundWindow is
|
||||
// refused when we are not the foreground process.
|
||||
function ForceForegroundWindow(ATargetHwnd: HWND): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@@ -313,6 +307,7 @@ function UnhookWinEvent(hWinEventHook: THandle): BOOL;
|
||||
// Defined further down but needed by the constructor / MinimizeToTray.
|
||||
function IsOwnProcessWindow(AHwnd: HWND): Boolean; forward;
|
||||
function IsShellWindow(AHwnd: HWND): Boolean; forward;
|
||||
function ForceForegroundWindow(ATargetHwnd: HWND): Boolean; forward;
|
||||
procedure ForegroundEventProc(hWinEventHook: THandle; AEvent: DWORD; AWnd: HWND;
|
||||
idObject, idChild: LongInt; idEventThread, dwmsEventTime: DWORD); stdcall; forward;
|
||||
|
||||
|
||||
@@ -514,10 +514,22 @@ begin
|
||||
if (LPopup <> 0) and (LPopup <> LHwnd) and IsWindow(LPopup) then
|
||||
begin
|
||||
ShowWindow(LPopup, SW_SHOW);
|
||||
// Not plain SetForegroundWindow: the hotkey is pressed from ANOTHER
|
||||
// application, so we are not the foreground process and Windows refuses
|
||||
// the call — the dialog was raised but never focused.
|
||||
ForceForegroundWindow(LPopup);
|
||||
// Windows refuses SetForegroundWindow to a process that isn't already in
|
||||
// front, and the hotkey comes from ANOTHER application. The way through is
|
||||
// to attach our input queue to the CURRENT FOREGROUND thread — not to the
|
||||
// dialog's, which is our own thread and would make the attach a no-op
|
||||
// (that is why the autofill helper, written for cross-process targets,
|
||||
// left the dialog raised but unfocused).
|
||||
var LFgThread := GetWindowThreadProcessId(GetForegroundWindow, nil);
|
||||
var LOurThread := GetCurrentThreadId;
|
||||
var LAttached := (LFgThread <> 0) and (LFgThread <> LOurThread) and
|
||||
AttachThreadInput(LOurThread, LFgThread, True);
|
||||
try
|
||||
BringWindowToTop(LPopup);
|
||||
SetForegroundWindow(LPopup);
|
||||
finally
|
||||
if LAttached then AttachThreadInput(LOurThread, LFgThread, False);
|
||||
end;
|
||||
// A tray balloon, not a toast: our own window is disabled and sits behind
|
||||
// the dialog, so nothing rendered in the page would be seen.
|
||||
FBridge.ShowBalloon('Finish the open dialog',
|
||||
|
||||
Reference in New Issue
Block a user