我希望我的Python脚本在Vista上复制文件。当我从普通的cmd.exe窗口运行它时,不会生成任何错误,但不会复制文件。如果我以管理员身份运行cmd.exe,然后运行我的脚本,则可以正常运行。

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

我可以从Python脚本中调用UAC提升请求吗(那些对话框显示诸如“诸如此类应用需要管理员访问权限,这样可以吗?”)

如果那是不可能的,我的脚本是否可以至少检测到未提升的脚本,以便它可以正常运行?

评论

stackoverflow.com/a/1445547/1628132在此答案之后,您可以使用py2exe从.py脚本中创建一个.exe,并使用一个名为“ uac_info”的标志,这是一种非常简洁的解决方案

#1 楼

截至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, " ".join(sys.argv), None, 1)


如果使用的是Python 2.x,则应将最后一行替换为:

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


还请注意,如果您将python脚本转换为可执行文件(使用py2execx_freezepyinstaller之类的工具),则应在第四个中使用sys.argv[1:]而不是sys.argv参数。

这里的一些优点是:


不需要外部库。它仅使用标准库中的ctypessys
可在Python 2和Python 3上使用。
无需修改文件资源或创建清单文件。
如果不需要,在if / else语句下添加代码,该代码将永远不会执行两次。
您可以在最后一行获取API调用的返回值,并在失败时采取措施(代码<= 32) 。在此处检查可能的返回值。
您可以更改第六个参数来更改生成的进程的显示方法。

底层ShellExecute调用的文档在此处。

评论


我必须使用unicode实例作为ShellExecuteW的参数(例如u'runas'和unicode(sys.executable))才能运行此代码。

–雅诺施
17年2月15日在17:54

@Janosch,这是因为您使用的是Python 2.x,而我的代码是使用Python 3(其中所有字符串都被视为unicode)。值得一提的是,谢谢!

–马丁·德拉·富恩特(MartínDe la Fuente)
17年2月16日在19:22

@Martin如果我正在Windows命令行中运行此代码,例如:“ python yourcode.py”,它将仅打开python.exe。有办法解决吗?

–user2978216
17年12月15日在10:23

@ 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__))

–哈维尔·乌比洛斯(Javier Ubillos)
17年12月30日在23:41



@HrvojeT ShellExecuteW和ShellExecuteA都是Windows API中对ShellExecute函数的调用。前者要求字符串采用unicode格式,而后者则与ANSI格式一起使用

–马丁·德拉·富恩特(MartínDe la Fuente)
18年4月9日,0:35

#2 楼

我花了一些时间使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)


评论


这似乎只是升高,然后退出...如果我输入一些打印语句,它们将不会再次执行

–乔兰·比斯利(Joran Beasley)
2012年8月13日在21:02

@JoranBeasley,您将看不到任何输出。 ShellExecuteEx不会将其STDOUT重新发布回原始Shell。在这方面,调试将是……挑战。但是,特权提升技巧绝对有效。

–蒂姆·基廷(Tim Keating)
13年2月19日在20:26

@ TimKeating,ActiveState有一个应该使调试更简单的方法:将DebugView实用程序与标准python日志一起使用

– Samwyse
2013年12月17日下午3:54

在同一个控制台中获取输出似乎似乎是不可能的,但是对于ShellExecuteEx使用参数nShow = 5,将打开一个新命令窗口,其中包含提升的脚本的输出。

–埃米尔·斯特尔克(Emil Styrke)
2014年2月13日7:00

对于引用,可以使用subprocess.list2cmdline正确执行。

–终身编码器
15年8月27日在22:35



#3 楼

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


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

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

如果您不想为CreateElevatedProcess API编写讨厌的ctypes包装器,我该怎么做?代码项目文章中介绍了ShellExecuteEx技巧(Pywin32随附ShellExecute的包装器)。怎么样?这样的事情:

程序启动时,它会检查它是否具有管理员权限,如果没有,则使用ShellExecute技巧自行运行并立即退出,如果执行,它将在以下位置执行任务:手。

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

干杯。

评论


感谢这些链接,它们对于我了解很多有关UAC的东西非常有用。

–科伦
09-09-10 23:08

您可能要注意的一点是,可以使用os.startfile($ EXECUTABLE,“ runas”)在不使用PyWin32的情况下执行ShellExecute(安装它时遇到问题)。

–迈克·麦奎德(Mike McQuaid)
10 Mar 8 '10 at 18:03

@Mike-但是runas会弹出一个新提示。并且startfile不接受$ EXECUTABLE的命令行参数。

– Sridhar Ratnakumar
2011-3-29 21:49



我添加了此技术的完整实现的另一个答案,应该可以将其添加到任何python脚本的开头。

–乔连科
2012年7月31日在18:10

第二个链接的文章是“ MSDN杂志2007年1月”中的“最低特权:教您的应用程序可以使用Windows Vista用户帐户控制很好地播放”,但是此问题现在仅作为.chm文件提供。

–彼得
18年1月10日在16:11

#4 楼

只是添加此答案,以防其他人照原样被Google搜索引导到这里。
我在Python脚本中使用了elevate模块,并在Windows 10中使用了Administrator Privileges执行了该脚本。

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

评论


嗨,我尝试使用elevate模块,但收到“系统无法访问该文件”错误,为什么会有这种想法?

–paxos1977
3月13日在22:21

@ paxos1977您可以发布一个演示该错误的代码段吗?谢谢!

–令人讨厌的莫伊
3月19日10:23

#5 楼

以下示例基于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()


#6 楼

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

摘录:

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

#7 楼

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

http://technet.microsoft.com/zh-cn/ magazine / 2008.06.elevation.aspx

我认为,如果您使用它,它将看起来像是“提升python yourscript.py”

#8 楼

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

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

#9 楼

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

runas /user:Administrator "python your_script.py"


评论


小心,提升!=以管理员身份运行

–库格尔
2010-11-28 17:37

我是python的新手...您能告诉我将代码放在哪里吗?

–拉哈特伊斯兰教汗
2015年4月3日,13:47

@RahatIslamKhan:打开命令提示符窗口,然后将其放在以下位置:该命令以管理员用户身份运行your_script.py。确保您了解@Kugel的评论。

– jfs
2015年4月3日在18:14

#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


评论


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

–伯温
16年1月13日在22:19

#11 楼

这主要是对Jorenko答案的升级,它允许在Windows中使用带空格的参数,但在Linux上也应能很好地工作:)
此外,由于我们不使用__file__,但可以使用cx_freeze或py2exe,但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:])