fix(quick-search): keep the open window visible during the fill (no minimize)

The minimize-then-SW_SHOWNOACTIVATE approach flickered and sometimes lost the
restore race (window stayed minimized). Root fix: when the window was open
before the hotkey (ARestoreAfter), skip the minimize entirely — being the
foreground process is exactly what allows handing focus to the target, so the
window simply stays in place beside it. Tray-origin flow keeps the old
minimize (the window is a temporary overlay, trayed after the fill anyway).

Safety: if the target refuses the foreground (elevated / UIPI) and our window
is still foreground, bail before typing — otherwise the password would be
typed into the vault's own visible UI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-11 20:01:05 +01:00
parent 3b32a621e8
commit 7103fbf703
+40 -39
View File
@@ -165,9 +165,10 @@ type
// without a stored username. // without a stored username.
// AUsernameOnly = True → type ONLY the username (no Tab, no password); // AUsernameOnly = True → type ONLY the username (no Tab, no password);
// used by the quick-search "autofill username" action. // used by the quick-search "autofill username" action.
// ARestoreAfter = True → if the fill had to minimise our own window (it // ARestoreAfter = True → the window was open before the hotkey: keep it
// was foreground, e.g. quick-search pick), show it again afterwards // visible (no minimize at all — the foreground process is allowed to hand
// WITHOUT activating it, so an app the user had open doesn't vanish. // focus to the target). False (tray-origin) → minimize out of the way,
// the caller trays it after the fill.
procedure ExecuteAutofill(ATargetHWND: HWND; procedure ExecuteAutofill(ATargetHWND: HWND;
const AUsername, APassword: string; AUsernameOnly: Boolean = False; const AUsername, APassword: string; AUsernameOnly: Boolean = False;
ARestoreAfter: Boolean = False); ARestoreAfter: Boolean = False);
@@ -1145,59 +1146,59 @@ const
FocusSettleDelayMs = 120; FocusSettleDelayMs = 120;
var var
OwnFormHwnd: HWND; OwnFormHwnd: HWND;
LMinimizedSelf: Boolean;
begin begin
OwnFormHwnd := MainFormHWND(FMainForm); OwnFormHwnd := MainFormHWND(FMainForm);
LMinimizedSelf := False; // ARestoreAfter (window was open before the hotkey): DON'T minimize at all.
if GetForegroundWindow = OwnFormHwnd then // Being the foreground process is precisely what lets us hand the focus to
// the target via ForceForegroundWindow — the window just stays where it is,
// beside the target. (The old minimize-then-restore flickered and sometimes
// lost the restore race.) Without ARestoreAfter (tray-origin flow), the
// restored window is a temporary overlay: shove it out of the way as before
// — the caller trays it after the fill anyway.
if (GetForegroundWindow = OwnFormHwnd) and (not ARestoreAfter) then
begin begin
ShowWindow(OwnFormHwnd, SW_MINIMIZE); ShowWindow(OwnFormHwnd, SW_MINIMIZE);
LMinimizedSelf := True;
Sleep(MinimizeSettleMs); Sleep(MinimizeSettleMs);
end; end;
try if ATargetHWND <> 0 then
if ATargetHWND <> 0 then ForceForegroundWindow(ATargetHWND);
ForceForegroundWindow(ATargetHWND);
WaitForModifierRelease(1000); WaitForModifierRelease(1000);
Sleep(FocusSettleDelayMs); Sleep(FocusSettleDelayMs);
// Username-only: type just the username into the focused field, no Tab, // Never type into our own window: if the target refused the foreground
// no password. Used by the quick-search right-click / Shift+Enter path. // (elevated process / UIPI), the keystrokes would land in the vault UI
if AUsernameOnly then // itself — a password typed into a visible search box. Bail instead.
begin if GetForegroundWindow = OwnFormHwnd then Exit;
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(AUsername);
Exit;
end;
if AUsername = '' then
begin
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(APassword);
Exit;
end;
// Username-only: type just the username into the focused field, no Tab,
// no password. Used by the quick-search right-click / Shift+Enter path.
if AUsernameOnly then
begin
SendSelectAllAndDelete; SendSelectAllAndDelete;
Sleep(60); Sleep(60);
SendUnicodeString(AUsername); SendUnicodeString(AUsername);
Sleep(200); Exit;
SendVKey(VK_TAB); end;
Sleep(200);
if AUsername = '' then
begin
SendSelectAllAndDelete; SendSelectAllAndDelete;
Sleep(60); Sleep(60);
SendUnicodeString(APassword); SendUnicodeString(APassword);
finally Exit;
// We shoved our own window out of the way to give the target the
// foreground. If it was open before the fill (quick-search pick while
// the app sits next to the target), bring it back at its old position
// — SW_SHOWNOACTIVATE so the freshly-filled app keeps the focus.
if LMinimizedSelf and ARestoreAfter then
ShowWindow(OwnFormHwnd, SW_SHOWNOACTIVATE);
end; end;
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(AUsername);
Sleep(200);
SendVKey(VK_TAB);
Sleep(200);
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(APassword);
end; end;
end. end.