fix(startup): hide the taskbar proxy from a worker thread
Why every previous attempt could not work: the main thread is BUSY inside FormCreate and pumps no message, and that is exactly when FMX creates and shows its taskbar proxy. A WinEvent hook, a timer, a queued call — none of them can run in that window. Deferring the server start shortened the window but did not remove it, because the proxy is created regardless. A worker thread is the one thing still running while the main thread blocks. It polls for our TFMAppClass window (matched on our own process id) and hides it the instant it appears; ShowWindow is safe cross-thread. It stops as soon as the tray startup completes, with a 10 s ceiling so it can never linger. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -227,6 +227,7 @@ procedure WinStateEventProc(hWinEventHook: THandle; AEvent: DWORD; AWnd: HWND;
|
||||
idObject, idChild: LongInt; idEventThread, dwmsEventTime: DWORD); stdcall; forward;
|
||||
procedure TrayShowEventProc(hWinEventHook: THandle; AEvent: DWORD; AWnd: HWND;
|
||||
idObject, idChild: LongInt; idEventThread, dwmsEventTime: DWORD); stdcall; forward;
|
||||
function FindOurFMXAppWindow: HWND; forward;
|
||||
|
||||
procedure TMainForm.FormCreate(Sender: TObject);
|
||||
begin
|
||||
@@ -247,6 +248,29 @@ begin
|
||||
@TrayShowEventProc, GetCurrentProcessId, 0, WINEVENT_INCONTEXT);
|
||||
LogLine('[tray] FormCreate begin at ' + FormatDateTime('hh:nn:ss.zzz', Now) +
|
||||
' show-hook=' + BoolToStr(FShowEventHook <> 0, True));
|
||||
// The decisive piece. Everything else runs on the main thread, which is
|
||||
// BUSY inside FormCreate and pumps no message — so no hook, timer or
|
||||
// queued call can fire while the FMX taskbar proxy is created and shown.
|
||||
// A worker thread is the only thing still running then. It polls for the
|
||||
// proxy and hides it the moment it appears; ShowWindow is safe to call
|
||||
// cross-thread. Stops on GTrayStartupPending or after 10 s, whichever
|
||||
// comes first, so it can never linger.
|
||||
TThread.CreateAnonymousThread(
|
||||
procedure
|
||||
var
|
||||
LWnd: HWND;
|
||||
LTicks: Integer;
|
||||
begin
|
||||
LTicks := 0;
|
||||
while GTrayStartupPending and (LTicks < 1000) do
|
||||
begin
|
||||
LWnd := FindOurFMXAppWindow;
|
||||
if (LWnd <> 0) and IsWindowVisible(LWnd) then
|
||||
ShowWindow(LWnd, SW_HIDE);
|
||||
Sleep(10);
|
||||
Inc(LTicks);
|
||||
end;
|
||||
end).Start;
|
||||
TrayHideNow('hide#1 top-of-FormCreate');
|
||||
end;
|
||||
|
||||
@@ -1659,6 +1683,23 @@ end;
|
||||
// process shows gets hidden again immediately. Scoped to our process by the
|
||||
// hook itself; WebView2 runs in separate processes so its windows never reach
|
||||
// here. Torn down as soon as startup finishes.
|
||||
// Our FMX taskbar proxy (TFMAppClass), found by class name and filtered on our
|
||||
// own process id — the same lookup TPMBridge does, duplicated here because it
|
||||
// must work from a worker thread before FBridge even exists.
|
||||
function FindOurFMXAppWindow: HWND;
|
||||
var
|
||||
LPid: DWORD;
|
||||
begin
|
||||
Result := FindWindowEx(0, 0, 'TFMAppClass', nil);
|
||||
while Result <> 0 do
|
||||
begin
|
||||
LPid := 0;
|
||||
GetWindowThreadProcessId(Result, LPid);
|
||||
if LPid = GetCurrentProcessId then Exit;
|
||||
Result := FindWindowEx(0, Result, 'TFMAppClass', nil);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TrayShowEventProc(hWinEventHook: THandle; AEvent: DWORD; AWnd: HWND;
|
||||
idObject, idChild: LongInt; idEventThread, dwmsEventTime: DWORD); stdcall;
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user