代码之家  ›  专栏  ›  技术社区  ›  jwfearn

从python脚本中请求UAC提升?

  •  72
  • jwfearn  · 技术社区  · 17 年前

    我希望我的python脚本在vista上复制文件。当我把它从一个正常的 cmd.exe 窗口中,没有生成任何错误,但没有复制文件。如果我跑 命令提示符 “作为管理员”,然后运行我的脚本,它可以正常工作。

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

    在python脚本中,有没有一种方法可以调用UAC提升请求(那些对话框会说类似于“such and such app needs admin access,this ok?”)

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

    10 回复  |  直到 17 年前
        1
  •  55
  •   Martín De la Fuente    8 年前

    截至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)
    

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

    ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1)
    

    还要注意,如果您将python脚本转换为可执行文件(使用类似 py2exe , cx_freeze , pyinstaller )然后应该替换空字符串的第四个参数( "" )

    这里的一些优势是:

    • 不需要外部库(Windows扩展也不需要python)。它只使用 ctypes 来自标准库。
    • 同时适用于python 2和python 3。
    • 不需要修改文件资源,也不需要创建清单文件。
    • 如果不在if/else语句下面添加代码,代码将不会执行两次。
    • 如果用户拒绝UAC提示,您可以很容易地将其修改为具有特殊行为。
    • 您可以指定修改第四个参数的参数。
    • 您可以指定修改第六个参数的显示方法。

    底层ShellExecute调用的文档是 here .

        2
  •  62
  •   Jorenko    13 年前

    我花了一点时间才让德古拉格里亚的回答起作用,因此为了节省别人的时间,我采取了以下措施来实现这个想法:

    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)
    
        3
  •  28
  •   dguaraglia    17 年前

    似乎暂时无法提升应用程序特权,使您无法执行特定任务。Windows需要在程序开始时知道应用程序是否需要某些特权,并要求用户确认应用程序何时执行任何 需要 那些特权。有两种方法可以做到这一点:

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

    这个 two articles 更详细地解释这是如何工作的。

    如果您不想为CreateElevatedProcess API编写一个讨厌的CTypes包装器,我会做的是使用代码项目文章中解释的ShellExecuteEx技巧(Pywin32附带了一个用于ShellExecute的包装器)。怎样?像这样:

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

    当你把你的程序描述成一个“脚本”时,我想这已经足够满足你的需求了。

    干杯。

        4
  •  4
  •   KenV99    10 年前

    认识到这个问题是多年前提出的,我认为在 github 通过frmdstryr使用他的模块pyminutils:

    Excerpt:

    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界面,并自动指示需要管理员权限,同时使用熟悉的对话框提示,您将看到是否要复制到需要管理员权限的目录中,并在复制操作期间提供典型的文件进度对话框。

        5
  •  2
  •   TinyGrasshopper    15 年前

    这可能无法完全回答您的问题,但您也可以尝试使用elevate命令powertoy,以便以提升的UAC权限运行脚本。

    http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx

    我想如果你使用它的话,它会看起来像“提升python yourscript.py”

        6
  •  2
  •   Noctis Skytower    9 年前

    下面的示例建立在 Martin de la Fuente Saavedra的 出色的工作和接受的回答。特别介绍了两种枚举。第一个允许简单地说明如何打开提升的程序,第二个允许在需要容易识别错误时帮助。请注意,如果希望将所有命令行参数传递给新进程, sys.argv[0] 应该用函数调用替换: 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()
    
        7
  •  1
  •   jfs    17 年前

    如果脚本始终需要管理员权限,则:

    runas /user:Administrator "python your_script.py"
    
        8
  •  1
  •   officialhopsof    12 年前

    您可以在某个地方创建快捷方式,作为目标用途: python yourscript.py版 然后在“属性和高级”下选择“以管理员身份运行”。

    当用户执行快捷方式时,会要求他们提升应用程序。

        9
  •  1
  •   Berwyn    10 年前

    上面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
    
        10
  •  1
  •   Orsiris de Jong    8 年前

    这主要是对jorenko答案的升级,它允许在Windows中使用带有空格的参数,但在Linux上也应该可以很好地工作:) 此外,还可以使用cx-freeze或py2exe,因为我们不使用 __file__ 但是 sys.argv[0] 作为可执行文件

    import sys,ctypes,platform
    
    def is_admin():
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            raise False
    
    if __name__ == '__main__':
    
        if platform.system() == "Windows":
            if is_admin():
                main(sys.argv[1:])
            else:
                # Re-run the program with admin rights, don't use __file__ since py2exe won't know about it
                # Use sys.argv[0] as script path and sys.argv[1:] as arguments, join them as lpstr, quoting each parameter or spaces will divide parameters
                lpParameters = ""
                # Litteraly quote all parameters which get unquoted when passed to python
                for i, item in enumerate(sys.argv[0:]):
                    lpParameters += '"' + item + '" '
                try:
                    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, lpParameters , None, 1)
                except:
                    sys.exit(1)
        else:
            main(sys.argv[1:])
    
    推荐文章