From f30c888c5bce9b18f0f08c3f5212baf4431df634 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Thu, 25 Dec 2025 19:35:44 +1100 Subject: [PATCH] fix(windows): Prevent systray menu crash on rapid clicks (#4151) (#4812) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add guard against concurrent TrackPopupMenuEx calls using atomic.Bool to prevent race condition when user clicks systray icon repeatedly. Change TrackPopupMenuEx failure from fatal to debug log with early return so the application gracefully handles failed menu display attempts instead of crashing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Vibe Kanban Co-authored-by: Claude --- v3/UNRELEASED_CHANGELOG.md | 1 + v3/pkg/application/popupmenu_windows.go | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index 8e4648038..c6bcc38a2 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -23,6 +23,7 @@ After processing, the content will be moved to the main changelog and this file ## Fixed +- Fix Windows systray menu crash when clicking icon repeatedly by adding guard against concurrent TrackPopupMenuEx calls (#4151) by @leaanthony ## Deprecated diff --git a/v3/pkg/application/popupmenu_windows.go b/v3/pkg/application/popupmenu_windows.go index f237b2669..ed809a130 100644 --- a/v3/pkg/application/popupmenu_windows.go +++ b/v3/pkg/application/popupmenu_windows.go @@ -1,8 +1,10 @@ package application import ( - "github.com/wailsapp/wails/v3/pkg/w32" + "sync/atomic" "unsafe" + + "github.com/wailsapp/wails/v3/pkg/w32" ) const ( @@ -49,6 +51,7 @@ type Win32Menu struct { currentMenuID int onMenuClose func() onMenuOpen func() + isShowing atomic.Bool // guards against concurrent TrackPopupMenuEx calls } func (p *Win32Menu) newMenu() w32.HMENU { @@ -189,6 +192,12 @@ func NewApplicationMenu(parent *windowsWebviewWindow, inputMenu *Menu) *Win32Men } func (p *Win32Menu) ShowAt(x int, y int) { + // Prevent concurrent menu displays - TrackPopupMenuEx is blocking and + // calling it while another popup is showing causes "TrackPopupMenu failed" + if !p.isShowing.CompareAndSwap(false, true) { + return + } + defer p.isShowing.Store(false) w32.SetForegroundWindow(p.parent) @@ -216,7 +225,10 @@ func (p *Win32Menu) ShowAt(x int, y int) { } if !w32.TrackPopupMenuEx(p.menu, menuFlags, int32(x), int32(y), p.parent, nil) { - globalApplication.fatal("TrackPopupMenu failed") + // TrackPopupMenuEx can fail if called during menu transitions or rapid clicks. + // This is not fatal - just skip this menu display attempt. + globalApplication.debug("TrackPopupMenu failed - menu may already be showing") + return } if p.onMenuClose != nil {