python程序,实现以管理员方式运行程序,也就是提升程序权限

2023-05-16,,

quest UAC elevation from within a Python script?

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

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

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

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

 相关讨论

 

stackoverflow.com/a/1445547/1628132在这个答案之后你使用py2exe从.py脚本创建一个.exe并使用一个名为'uac_info'的标志它是非常简洁的解决方案


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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,那么您应该替换最后一行:

1 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调用的文档在这里。

 相关讨论

 

我不得不使用unicode实例作为ShellExecuteW的参数(如u'runas'和unicode(sys.executable))来运行它。
@Janosch,那是因为你使用的是Python 2.x,而我的代码是在Python 3中(其中所有字符串都被视为unicodes)。但是值得一提,谢谢!
@Martin如果我从Windows命令行运行这样的代码:"python yourcode.py"它只是打开python.exe。有办法解决吗?
@ 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__))
ShellExecuteW和ShellExecute有什么区别?
谢谢@JavierUbillos,我刚刚编辑了答案
@HrvojeT两者,ShellExecuteW和ShellExecuteA都是对Windows API中ShellExecute函数的调用。前者要求字符串采用unicode格式,后者使用ANSI格式
谢谢。你能告诉我为什么用pyinstaller而不是__file__制作可执行文件时需要空字符串""?
使用py2exe或cx_freeze创建可执行文件,变量__file__不存在。我不是在这里使用空变量,而是用sys.argv[0]替换__file__
@HrvojeT运行python脚本(.py)时,sys.executable解析为PYTHONPATH(e.x C:\Python34\python.exe),因此您需要将文件名作为参数传递。运行转换为可执行文件的脚本时,sys.executable会解析为可执行文件本身,因此不需要参数。此外,由于@deajan提及__file__变量甚至没有在最后一种情况下定义,所以你不应该使用它。
谢谢。为什么不在脚本中使用sys.argv [0],而不是od文件?
我的回答是假设我们实际上并不关心参数。所以,在python脚本中使用sys.argv[0]或__file__都可以。如果您将其转换为可执行文件,则可以使用"","anyrandomstring",sys.argv[0],它也可以正常工作(但不是__file__,因为它未定义)。现在,如果你关心参数,你应该使用"".join(sys.argv)作为python脚本,使用"".join(sys.argv[1:])作为转换后的脚本。 @HrvojeT
请有人告诉我为什么需要try / except块。
是否有可能解析退出状态?
@ solstice333是的,确实如此。您可以在文档中查看,ShellExecuteW返回一个整数。如果它等于或大于32,那么一切都没问题,否则你就出错了。
@MartnDelaFuente你看到了并试了一下。在我的ShellExecuteW'ed过程中,我退出1.当我在父进程中使用返回值时,当我期望小于或等于32时,它会让我回到42.忘记在我之前的评论中提到它。
当正在运行的应用程序是使用python -m module而不是脚本运行的python模块时,您将如何使这项工作?


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

1
2
3
4
5
6
7
8
9
10
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)

 相关讨论

 

这似乎提升然后退出...如果我输入一些打印语句,他们不会再次执行
这是我见过的最酷的技巧之一。
@JoranBeasley,你不会看到任何输出。 ShellExecuteEx不会将其STDOUT发布回原始shell。在这方面,调试将是......具有挑战性。但特权提升技巧肯定有效。
完美适合我的需求,谢谢!
@TimKeating,ActiveState有一个配方,应该使调试更容易:使用DebugView实用程序与标准python日志记录
似乎不可能在同一个控制台中获取输出,但是使用参数nShow = 5到ShellExecuteEx,将使用提升脚本的输出打开一个新的命令窗口。
如果子将其输出重定向到提供的PIPE,则可能是可能的。一些链接stackoverflow.com/questions/4093252/
一些建议的改进:使用bool(windll.advpack.IsNTAdmin(0, None))来检测当前它是否为admin(使用来自ctypes import windll)。此外,您的params创建还应包括正确的引用,以便带有空格的参数和脚本可以工作。引用每一个是一个好的开始('"'+'""'.join(...)+'"')但是为了更好,你还需要检测参数中的嵌入式引号。
对于引用,您可以使用subprocess.list2cmdline来正确执行此操作。
谢谢,我只需要以管理员身份运行单个子流程,这样就完美了:import win32com.shell.shell as shell shell.ShellExecuteEx(lpVerb='runas', lpFile='net', lpParameters='start service', nShow=5) assert res['hInstApp'] > 32 Microsoft win32-ShellExecuteEx Docs


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

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

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

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

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

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

干杯。

 相关讨论

 

感谢这些链接,它们对我找到很多关于UAC的东西非常有用。
您可能需要注意的一点是,您可以使用os.startfile($ EXECUTABLE,"runas")在没有PyWin32的情况下执行ShellExecute(我在安装它时遇到了问题)。
@Mike - 但是runas会带来一个新的提示。并且startfile不接受$EXECUTABLE.的命令行参数
我添加了另一个答案,该技术的完整实现应该可以添加到任何python脚本的开头。
第二个链接的文章是"最低权限:使用Windows Vista用户帐户控制教你的应用程序","MSDN Magazine 2007年1月",但此问题现在仅作为.chm文件提供。


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

摘抄:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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接口,并自动指示熟悉的对话框提示需要管理员权限,如果您要复制到需要管理员权限的目录中,您将看到该提示,并在复制操作期间提供典型的文件进度对话框。

 相关讨论

 

请在此处查看@frmdstryr的答案:stackoverflow.com/a/19989764/281545


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#! /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()


这可能无法完全回答您的问题,但您也可以尝试使用Elevate Command Powertoy以使用提升的UAC权限运行脚本。

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

我想如果你使用它看起来像'提升python yourscript.py'


这主要是对Jorenko的答案的升级,它允许在Windows中使用带空格的参数,但在Linux上也应该运行得相当好:)
此外,将使用cx_freeze或py2exe,因为我们不使用__file__但sys.argv[0]作为可执行文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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:])


Jorenko上面工作的变体允许提升的进程使用相同的控制台(但请参阅下面的评论):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

 相关讨论

 

抱歉。相同的控制台选项(SEE_MASK_NO_CONSOLE)仅在您已经提升时才有效。我的错。


您可以在某处创建快捷方式并作为目标使用:
python yourscript.py
然后在属性和高级选择下以管理员身份运行。

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


如果您的脚本始终需要管理员权限,那么:

1 runas /user:Administrator"python your_script.py"

 相关讨论

 

小心,提升!=以管理员身份运行
我是python的新手......你能告诉我在哪里放这个代码吗?
@RahatIslamKhan:打开命令提示符窗口并将其放在以下位置:该命令以管理员用户身份运行your_script.py。确保你理解@ Kugel的评论。

python程序,实现以管理员方式运行程序,也就是提升程序权限的相关教程结束。

《python程序,实现以管理员方式运行程序,也就是提升程序权限.doc》

下载本文的Word格式文档,以方便收藏与打印。