fix(windows): Prevent systray menu crash on rapid clicks (#4151) (#4812)

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 <noreply@vibekanban.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2025-12-25 19:35:44 +11:00 committed by GitHub
commit f30c888c5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 15 additions and 2 deletions

View file

@ -23,6 +23,7 @@ After processing, the content will be moved to the main changelog and this file
## Fixed
<!-- Bug fixes -->
- Fix Windows systray menu crash when clicking icon repeatedly by adding guard against concurrent TrackPopupMenuEx calls (#4151) by @leaanthony
## Deprecated
<!-- Soon-to-be removed features -->

View file

@ -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 {