package main import ( "os" "os/exec" "syscall" "unsafe" ) var ( 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 ( mbOK = 0x00000000 mbIconWarning = 0x00000030 mbSetForeground = 0x00010000 mbTopmost = 0x00040000 ) 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(mustUTF16Ptr(msg))), uintptr(unsafe.Pointer(mustUTF16Ptr(title))), uintptr(mbOK|mbIconWarning|mbSetForeground|mbTopmost), ) } func runNotepad(path string) { err := exec.Command("notepad.exe", path).Start() 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) }