| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- //go:build windows
- package main
- import (
- "context"
- "os"
- "os/exec"
- "strconv"
- "syscall"
- "time"
- "unsafe"
- "golang.org/x/sys/windows"
- )
- const createNoWindow = 0x08000000
- // hideWindow 让子进程完全不显示终端窗口(CREATE_NO_WINDOW + HideWindow)。
- func hideWindow(cmd *exec.Cmd) {
- if cmd.SysProcAttr == nil {
- cmd.SysProcAttr = &syscall.SysProcAttr{}
- }
- cmd.SysProcAttr.HideWindow = true
- cmd.SysProcAttr.CreationFlags |= createNoWindow
- }
- // killTree 强制结束进程树(含 node 派生的子进程),带超时防止卡住。
- func killTree(pid int) error {
- if pid <= 0 {
- return nil
- }
- ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
- defer cancel()
- cmd := exec.CommandContext(ctx, "taskkill", "/PID", strconv.Itoa(pid), "/T", "/F")
- hideWindow(cmd)
- return cmd.Run()
- }
- // processAlive 判断进程是否仍在运行。
- func processAlive(pid int) bool {
- if pid <= 0 {
- return false
- }
- h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
- if err != nil {
- return false
- }
- defer windows.CloseHandle(h)
- var code uint32
- if err := windows.GetExitCodeProcess(h, &code); err != nil {
- return false
- }
- return code == 259 // STILL_ACTIVE
- }
- var (
- kernel32 = windows.NewLazySystemDLL("kernel32.dll")
- procCreateMutex = kernel32.NewProc("CreateMutexW")
- )
- // singleInstance 基于命名互斥体的单实例保护。
- type singleInstance struct {
- handle windows.Handle
- }
- func acquireSingleInstance(name string) (*singleInstance, bool) {
- n, err := windows.UTF16PtrFromString(name)
- if err != nil {
- return nil, true
- }
- h, _, callErr := procCreateMutex.Call(0, 0, uintptr(unsafe.Pointer(n)))
- if h == 0 {
- return nil, true // 创建失败时放行,避免误伤
- }
- if errno, ok := callErr.(syscall.Errno); ok && errno == syscall.ERROR_ALREADY_EXISTS {
- _ = windows.CloseHandle(windows.Handle(h))
- return nil, false
- }
- return &singleInstance{handle: windows.Handle(h)}, true
- }
- func (s *singleInstance) release() {
- if s != nil && s.handle != 0 {
- _ = windows.CloseHandle(s.handle)
- s.handle = 0
- }
- }
- // attachConsole 让 windowsgui 子系统程序能输出到调用它的终端(仅 CLI 子命令使用)。
- func attachConsole() {
- const attachParentProcess = 0xFFFFFFFF
- procAttach := kernel32.NewProc("AttachConsole")
- ret, _, _ := procAttach.Call(uintptr(attachParentProcess))
- if ret == 0 {
- return
- }
- if f, err := os.OpenFile("CONOUT$", os.O_WRONLY, 0); err == nil {
- os.Stdout = f
- os.Stderr = f
- }
- }
|