proc_windows.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. //go:build windows
  2. package main
  3. import (
  4. "os"
  5. "os/exec"
  6. "strconv"
  7. "syscall"
  8. "unsafe"
  9. "golang.org/x/sys/windows"
  10. )
  11. const createNoWindow = 0x08000000
  12. // hideWindow 让子进程完全不显示终端窗口(CREATE_NO_WINDOW + HideWindow)。
  13. func hideWindow(cmd *exec.Cmd) {
  14. if cmd.SysProcAttr == nil {
  15. cmd.SysProcAttr = &syscall.SysProcAttr{}
  16. }
  17. cmd.SysProcAttr.HideWindow = true
  18. cmd.SysProcAttr.CreationFlags |= createNoWindow
  19. }
  20. // killTree 强制结束进程树(含 node 派生的子进程)。
  21. func killTree(pid int) error {
  22. if pid <= 0 {
  23. return nil
  24. }
  25. cmd := exec.Command("taskkill", "/PID", strconv.Itoa(pid), "/T", "/F")
  26. hideWindow(cmd)
  27. return cmd.Run()
  28. }
  29. // processAlive 判断进程是否仍在运行。
  30. func processAlive(pid int) bool {
  31. if pid <= 0 {
  32. return false
  33. }
  34. h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
  35. if err != nil {
  36. return false
  37. }
  38. defer windows.CloseHandle(h)
  39. var code uint32
  40. if err := windows.GetExitCodeProcess(h, &code); err != nil {
  41. return false
  42. }
  43. return code == 259 // STILL_ACTIVE
  44. }
  45. var (
  46. kernel32 = windows.NewLazySystemDLL("kernel32.dll")
  47. procCreateMutex = kernel32.NewProc("CreateMutexW")
  48. )
  49. // singleInstance 基于命名互斥体的单实例保护。
  50. type singleInstance struct {
  51. handle windows.Handle
  52. }
  53. func acquireSingleInstance(name string) (*singleInstance, bool) {
  54. n, err := windows.UTF16PtrFromString(name)
  55. if err != nil {
  56. return nil, true
  57. }
  58. h, _, callErr := procCreateMutex.Call(0, 0, uintptr(unsafe.Pointer(n)))
  59. if h == 0 {
  60. return nil, true // 创建失败时放行,避免误伤
  61. }
  62. if errno, ok := callErr.(syscall.Errno); ok && errno == syscall.ERROR_ALREADY_EXISTS {
  63. _ = windows.CloseHandle(windows.Handle(h))
  64. return nil, false
  65. }
  66. return &singleInstance{handle: windows.Handle(h)}, true
  67. }
  68. func (s *singleInstance) release() {
  69. if s != nil && s.handle != 0 {
  70. _ = windows.CloseHandle(s.handle)
  71. s.handle = 0
  72. }
  73. }
  74. // attachConsole 让 windowsgui 子系统程序能输出到调用它的终端(CLI 子命令用)。
  75. func attachConsole() {
  76. const attachParentProcess = 0xFFFFFFFF
  77. procAttach := kernel32.NewProc("AttachConsole")
  78. ret, _, _ := procAttach.Call(uintptr(attachParentProcess))
  79. if ret == 0 {
  80. return
  81. }
  82. if f, err := os.OpenFile("CONOUT$", os.O_WRONLY, 0); err == nil {
  83. os.Stdout = f
  84. os.Stderr = f
  85. }
  86. }