5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 17:52:29 +08:00
wails/v2/internal/frontend/desktop/windows/single_instance.go
Andrey Pshenkin c24bd5e3e8
Implement Single instance lock feature with passing arguments to initial instance (#2951)
* implement MacOS openFile/openFiles events

* wip: windows file association

* fix macro import

* add file icon copy

* try copy icon

* keep only required part of scripts

* update config schema

* fix json

* set fileAssociation for mac via config

* proper iconName handling

* add fileAssociation icon generator

* fix file association icons bundle

* don't break compatibility

* remove mimeType as not supported linux for now

* add documentation

* adjust config schema

* restore formatting

* try implement single instance lock with params passing

* fix focusing

* fix focusing

* formatting

* use channel buffer for second instance events

* handle errors

* add comment

* remove unused option in file association

* wip: linux single instance lock

* wip: linux single instance

* some experiments with making window active

* try to use unminimise

* remove unused

* try present for window

* try present for window

* fix build

* cleanup

* cleanup

* implement single instance lock on mac os

* implement proper show for windows

* proper unmimimise

* get rid of openFiles mac os. change configuration structure

* remove unused channel

* remove unused function

* add documentation for single instance lock

* add PR link

* changes after review

* update docs

* changes after review

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
2023-10-23 21:31:56 +11:00

127 lines
3.1 KiB
Go

//go:build windows
package windows
import (
"encoding/json"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
"github.com/wailsapp/wails/v2/pkg/options"
"golang.org/x/sys/windows"
"os"
"syscall"
"unsafe"
)
type COPYDATASTRUCT struct {
dwData uintptr
cbData uint32
lpData uintptr
}
// WMCOPYDATA_SINGLE_INSTANCE_DATA we define our own type for WM_COPYDATA message
const WMCOPYDATA_SINGLE_INSTANCE_DATA = 1542
func SendMessage(hwnd w32.HWND, data string) {
arrUtf16, _ := syscall.UTF16FromString(data)
pCopyData := new(COPYDATASTRUCT)
pCopyData.dwData = WMCOPYDATA_SINGLE_INSTANCE_DATA
pCopyData.cbData = uint32(len(arrUtf16)*2 + 1)
pCopyData.lpData = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(data)))
w32.SendMessage(hwnd, w32.WM_COPYDATA, 0, uintptr(unsafe.Pointer(pCopyData)))
}
// SetupSingleInstance single instance Windows app
func SetupSingleInstance(uniqueId string) {
id := "wails-app-" + uniqueId
className := id + "-sic"
windowName := id + "-siw"
mutexName := id + "sim"
_, err := windows.CreateMutex(nil, false, windows.StringToUTF16Ptr(mutexName))
if err != nil {
if err == windows.ERROR_ALREADY_EXISTS {
// app is already running
hwnd := w32.FindWindowW(windows.StringToUTF16Ptr(className), windows.StringToUTF16Ptr(windowName))
if hwnd != 0 {
data := options.SecondInstanceData{
Args: os.Args[1:],
}
serialized, _ := json.Marshal(data)
SendMessage(hwnd, string(serialized))
// exit second instance of app after sending message
os.Exit(0)
}
// if we got any other unknown error we will just start new application instance
}
} else {
createEventTargetWindow(className, windowName)
}
}
func createEventTargetWindow(className string, windowName string) w32.HWND {
// callback handler in the event target window
wndProc := func(
hwnd w32.HWND, msg uint32, wparam w32.WPARAM, lparam w32.LPARAM,
) w32.LRESULT {
if msg == w32.WM_COPYDATA {
ldata := (*COPYDATASTRUCT)(unsafe.Pointer(lparam))
if ldata.dwData == WMCOPYDATA_SINGLE_INSTANCE_DATA {
serialized := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(ldata.lpData)))
var secondInstanceData options.SecondInstanceData
err := json.Unmarshal([]byte(serialized), &secondInstanceData)
if err == nil {
secondInstanceBuffer <- secondInstanceData
}
}
return w32.LRESULT(0)
}
return w32.DefWindowProc(hwnd, msg, wparam, lparam)
}
var class w32.WNDCLASSEX
class.Size = uint32(unsafe.Sizeof(class))
class.Style = 0
class.WndProc = syscall.NewCallback(wndProc)
class.ClsExtra = 0
class.WndExtra = 0
class.Instance = w32.GetModuleHandle("")
class.Icon = 0
class.Cursor = 0
class.Background = 0
class.MenuName = nil
class.ClassName = windows.StringToUTF16Ptr(className)
class.IconSm = 0
w32.RegisterClassEx(&class)
// create event window that will not be visible for user
hwnd := w32.CreateWindowEx(
0,
windows.StringToUTF16Ptr(className),
windows.StringToUTF16Ptr(windowName),
0,
0,
0,
0,
0,
w32.HWND_MESSAGE,
0,
w32.GetModuleHandle(""),
nil,
)
return hwnd
}