|
|
@@ -1,14 +1,22 @@
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
+ "os"
|
|
|
"os/exec"
|
|
|
"syscall"
|
|
|
"unsafe"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
- user32 = syscall.NewLazyDLL("user32.dll")
|
|
|
- msgBoxW = user32.NewProc("MessageBoxW")
|
|
|
+ user32 = syscall.NewLazyDLL("user32.dll")
|
|
|
+ kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
|
+ wtsapi32 = syscall.NewLazyDLL("wtsapi32.dll")
|
|
|
+ msgBoxW = user32.NewProc("MessageBoxW")
|
|
|
+ consoleSessionID = kernel32.NewProc("WTSGetActiveConsoleSessionId")
|
|
|
+ queryUserToken = wtsapi32.NewProc("WTSQueryUserToken")
|
|
|
+ createEnvBlock = user32.NewProc("CreateEnvironmentBlock")
|
|
|
+ destroyEnvBlock = user32.NewProc("DestroyEnvironmentBlock")
|
|
|
+ createAsUser = kernel32.NewProc("CreateProcessAsUserW")
|
|
|
)
|
|
|
|
|
|
const (
|
|
|
@@ -18,19 +26,15 @@ const (
|
|
|
mbTopmost = 0x00040000
|
|
|
)
|
|
|
|
|
|
-func showMessageBox(title, msg string) {
|
|
|
- ptitle, err := syscall.UTF16PtrFromString(title)
|
|
|
- if err != nil {
|
|
|
- return
|
|
|
- }
|
|
|
- ptext, err := syscall.UTF16PtrFromString(msg)
|
|
|
- if err != nil {
|
|
|
- return
|
|
|
- }
|
|
|
+func mustUTF16Ptr(s string) *uint16 {
|
|
|
+ p, _ := syscall.UTF16PtrFromString(s) // NUL-free strings; ignore impossible error
|
|
|
+ return p
|
|
|
+}
|
|
|
|
|
|
+func showMessageBox(title, msg string) {
|
|
|
msgBoxW.Call(0,
|
|
|
- uintptr(unsafe.Pointer(ptext)),
|
|
|
- uintptr(unsafe.Pointer(ptitle)),
|
|
|
+ uintptr(unsafe.Pointer(mustUTF16Ptr(msg))),
|
|
|
+ uintptr(unsafe.Pointer(mustUTF16Ptr(title))),
|
|
|
uintptr(mbOK|mbIconWarning|mbSetForeground|mbTopmost),
|
|
|
)
|
|
|
}
|
|
|
@@ -40,4 +44,73 @@ func runNotepad(path string) {
|
|
|
if err != nil {
|
|
|
showMessageBox("错误", "无法打开记事本: "+err.Error())
|
|
|
}
|
|
|
-}
|
|
|
+}
|
|
|
+
|
|
|
+// relaunchInUserSession 若当前进程在 Session 0(如服务)中运行,
|
|
|
+// 则在活跃用户会话中重新启动自身,然后退出当前进程。
|
|
|
+// 调用方应在 main() 最开始调用此函数。
|
|
|
+func relaunchInUserSession() {
|
|
|
+ sessionID, _, _ := consoleSessionID.Call()
|
|
|
+ if sessionID == 0 {
|
|
|
+ // 当前已在用户会话或 sessionID 获取失败,不做处理
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 仅在 Session 0 时才需要重新启动
|
|
|
+ var ourSession uint32
|
|
|
+ kernel32.NewProc("ProcessIdToSessionId").Call(
|
|
|
+ uintptr(os.Getpid()), uintptr(unsafe.Pointer(&ourSession)),
|
|
|
+ )
|
|
|
+ if ourSession != 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取用户令牌
|
|
|
+ var token syscall.Handle
|
|
|
+ ret, _, _ := queryUserToken.Call(sessionID, uintptr(unsafe.Pointer(&token)))
|
|
|
+ if ret == 0 || token == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer syscall.CloseHandle(token)
|
|
|
+
|
|
|
+ // 创建用户环境块
|
|
|
+ var envBlock unsafe.Pointer
|
|
|
+ ret, _, _ = createEnvBlock.Call(uintptr(unsafe.Pointer(&envBlock)), uintptr(token), 0)
|
|
|
+ if ret == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer destroyEnvBlock.Call(uintptr(envBlock))
|
|
|
+
|
|
|
+ // 获取当前 EXE 路径
|
|
|
+ exePath, err := os.Executable()
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ exePtr := mustUTF16Ptr(exePath)
|
|
|
+
|
|
|
+ // 准备 STARTUPINFO 和 PROCESS_INFORMATION
|
|
|
+ var si syscall.StartupInfo
|
|
|
+ si.Cb = uint32(unsafe.Sizeof(si))
|
|
|
+ var pi syscall.ProcessInformation
|
|
|
+
|
|
|
+ ret, _, _ = createAsUser.Call(
|
|
|
+ uintptr(token),
|
|
|
+ 0,
|
|
|
+ uintptr(unsafe.Pointer(exePtr)),
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0,
|
|
|
+ 0, // no extra flags; -H windowsgui already linked
|
|
|
+ uintptr(unsafe.Pointer(envBlock)),
|
|
|
+ 0,
|
|
|
+ uintptr(unsafe.Pointer(&si)),
|
|
|
+ uintptr(unsafe.Pointer(&pi)),
|
|
|
+ )
|
|
|
+ if ret != 0 {
|
|
|
+ syscall.CloseHandle(pi.Thread)
|
|
|
+ syscall.CloseHandle(pi.Process)
|
|
|
+ }
|
|
|
+
|
|
|
+ os.Exit(0)
|
|
|
+}
|
|
|
+
|