Solicitar elevación de UAC desde un script Python?


Quiero que mi script Python copie archivos en Vista. Cuando lo corro desde una ventana cmd.exe normal, no se generan errores, pero los archivos NO se copian. Si corro cmd.exe "como administrador" y luego corro mi script, funciona bien.

Esto tiene sentido ya que el Control de Cuentas de Usuario (UAC) normalmente evita muchas acciones del sistema de archivos.

¿Hay alguna manera de que pueda, desde un script Python, invocar una solicitud de elevación de UAC (esos diálogos que dicen algo como " tal y tal aplicación necesita administración access, ¿está bien?")

Si eso no es posible, ¿hay alguna forma en que mi script pueda al menos detectar que no está elevado para que pueda fallar correctamente?

Author: Peter O., 2008-09-25

10 answers

A partir de 2017, un método fácil para lograr esto es el siguiente:

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)

Si está utilizando Python 2.x, entonces debe reemplazar la última línea para:

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

También tenga en cuenta que si convirtió su script python en un archivo ejecutable (utilizando herramientas como py2exe, cx_freeze, pyinstaller) luego debe reemplazar el cuarto parámetro por una cadena vacía ("").

Algunas de las ventajas aquí son:

  • No se requieren bibliotecas externas (ni Python para la extensión de Windows). Se solo usa ctypes de la biblioteca estándar.
  • Funciona tanto en Python 2 como en Python 3.
  • No es necesario modificar los recursos del archivo ni crear un archivo de manifiesto.
  • Si no agrega código debajo de la instrucción if/else, el código no se ejecutará dos veces.
  • Puede modificarlo fácilmente para que tenga un comportamiento especial si el usuario rechaza el prompt de UAC.
  • Puede especificar argumentos modificando el cuarto parámetro.
  • Puede especificar el método de visualización modificando el sexto parámetro.

La documentación para la llamada ShellExecute subyacente es aquí.

 46
Author: Martín De la Fuente,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-09 00:24:18

Me tomó un poco de tiempo conseguir que la respuesta de dguaraglia funcionara, así que en el interés de ahorrar tiempo a otros, esto es lo que hice para implementar esta idea:

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)
 61
Author: Jorenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-31 18:09:35

Parece que no hay manera de elevar los privilegios de la aplicación por un tiempo para que pueda realizar una tarea en particular. Windows necesita saber al inicio del programa si la aplicación requiere ciertos privilegios, y le pedirá al usuario que confirme cuándo la aplicación realiza cualquier tarea que necesite esos privilegios. Hay dos maneras de hacer esto:

  1. Escribir un archivo de manifiesto que le diga a Windows que la aplicación podría requerir algunos privilegios
  2. Ejecute la aplicación con privilegios elevados desde dentro de otro programa

Este dos los artículos explican con mucho más detalle cómo funciona esto.

Lo que haría, si no desea escribir un contenedor ctypes desagradable para la API CreateElevatedProcess, es usar el truco ShellExecuteEx explicado en el artículo Code Project (Pywin32 viene con un contenedor para ShellExecute). ¿Cómo? Algo como esto:

Cuando su programa se inicia, comprueba si tiene privilegios de administrador, si no se ejecuta por sí mismo usando el truco ShellExecute y sale inmediatamente, si lo hace, realiza la tarea en cuestión.

Al describir su programa como un "script", supongo que eso es suficiente para sus necesidades.

Salud.
 28
Author: dguaraglia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-25 02:01:34

Reconociendo que esta pregunta se hizo hace años, creo que una solución más elegante se ofrece en github por frmdstryr usando su módulo pyminutils:

Extracto:

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    

Esto utiliza la interfaz COM e indica automáticamente que se necesitan privilegios de administrador con el diálogo familiar que vería si estuviera copiando en un directorio donde se requieren privilegios de administrador y también proporciona el diálogo de progreso del archivo típico durante la operación de copia.

 4
Author: KenV99,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-26 15:03:36

Esto puede no responder completamente a su pregunta, pero también podría intentar usar el comando Elevate Powertoy para ejecutar el script con privilegios UAC elevados.

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

Creo que si lo usas se vería como 'elevate python yourscript.py"

 2
Author: TinyGrasshopper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-24 13:43:33

El siguiente ejemplo se basa en El excelente trabajo y respuesta aceptada de MARTIN DE LA FUENTE SAAVEDRA. En particular, se introducen dos enumeraciones. El primero permite una fácil especificación de cómo se abrirá un programa elevado, y el segundo ayuda cuando los errores deben ser fácilmente identificados. Tenga en cuenta que si desea que todos los argumentos de la línea de comandos se pasen al nuevo proceso, sys.argv[0] probablemente debería reemplazarse con una llamada a la función: 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()
 2
Author: Noctis Skytower,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-14 13:35:42

Si su script siempre requiere privilegios de Administrador entonces:

runas /user:Administrator "python your_script.py"
 1
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-26 11:54:18

Puede hacer un acceso directo en algún lugar y como el uso de destino: python yourscript.py a continuación, en propiedades y opciones avanzadas, seleccione ejecutar como administrador.

Cuando el usuario ejecuta el atajo, le pedirá que eleve la aplicación.

 1
Author: officialhopsof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-02 19:56:40

Una variación del trabajo de Jorenko anterior permite que el proceso elevado use la misma consola (pero vea mi comentario a continuación):

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
 1
Author: Berwyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-13 22:20:08

Esto es principalmente una actualización a la respuesta de Jorenko, que permite usar parámetros con espacios en Windows, pero también debería funcionar bastante bien en Linux :) Además, funcionará con cx_freeze o py2exe ya que no usamos __file__ sino sys.argv[0] como ejecutable

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:])
 1
Author: deajan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 16:52:42