github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/windows/single_instance.go (about) 1 //go:build windows 2 3 package windows 4 5 import ( 6 "encoding/json" 7 "github.com/secoba/wails/v2/internal/frontend/desktop/windows/winc/w32" 8 "github.com/secoba/wails/v2/pkg/options" 9 "golang.org/x/sys/windows" 10 "os" 11 "syscall" 12 "unsafe" 13 ) 14 15 type COPYDATASTRUCT struct { 16 dwData uintptr 17 cbData uint32 18 lpData uintptr 19 } 20 21 // WMCOPYDATA_SINGLE_INSTANCE_DATA we define our own type for WM_COPYDATA message 22 const WMCOPYDATA_SINGLE_INSTANCE_DATA = 1542 23 24 func SendMessage(hwnd w32.HWND, data string) { 25 arrUtf16, _ := syscall.UTF16FromString(data) 26 27 pCopyData := new(COPYDATASTRUCT) 28 pCopyData.dwData = WMCOPYDATA_SINGLE_INSTANCE_DATA 29 pCopyData.cbData = uint32(len(arrUtf16)*2 + 1) 30 pCopyData.lpData = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(data))) 31 32 w32.SendMessage(hwnd, w32.WM_COPYDATA, 0, uintptr(unsafe.Pointer(pCopyData))) 33 } 34 35 // SetupSingleInstance single instance Windows app 36 func SetupSingleInstance(uniqueId string) { 37 id := "wails-app-" + uniqueId 38 39 className := id + "-sic" 40 windowName := id + "-siw" 41 mutexName := id + "sim" 42 43 _, err := windows.CreateMutex(nil, false, windows.StringToUTF16Ptr(mutexName)) 44 45 if err != nil { 46 if err == windows.ERROR_ALREADY_EXISTS { 47 // app is already running 48 hwnd := w32.FindWindowW(windows.StringToUTF16Ptr(className), windows.StringToUTF16Ptr(windowName)) 49 50 if hwnd != 0 { 51 data := options.SecondInstanceData{ 52 Args: os.Args[1:], 53 } 54 serialized, _ := json.Marshal(data) 55 56 SendMessage(hwnd, string(serialized)) 57 // exit second instance of app after sending message 58 os.Exit(0) 59 } 60 // if we got any other unknown error we will just start new application instance 61 } 62 } else { 63 createEventTargetWindow(className, windowName) 64 } 65 } 66 67 func createEventTargetWindow(className string, windowName string) w32.HWND { 68 // callback handler in the event target window 69 wndProc := func( 70 hwnd w32.HWND, msg uint32, wparam w32.WPARAM, lparam w32.LPARAM, 71 ) w32.LRESULT { 72 if msg == w32.WM_COPYDATA { 73 ldata := (*COPYDATASTRUCT)(unsafe.Pointer(lparam)) 74 75 if ldata.dwData == WMCOPYDATA_SINGLE_INSTANCE_DATA { 76 serialized := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ldata.lpData))) 77 78 var secondInstanceData options.SecondInstanceData 79 80 err := json.Unmarshal([]byte(serialized), &secondInstanceData) 81 82 if err == nil { 83 secondInstanceBuffer <- secondInstanceData 84 } 85 } 86 87 return w32.LRESULT(0) 88 } 89 90 return w32.DefWindowProc(hwnd, msg, wparam, lparam) 91 } 92 93 var class w32.WNDCLASSEX 94 class.Size = uint32(unsafe.Sizeof(class)) 95 class.Style = 0 96 class.WndProc = syscall.NewCallback(wndProc) 97 class.ClsExtra = 0 98 class.WndExtra = 0 99 class.Instance = w32.GetModuleHandle("") 100 class.Icon = 0 101 class.Cursor = 0 102 class.Background = 0 103 class.MenuName = nil 104 class.ClassName = windows.StringToUTF16Ptr(className) 105 class.IconSm = 0 106 107 w32.RegisterClassEx(&class) 108 109 // create event window that will not be visible for user 110 hwnd := w32.CreateWindowEx( 111 0, 112 windows.StringToUTF16Ptr(className), 113 windows.StringToUTF16Ptr(windowName), 114 0, 115 0, 116 0, 117 0, 118 0, 119 w32.HWND_MESSAGE, 120 0, 121 w32.GetModuleHandle(""), 122 nil, 123 ) 124 125 return hwnd 126 }