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:
@@ -165,9 +165,10 @@ type
|
||||
// without a stored username.
|
||||
// AUsernameOnly = True → type ONLY the username (no Tab, no password);
|
||||
// used by the quick-search "autofill username" action.
|
||||
// ARestoreAfter = True → if the fill had to minimise our own window (it
|
||||
// was foreground, e.g. quick-search pick), show it again afterwards
|
||||
// WITHOUT activating it, so an app the user had open doesn't vanish.
|
||||
// ARestoreAfter = True → the window was open before the hotkey: keep it
|
||||
// visible (no minimize at all — the foreground process is allowed to hand
|
||||
// focus to the target). False (tray-origin) → minimize out of the way,
|
||||
// the caller trays it after the fill.
|
||||
procedure ExecuteAutofill(ATargetHWND: HWND;
|
||||
const AUsername, APassword: string; AUsernameOnly: Boolean = False;
|
||||
ARestoreAfter: Boolean = False);
|
||||
@@ -1145,59 +1146,59 @@ const
|
||||
FocusSettleDelayMs = 120;
|
||||
var
|
||||
OwnFormHwnd: HWND;
|
||||
LMinimizedSelf: Boolean;
|
||||
begin
|
||||
OwnFormHwnd := MainFormHWND(FMainForm);
|
||||
LMinimizedSelf := False;
|
||||
if GetForegroundWindow = OwnFormHwnd then
|
||||
// ARestoreAfter (window was open before the hotkey): DON'T minimize at all.
|
||||
// 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
|
||||
ShowWindow(OwnFormHwnd, SW_MINIMIZE);
|
||||
LMinimizedSelf := True;
|
||||
Sleep(MinimizeSettleMs);
|
||||
end;
|
||||
|
||||
try
|
||||
if ATargetHWND <> 0 then
|
||||
ForceForegroundWindow(ATargetHWND);
|
||||
if ATargetHWND <> 0 then
|
||||
ForceForegroundWindow(ATargetHWND);
|
||||
|
||||
WaitForModifierRelease(1000);
|
||||
Sleep(FocusSettleDelayMs);
|
||||
WaitForModifierRelease(1000);
|
||||
Sleep(FocusSettleDelayMs);
|
||||
|
||||
// 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;
|
||||
Sleep(60);
|
||||
SendUnicodeString(AUsername);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if AUsername = '' then
|
||||
begin
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
SendUnicodeString(APassword);
|
||||
Exit;
|
||||
end;
|
||||
// Never type into our own window: if the target refused the foreground
|
||||
// (elevated process / UIPI), the keystrokes would land in the vault UI
|
||||
// itself — a password typed into a visible search box. Bail instead.
|
||||
if GetForegroundWindow = OwnFormHwnd then Exit;
|
||||
|
||||
// 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;
|
||||
Sleep(60);
|
||||
SendUnicodeString(AUsername);
|
||||
Sleep(200);
|
||||
SendVKey(VK_TAB);
|
||||
Sleep(200);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if AUsername = '' then
|
||||
begin
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
SendUnicodeString(APassword);
|
||||
finally
|
||||
// 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);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
SendUnicodeString(AUsername);
|
||||
Sleep(200);
|
||||
SendVKey(VK_TAB);
|
||||
Sleep(200);
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
SendUnicodeString(APassword);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user