我如何 运行 使用 ctypes 提升 UAC 权限的脚本?

How do I run a script with elevated UAC permissions using ctypes?

我正在尝试通过 Python 3.x 为 Windows 10 命令行创建一个实用工具。因为它将更好地将一般命令行命令格式化为更用户友好的菜单,所以我希望它在 运行s.

时需要通过 UAC 提升权限

我正在使用 here 中描述的 ctypes 方法,它确实具有 Python 的可执行请求 UAC 提升。

但是,由于我将要编写菜单等的很多东西都需要(或在没有这些权限的情况下受到严格限制)这些提升的权限,我希望脚本退出(最好通过 sys.exit ) 如果没有找到。

在我提到的ctypes方法中,应该运行如下;

  1. 定义了一个函数is_admin(),获取ctypes.windll.shell32.IsUserAnAdmin()的值,如果为0,则returns为false。

  2. is_admin() 在有条件的情况下被调用,如果它为假,它会尝试执行命令行命令以重新 运行 脚本作为可执行文件使用ShellExecuteWsys;

    中的一些变量
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv[0]), None, 1)
    

在我的代码中,我有上述条件,并添加了一个我设置为 true 的变量 elevReq

if is_admin():
    success("Already running as administrator!") # "success" and "warn" are defined earlier
    elevReq = True
else:
    warn("Requesting administrative permissions...", False)
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv[0]), None, 1)
    elevReq = True

我用另一个条件来跟进它,检查 elevReq 是否为真,is_admin() 是否为假,以查看用户是否在 UAC 的弹出窗口中选择了“否”——如果是的,它应该抛出一个关于缺少提升权限的错误,然后退出;

if elevReq and is_admin() == False:
    error("[FATAL] Elevation was not given! Stopping...", True)
    sys.exit(1)

我遇到的问题是给定的方法似乎实际上并没有提升 Python 的权限。 UAC does pop up, but when any option is selected, it doesn't seem to matter, as the above condition fires anyway.从一开始就在提升的命令提示符中手动 运行 运行脚本没有这个问题。

这是脚本在应该重新加载时没有重新加载的问题吗?如果没有,它为什么要退出?

import ctypes
import sys
import platform

def admin() -> "Admin Bool":
    """Requests UAC Admin on Windows with a prompt"""
    if platform.system() == "Windows":
        ctypes.windll.shell32.ShellExecuteW(
            None,
            'runas',
            sys.executable,
            ' '.join(sys.argv),
            None,
            None
        )
        
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        
        except:
            return False
        
    else:
        raise OSError("admin() only works for windows.")

这将通过管理员提示请求管理员。 如果返回True,那么admin已经被用户授予。否则,将返回 False

ShellExecute API call will spawn a new process,它不会提升当前进程的权限运行ning。

让我们分析一下这段代码:

if is_admin():
    main()
else:
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)

当你第一次运行这个Python没有权限的脚本时,这个初始进程会跳到代码的最后一行,因为is_admin returns False。到达那里后,会显示 UAC 提示。

  • 如果 UAC 提示被接受,那么一个 全新的 进程(具有不同的进程 ID)被创建并再次执行代码(从头开始),但是这次具有管理员权限。现在 is_admin 应该 return Truemain 应该被调用。
  • 如果 UAC 提示被拒绝,则不会创建新进程。

无论 UAC 响应如何,初始 进程将取回 return code,但其特权将保持不变。

如果你想自己尝试,在文件末尾添加一个input(),你应该可以在接受UAC提示后看到两个不同的windows。

为避免您的代码被执行两次,请务必将所有内容保存在 main 函数中。如果您想根据 return 代码执行操作,这仅对失败(代码 <= 32)有意义。如果 return 代码成功(> 32),那么进程应该优雅地结束并让新生成的进程完成它的工作。