diff --git a/delphi-backend/UMainForm.pas b/delphi-backend/UMainForm.pas index e46a6b0..95df071 100644 --- a/delphi-backend/UMainForm.pas +++ b/delphi-backend/UMainForm.pas @@ -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