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 {