从Python脚本中请求UAC提升?

jwf*_*arn 82 python windows uac windows-vista

我希望我的Python脚本能够在Vista上复制文件.当我从普通cmd.exe窗口运行它时,不会生成错误,但不会复制文件.如果我运行cmd.exe"作为管理员",然后运行我的脚本,它工作正常.

这是有道理的,因为用户帐户控制(UAC)通常会阻止许多文件系统操作.

有没有办法可以在Python脚本中调用UAC提升请求(这些对话框说"像这样的应用程序需要管理员访问权限,这样可以吗?")

如果那是不可能的,那么我的脚本是否有一种方法可以至少检测到它没有被提升以便它可以优雅地失败?

Mar*_*nte 83

截至2017年,实现这一目标的简单方法如下:

import ctypes, sys

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

if is_admin():
    # Code of your program here
else:
    # Re-run the program with admin rights
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)
Run Code Online (Sandbox Code Playgroud)

如果您使用的是Python 2.x,那么您应该替换最后一行:

ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1)
Run Code Online (Sandbox Code Playgroud)

还要注意的是,如果你转换你的Python脚本到一个可执行文件(使用工具,如py2exe,cx_freeze,pyinstaller),那么你应该更换第四个参数为空字符串("").

这里的一些优点是:

  • 不需要外部库(也不是用于Windows扩展的Python).它仅使用ctypes标准库.
  • 适用于Python 2和Python 3.
  • 无需修改文件资源也无需创建清单文件.
  • 如果你没有在if/else语句下面添加代码,那么代码将不会被执行两次.
  • 如果用户拒绝UAC提示,您可以轻松地将其修改为具有特殊行为.
  • 您可以指定修改第四个参数的参数.
  • 您可以指定修改第六个参数的显示方法.

底层ShellExecute调用的文档在这里.

  • 我不得不使用unicode实例作为ShellExecuteW的参数(如u'runas'和unicode(sys.executable))来运行它. (8认同)
  • @Janosch,那是因为你使用的是Python 2.x,而我的代码是在Python 3中(其中所有字符串都被视为unicodes).但是值得一提,谢谢! (6认同)
  • 我的回答是假设我们实际上并不关心参数.所以,在python脚本中使用`sys.argv [0]`或`__file__`就可以了.如果你把它转换成一个可执行文件,那么你可以使用``"`,`"anyrandomstring"`,`sys.argv [0]`它也可以工作(但不是`__file__`,因为它没有定义) .现在,如果你关心参数,你应该使用`"".join(sys.argv)`作为python脚本,使用`"".join(sys.argv [1:])`作为转换后的脚本.@HrvojeT (4认同)
  • @Martin如果我从Windows命令行运行这样的代码:"python yourcode.py"它只是打开python.exe.有办法解决吗? (2认同)
  • @ user2978216 我遇到了同样的问题。在行`ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1)` sys.executable` 仅解析为python 解释器(例如`C:\Python27\Python .exe`) 解决方法是将运行脚本添加为参数(替换`""`)。`ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)` 另请注意,为了在 python 2.x 中工作,所有字符串参数都需要是 unicode(即`u "runas"`、`unicode(sys.executable)` 和 `unicode(__file__)`) (2认同)
  • @HrvojeT,ShellExecuteW`和`ShellExecuteA`都是对Windows API中`ShellExecute`函数的调用.前者要求字符串采用unicode格式,后者使用ANSI格式 (2认同)
  • 2020年尝试过,效果仍然很好 (2认同)

Jor*_*nko 66

我花了一点时间让dguaraglia的答案正常工作,所以为了节省他人的时间,这就是我为实现这个想法而采取的措施:

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)
Run Code Online (Sandbox Code Playgroud)

  • @JoranBeasley,你不会看到任何输出.ShellExecuteEx不会将其STDOUT发布回原始shell.在这方面,调试将是......具有挑战性.但特权提升技巧肯定有效. (6认同)
  • 这是我见过的最酷的技巧之一. (2认同)
  • 对于报价,您可以使用subprocess.list2cmdline正确执行。 (2认同)

dgu*_*lia 29

似乎没有办法提升应用程序权限一段时间来执行特定任务.Windows需要在程序开始时知道应用程序是否需要某些特权,并要求用户确认应用程序何时执行需要这些特权的任务.有两种方法可以做到这一点:

  1. 编写一个清单文件,告诉Windows应用程序可能需要一些权限
  2. 从另一个程序内部使用提升的权限运行应用程序

两篇 文章更详细地解释了它是如何工作的.

如果您不想为CreateElevatedProcess API编写令人讨厌的ctypes包装器,我会使用代码项目文章中解释的ShellExecuteEx技巧(Pywin32带有ShellExecute的包装器).怎么样?像这样的东西:

当你的程序启动时,它会检查它是否具有管理员权限,如果它没有使用ShellExecute技巧自行运行并立即退出,如果是,则执行手头的任务.

当您将程序描述为"脚本"时,我认为这足以满足您的需求.

干杯.

  • 您可能需要注意的一点是,您可以使用os.startfile($ EXECUTABLE,"runas")在没有PyWin32的情况下执行ShellExecute(我在安装它时遇到了问题). (4认同)

Irv*_*Moy 9

只是添加这个答案,以防其他人像我一样被谷歌搜索引导到这里。我elevate在 Python 脚本中使用了该模块,并在 Windows 10 中使用管理员权限执行了该脚本。

https://pypi.org/project/elevate/


Noc*_*wer 6

以下示例基于MARTIN DE LA FUENTE SAAVEDRA 的出色工作和公认答案。特别是,引入了两个枚举。第一个允许轻松指定如何打开提升的程序,第二个在需要轻松识别错误时提供帮助。请注意,如果您希望将所有命令行参数传递给新进程,sys.argv[0]则可能应该替换为函数调用:subprocess.list2cmdline(sys.argv)

#! /usr/bin/env python3
import ctypes
import enum
import subprocess
import sys

# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx


# noinspection SpellCheckingInspection
class SW(enum.IntEnum):
    HIDE = 0
    MAXIMIZE = 3
    MINIMIZE = 6
    RESTORE = 9
    SHOW = 5
    SHOWDEFAULT = 10
    SHOWMAXIMIZED = 3
    SHOWMINIMIZED = 2
    SHOWMINNOACTIVE = 7
    SHOWNA = 8
    SHOWNOACTIVATE = 4
    SHOWNORMAL = 1


class ERROR(enum.IntEnum):
    ZERO = 0
    FILE_NOT_FOUND = 2
    PATH_NOT_FOUND = 3
    BAD_FORMAT = 11
    ACCESS_DENIED = 5
    ASSOC_INCOMPLETE = 27
    DDE_BUSY = 30
    DDE_FAIL = 29
    DDE_TIMEOUT = 28
    DLL_NOT_FOUND = 32
    NO_ASSOC = 31
    OOM = 8
    SHARE = 26


def bootstrap():
    if ctypes.windll.shell32.IsUserAnAdmin():
        main()
    else:
       # noinspection SpellCheckingInspection
        hinstance = ctypes.windll.shell32.ShellExecuteW(
            None,
            'runas',
            sys.executable,
            subprocess.list2cmdline(sys.argv),
            None,
            SW.SHOWNORMAL
        )
        if hinstance <= 32:
            raise RuntimeError(ERROR(hinstance))


def main():
    # Your Code Here
    print(input('Echo: '))


if __name__ == '__main__':
    bootstrap()
Run Code Online (Sandbox Code Playgroud)


Ken*_*V99 5

认识到这个问题是几年前提出的,我认为frmdstryr 使用他的模块 pywinutils在github上提供了一个更优雅的解决方案:

摘抄:

import pythoncom
from win32com.shell import shell,shellcon

def copy(src,dst,flags=shellcon.FOF_NOCONFIRMATION):
    """ Copy files using the built in Windows File copy dialog

    Requires absolute paths. Does NOT create root destination folder if it doesn't exist.
    Overwrites and is recursive by default 
    @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx for flags available
    """
    # @see IFileOperation
    pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)

    # Respond with Yes to All for any dialog
    # @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
    pfo.SetOperationFlags(flags)

    # Set the destionation folder
    dst = shell.SHCreateItemFromParsingName(dst,None,shell.IID_IShellItem)

    if type(src) not in (tuple,list):
        src = (src,)

    for f in src:
        item = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
        pfo.CopyItem(item,dst) # Schedule an operation to be performed

    # @see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
    success = pfo.PerformOperations()

    # @see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
    aborted = pfo.GetAnyOperationsAborted()
    return success is None and not aborted    
Run Code Online (Sandbox Code Playgroud)

这利用 COM 接口,并自动指示需要管理员权限,如果您正在复制到需要管理员权限的目录,您会看到熟悉的对话框提示,并且还在复制操作期间提供典型的文件进度对话框。