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.
|
// 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.
|
||||||
|
|||||||
Reference in New Issue
Block a user