在Python脚本中请求UAC提升?
我想我的Python脚本在Vista上复制文件。 当我从一个正常的cmd.exe
窗口运行它,没有错误生成,但文件不被复制。 如果我运行cmd.exe
“作为pipe理员”,然后运行我的脚本,它工作正常。
这很有意义,因为用户帐户控制(UAC)通常会阻止许多文件系统操作。
有没有一种方法可以从Python脚本中调用UAC提升请求(那些类似于“这样和那样的应用程序需要pipe理员访问的对话框,是这样吗?”)
如果这是不可能的,有没有一种方法我的脚本可以至less检测到它没有提升,所以它可以优雅地失败?
截至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, "", None, 1)
这里的一些优点是:
- 不需要外部库(也不需要Python for Windows扩展)。 它只使用标准库中的
ctypes
。 - 适用于Python 2和Python 3。
- 不需要修改文件资源,也不需要创build清单文件。
- 如果在/ else语句中不添加下面的代码,代码将不会被执行两次。
- 如果用户拒绝UAC提示,则可以轻松修改它以具有特殊行为。
- 您可以指定修改第四个参数的参数。
- 您可以指定修改第六个参数的显示方法。
底层ShellExecute调用的文档在这里 。
我花了一点时间才得到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)
似乎没有办法提升一段时间的应用程序权限,以执行特定的任务。 Windows需要在程序开始时知道应用程序是否需要某些特权,并要求用户确认应用程序何时执行需要这些特权的任务。 有两种方法可以做到这一点:
- 编写一个清单文件,告诉Windows应用程序可能需要一些权限
- 使用另一个程序中的提升特权运行应用程序
这两篇 文章更详细地解释了这是如何工作的。
如果你不想为CreateElevatedProcess API写一个令人讨厌的ctypes包装,我会这样做,就是使用代码项目文章(Pywin32自带的ShellExecute的包装器)中解释的ShellExecuteEx技巧。 怎么样? 像这样的东西:
当你的程序启动时,它会检查它是否具有pipe理员权限,如果它不使用ShellExecute技巧运行,并立即退出,如果它执行,则执行手头的任务。
当你把你的程序描述成一个“脚本”时,我想这足以满足你的需求。
干杯。
认识到这个问题是多年以前提出的,我认为frmdstryr使用模块pyminutils在github上提供了一个更优雅的解决scheme:
摘抄:
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
这将利用COM接口,并自动指出需要使用熟悉的对话框提示的pipe理员权限,如果您要复制到需要pipe理员权限的目录中,并且在复制操作期间还提供了典型的文件进度对话框。
这可能不会完全回答你的问题,但你也可以尝试使用Elevate命令Powertoy以提升UAC权限运行脚本。
http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx
我想如果你使用它,它会看起来像“提升pythonyourscript.py”
如果您的脚本始终需要pipe理员权限,则:
runas /user:Administrator "python your_script.py"
您可以在某处作为快捷方式,并作为目标使用:python yourscript.py然后在属性和高级select以pipe理员身份运行。
当用户执行快捷方式时,会要求他们提升应用程序。
Jorenko上面的工作的一个变种允许升级过程使用相同的控制台(但请参阅下面我的评论):
def spawn_as_administrator(): """ Spawn ourself with administrator rights and wait for new process to exit Make the new process use the same console as the old one. Raise Exception() if we could not get a handle for the new re-run the process Raise pywintypes.error() if we could not re-spawn Return the exit code of the new process, or return None if already running the second admin process. """ #pylint: disable=no-name-in-module,import-error import win32event, win32api, win32process import win32com.shell.shell as shell if '--admin' in sys.argv: return None script = os.path.abspath(sys.argv[0]) params = ' '.join([script] + sys.argv[1:] + ['--admin']) SEE_MASK_NO_CONSOLE = 0x00008000 SEE_MASK_NOCLOSE_PROCESS = 0x00000040 process = shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params, fMask=SEE_MASK_NO_CONSOLE|SEE_MASK_NOCLOSE_PROCESS) hProcess = process['hProcess'] if not hProcess: raise Exception("Could not identify administrator process to install drivers") # It is necessary to wait for the elevated process or else # stdin lines are shared between 2 processes: they get one line each INFINITE = -1 win32event.WaitForSingleObject(hProcess, INFINITE) exitcode = win32process.GetExitCodeProcess(hProcess) win32api.CloseHandle(hProcess) return exitcode
下面的例子build立在MARTIN DE LA FUENTE SAAVEDRA的出色工作和接受的答案上。 特别介绍了两个枚举。 第一个function允许轻松指定如何打开高级程序,第二个function则可以帮助识别错误。 请注意,如果您希望将所有命令行parameter passing给新进程,则可能应将sys.argv[0]
replace为函数调用: subprocess.list2cmdline(sys.argv)
。
#! /usr/bin/env python3 import ctypes import enum import sys # Reference: # msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx 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: hinstance = ctypes.windll.shell32.ShellExecuteW( None, 'runas', sys.executable, sys.argv[0], None, SW.SHOWNORMAL ) if hinstance <= 32: raise RuntimeError(ERROR(hinstance)) def main(): # Your Code Here print(input('Echo: ')) if __name__ == '__main__': bootstrap()