Golang程序请求UAC后仍遭遇注册表访问拒绝问题求助
问题分析
你遇到的核心问题是:当前进程并没有真正获得管理员权限。你的代码用ShellExecute以runas动词启动了一个新的管理员权限进程,但原进程仍然保持普通权限,并且继续执行后续的注册表操作,这自然会触发"Access is denied"错误。
解决方案
需要先判断当前进程是否已拥有管理员权限:
- 如果没有,启动管理员权限的进程后立即退出原进程,不要再执行后续代码;
- 如果已有管理员权限,再执行注册表检查/创建操作。
步骤1:添加管理员权限检查函数
先实现一个函数来判断当前进程是否是管理员:
import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) func isAdmin() bool { var token windows.Token err := windows.OpenProcessToken(windows.CurrentProcess(), windows.TOKEN_QUERY, &token) if err != nil { return false } defer token.Close() var elevation windows.TokenElevation elevationSize := uint32(unsafe.Sizeof(elevation)) err = windows.GetTokenInformation(token, windows.TokenElevation, unsafe.Pointer(&elevation), elevationSize, &elevationSize) if err != nil { return false } return elevation.TokenIsElevated != 0 }
步骤2:修改regCheck函数逻辑
调整代码流程,确保只有管理员权限的进程才执行注册表操作:
func (t *Toaster) regCheck() error { // 先检查是否已拥有管理员权限 if !isAdmin() { verb := "runas" exe, err := os.Executable() if err != nil { return err } cwd, err := os.Getwd() if err != nil { return err } args := strings.Join(os.Args[1:], " ") verbPtr, _ := syscall.UTF16PtrFromString(verb) exePtr, _ := syscall.UTF16PtrFromString(exe) cwdPtr, _ := syscall.UTF16PtrFromString(cwd) argPtr, _ := syscall.UTF16PtrFromString(args) const showCmd int32 = 1 // SW_NORMAL err = windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) if err != nil { return err } // 启动管理员进程后,原进程直接退出,不要继续执行后续代码 os.Exit(0) } // 现在进程已拥有管理员权限,执行注册表操作 key, err := registry.OpenKey(registry.CLASSES_ROOT, PROTOCOL_REG_PATH, registry.ALL_ACCESS) if err != nil { if err == registry.ErrNotExist { return t.createRegKeys() } return errors.New("Error opening protocol registry key: " + err.Error()) } key.Close() return nil }
额外注意事项
- 调用
os.Exit(0)是关键:原普通权限进程在启动管理员进程后必须立即退出,避免继续执行无权限的操作; - Wails环境中,
os.Executable()指向的临时目录中的wailsbindings可执行文件是正常的,提权启动它不会影响功能; - 注册表操作时,尽量使用最小必要权限:如果只是检查是否存在,用
registry.QUERY_VALUE代替registry.ALL_ACCESS,减少权限风险。
内容的提问来源于stack exchange,提问作者nigel239
相关产品推荐
相关产品推荐

