//go:build windows package singleinstance import ( "fmt" "unsafe" "golang.org/x/sys/windows" ) var ( user32 = windows.NewLazySystemDLL("user32.dll") procFindWindow = user32.NewProc("FindWindowW") procShowWindow = user32.NewProc("ShowWindow") procSetForeground = user32.NewProc("SetForegroundWindow") procIsIconic = user32.NewProc("IsIconic") ) const ( swShow = 5 swRestore = 9 ) // Lock acquires a per-user mutex. If another Navis holds it, activates that // window (title "Navis") and returns ErrAlreadyRunning. func Lock(appName string) (func(), error) { if appName == "" { appName = "Navis" } name, err := windows.UTF16PtrFromString("Local\\" + appName + "SingleInstance") if err != nil { return nil, err } handle, err := windows.CreateMutex(nil, false, name) if err == windows.ERROR_ALREADY_EXISTS { if handle != 0 { _ = windows.CloseHandle(handle) } activateWindow(appName) return nil, ErrAlreadyRunning } if err != nil { return nil, fmt.Errorf("singleinstance: %w", err) } return func() { _ = windows.ReleaseMutex(handle) _ = windows.CloseHandle(handle) }, nil } func activateWindow(title string) { t, err := windows.UTF16PtrFromString(title) if err != nil { return } hwnd, _, _ := procFindWindow.Call(0, uintptr(unsafe.Pointer(t))) if hwnd == 0 { return } iconic, _, _ := procIsIconic.Call(hwnd) if iconic != 0 { procShowWindow.Call(hwnd, swRestore) } else { procShowWindow.Call(hwnd, swShow) } procSetForeground.Call(hwnd) }