Запросить повышение UAC из скрипта Python?

Я хочу, чтобы мой скрипт на Python копировал файлы в Vista. Когда я запускаю его из нормального cmd.exe окно, ошибки не генерируются, но файлы НЕ копируются. Если я бегу cmd.exe "как администратор", а затем запустить мой скрипт, он работает нормально.

Это имеет смысл, поскольку контроль учетных записей (UAC) обычно предотвращает многие действия файловой системы.

Есть ли способ, которым я могу из скрипта Python вызвать запрос на повышение прав UAC (те диалоги, которые говорят что-то вроде "такое-то и такое-приложение требует доступа администратора, это нормально?")

Если это невозможно, есть ли способ, по которому мой сценарий может, по крайней мере, обнаружить, что он не повышен, поэтому он может завершиться неудачно?

12 ответов

Решение

Начиная с 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) тогда вы должны заменить четвертый параметр на пустую строку ("").

Некоторые из преимуществ здесь:

  • Никаких внешних библиотек не требуется (ни расширение Python для Windows). Он использует только ctypes из стандартной библиотеки.
  • Работает как на Python 2, так и на Python 3.
  • Нет необходимости ни изменять файловые ресурсы, ни создавать файл манифеста.
  • Если вы не добавите код ниже в оператор if / 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 необходимо знать, требует ли приложение определенных привилегий, и попросить пользователя подтвердить, когда приложение выполняет какие-либо задачи, для которых необходимы эти привилегии. Есть два способа сделать это:

  1. Напишите файл манифеста, который сообщает Windows, что приложению могут потребоваться некоторые привилегии.
  2. Запустите приложение с повышенными привилегиями из другой программы

Эти две статьи объясняют гораздо более подробно, как это работает.

Что бы я сделал, если вы не хотите писать противную оболочку ctypes для API CreateElevatedProcess, это использовать трюк ShellExecuteEx, описанный в статье проекта кода (Pywin32 поставляется с оболочкой для ShellExecute). Как? Что-то вроде этого:

Когда ваша программа запускается, она проверяет, имеет ли она права администратора, если она не запускается сама, используя трюк ShellExecute, и немедленно завершает работу, если она это делает, она выполняет задачу под рукой.

Поскольку вы описываете свою программу как "скрипт", я полагаю, этого достаточно для ваших нужд.

Приветствия.

Просто добавляю этот ответ на тот случай, если другие направляются сюда поиском Google, как и я. Я использовалelevate модуль в моем скрипте Python и скрипт, выполняемый с правами администратора в Windows 10.

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

Следующий пример основан на превосходной работе Мартина де ла Фюенте Сааведры и принятом ответе. В частности, введены два перечисления. Первый позволяет легко определить способ открытия программы с повышенными правами, а второй помогает, когда необходимо легко идентифицировать ошибки. Обратите внимание, что если вы хотите, чтобы все аргументы командной строки передавались новому процессу, 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()

Признавая, что этот вопрос задавался много лет назад, я думаю, что frmdstryr с помощью своего модуля pyminutils предлагает на 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    

При этом используется интерфейс COM и автоматически указывается, что необходимы привилегии администратора в знакомой диалоговой подсказке, которую вы увидите, если вы копируете в каталог, где требуются права администратора, а также предоставляется типичный диалог хода выполнения файла во время операции копирования.

Для однострочников поместите код туда, где вам нужен UAC.

Запросите UAC, если не удалось, продолжайте работу:

      import ctypes, sys

ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
    None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and exit()


Запросить UAC, если не удалось, выйти:

      import ctypes, sys

ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
    None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, exit())

Стиль функции:

      # Created by BaiJiFeiLong@gmail.com at 2022/6/24
import ctypes
import sys


def request_uac_or_skip():
    ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
        None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and sys.exit()


def request_uac_or_exit():
    ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
        None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, sys.exit())

Это может не полностью ответить на ваш вопрос, но вы также можете попробовать использовать Elevate Command Powertoy для запуска сценария с повышенными привилегиями UAC.

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

Я думаю, что если вы используете его, он будет выглядеть как "поднять python yourscript.py"

Разновидность работы 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

В основном это обновление к ответу Йоренко, которое позволяет использовать параметры с пробелами в 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:])

Вы можете сделать ярлык где-нибудь и в качестве цели использовать: python yourscript.py, затем в свойствах и расширенный выберите запуск от имени администратора.

Когда пользователь выполняет ярлык, он попросит его поднять приложение.

Если ваш сценарий всегда требует прав администратора, то:

runas /user:Administrator "python your_script.py"
Другие вопросы по тегам