msgbox_windows.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package main
  2. import (
  3. "os"
  4. "os/exec"
  5. "syscall"
  6. "unsafe"
  7. )
  8. var (
  9. user32 = syscall.NewLazyDLL("user32.dll")
  10. kernel32 = syscall.NewLazyDLL("kernel32.dll")
  11. wtsapi32 = syscall.NewLazyDLL("wtsapi32.dll")
  12. msgBoxW = user32.NewProc("MessageBoxW")
  13. consoleSessionID = kernel32.NewProc("WTSGetActiveConsoleSessionId")
  14. queryUserToken = wtsapi32.NewProc("WTSQueryUserToken")
  15. createEnvBlock = user32.NewProc("CreateEnvironmentBlock")
  16. destroyEnvBlock = user32.NewProc("DestroyEnvironmentBlock")
  17. createAsUser = kernel32.NewProc("CreateProcessAsUserW")
  18. )
  19. const (
  20. mbOK = 0x00000000
  21. mbIconWarning = 0x00000030
  22. mbSetForeground = 0x00010000
  23. mbTopmost = 0x00040000
  24. )
  25. func mustUTF16Ptr(s string) *uint16 {
  26. p, _ := syscall.UTF16PtrFromString(s) // NUL-free strings; ignore impossible error
  27. return p
  28. }
  29. func showMessageBox(title, msg string) {
  30. msgBoxW.Call(0,
  31. uintptr(unsafe.Pointer(mustUTF16Ptr(msg))),
  32. uintptr(unsafe.Pointer(mustUTF16Ptr(title))),
  33. uintptr(mbOK|mbIconWarning|mbSetForeground|mbTopmost),
  34. )
  35. }
  36. func runNotepad(path string) {
  37. err := exec.Command("notepad.exe", path).Start()
  38. if err != nil {
  39. showMessageBox("错误", "无法打开记事本: "+err.Error())
  40. }
  41. }
  42. // relaunchInUserSession 若当前进程在 Session 0(如服务)中运行,
  43. // 则在活跃用户会话中重新启动自身,然后退出当前进程。
  44. // 调用方应在 main() 最开始调用此函数。
  45. func relaunchInUserSession() {
  46. sessionID, _, _ := consoleSessionID.Call()
  47. if sessionID == 0 {
  48. // 当前已在用户会话或 sessionID 获取失败,不做处理
  49. return
  50. }
  51. // 仅在 Session 0 时才需要重新启动
  52. var ourSession uint32
  53. kernel32.NewProc("ProcessIdToSessionId").Call(
  54. uintptr(os.Getpid()), uintptr(unsafe.Pointer(&ourSession)),
  55. )
  56. if ourSession != 0 {
  57. return
  58. }
  59. // 获取用户令牌
  60. var token syscall.Handle
  61. ret, _, _ := queryUserToken.Call(sessionID, uintptr(unsafe.Pointer(&token)))
  62. if ret == 0 || token == 0 {
  63. return
  64. }
  65. defer syscall.CloseHandle(token)
  66. // 创建用户环境块
  67. var envBlock unsafe.Pointer
  68. ret, _, _ = createEnvBlock.Call(uintptr(unsafe.Pointer(&envBlock)), uintptr(token), 0)
  69. if ret == 0 {
  70. return
  71. }
  72. defer destroyEnvBlock.Call(uintptr(envBlock))
  73. // 获取当前 EXE 路径
  74. exePath, err := os.Executable()
  75. if err != nil {
  76. return
  77. }
  78. exePtr := mustUTF16Ptr(exePath)
  79. // 准备 STARTUPINFO 和 PROCESS_INFORMATION
  80. var si syscall.StartupInfo
  81. si.Cb = uint32(unsafe.Sizeof(si))
  82. var pi syscall.ProcessInformation
  83. ret, _, _ = createAsUser.Call(
  84. uintptr(token),
  85. 0,
  86. uintptr(unsafe.Pointer(exePtr)),
  87. 0,
  88. 0,
  89. 0,
  90. 0, // no extra flags; -H windowsgui already linked
  91. uintptr(unsafe.Pointer(envBlock)),
  92. 0,
  93. uintptr(unsafe.Pointer(&si)),
  94. uintptr(unsafe.Pointer(&pi)),
  95. )
  96. if ret != 0 {
  97. syscall.CloseHandle(pi.Thread)
  98. syscall.CloseHandle(pi.Process)
  99. }
  100. os.Exit(0)
  101. }