From 7b98ca450bf00c9cd1fd745a3afd9f93440c40c2 Mon Sep 17 00:00:00 2001 From: Sudhir Pola Date: Fri, 10 Jul 2026 14:24:43 +0530 Subject: [PATCH] fix(tray): improve Windows browser launch fallbacks --- pkg/tray/tray.go | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/pkg/tray/tray.go b/pkg/tray/tray.go index 506cce545..2e704df8d 100644 --- a/pkg/tray/tray.go +++ b/pkg/tray/tray.go @@ -4,7 +4,10 @@ package tray import ( "context" + "errors" + "os" "os/exec" + "path/filepath" "runtime" "sync" @@ -134,10 +137,7 @@ func openBrowser(url string) error { cmd = "open" args = []string{url} case "windows": - // rundll32 invokes the URL handler directly, avoiding cmd.exe's - // metacharacter parsing that affects "cmd /c start ". - cmd = "rundll32" - args = []string{"url.dll,FileProtocolHandler", url} + return openBrowserWindows(url) default: cmd = "xdg-open" args = []string{url} @@ -146,6 +146,37 @@ func openBrowser(url string) error { return exec.CommandContext(context.Background(), cmd, args...).Start() } +func openBrowserWindows(url string) error { + var launchErrs []error + + if windir := os.Getenv("WINDIR"); windir != "" { + rundll32Path := filepath.Join(windir, "System32", "rundll32.exe") + if _, err := os.Stat(rundll32Path); err == nil { + if err := exec.CommandContext(context.Background(), rundll32Path, "url.dll,FileProtocolHandler", url).Start(); err == nil { + return nil + } else { + launchErrs = append(launchErrs, err) + } + } + } + + // Fallback to PATH lookup. + if err := exec.CommandContext(context.Background(), "rundll32", "url.dll,FileProtocolHandler", url).Start(); err == nil { + return nil + } else { + launchErrs = append(launchErrs, err) + } + + // Final fallback for hardened environments where rundll32 is blocked. + if err := exec.CommandContext(context.Background(), "cmd", "/c", "start", "", url).Start(); err == nil { + return nil + } else { + launchErrs = append(launchErrs, err) + } + + return errors.Join(launchErrs...) +} + // getIcon returns the icon bytes for the system tray. func getIcon() []byte { return iconData