2008-09-25 30 views
59

Quiero que mi script de Python copie archivos en Vista. Cuando lo ejecuto desde una ventana normal cmd.exe, no se generan errores, pero los archivos NO se copian. Si ejecuto cmd.exe "como administrador" y luego ejecuto mi script, funciona bien.¿Solicitar elevación de UAC desde dentro de un script de Python?

Esto tiene sentido ya que el Control de cuentas de usuario (UAC) normalmente impide muchas acciones del sistema de archivos.

¿Hay una manera que puedo, desde un script Python, invocar una petición de elevación de UAC (los cuadros de diálogo que decir algo como "aplicación de tal o cual necesita acceso de administrador, ¿Está bien?")

Si eso es no es posible, ¿hay alguna manera en que mi script al menos pueda detectar que no está elevado por lo que puede fallar con elegancia?

+2

http://stackoverflow.com/a/1445547/1628132 tras esta respuesta se crea un .exe desde el script .py usando py2exe y el uso de una bandera llamada 'uac_info' Es una solución bastante limpio – foxcoreg

Respuesta

30

A partir de 2017, un método fácil para lograr esto es la 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, "", None, 1) 

Algunas de las ventajas de aquí son:

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

La documentación para la llamada ShellExecute subyacente es here.

+7

Tuve que usar instancias Unicode como parámetros para ShellExecuteW (como u'runas 'y unicode (sys.executable)) para ejecutar esto. – Janosch

+4

@Janosch, eso se debe a que está utilizando Python 2.x, mientras que mi código está en Python 3 (donde todas las cadenas se tratan como unicodes). Pero es bueno mencionarlo, ¡gracias! –

+2

@Martin si estoy ejecutando este código desde la línea de comandos de Windows de esta manera: "python yourcode.py" acaba de abrir python.exe. ¿Hay alguna manera de arreglarlo? – user2978216

29

Parece que no hay forma 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 cuando la aplicación realice cualquier tarea que necesite esos privilegios. Hay dos maneras de hacer esto:

  1. escribir un archivo de manifiesto que indica a Windows la aplicación podría requerir algunos privilegios
  2. ejecutar la aplicación con privilegios elevados desde el interior de otro programa

Este twoarticles explicar con mucho más detalle cómo funciona esto.

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

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

Como usted describe su programa como un "script", supongo que eso es suficiente para sus necesidades.

Saludos.

+0

Gracias por esos enlaces, fueron muy útiles para que descubriera mucho sobre las cosas de UAC. – Colen

+4

Algo que quizás desee tener en cuenta es que puede ejecutar ShellExecute sin PyWin32 (tuve problemas para instalarlo) utilizando os.startfile ($ EXECUTABLE, "runas"). –

+0

@Mike - pero 'runas' trae un nuevo mensaje. Y el archivo de inicio no acepta los argumentos de línea de comando para '$ EXECUTABLE.' –

1

Si la secuencia de comandos siempre requiere privilegios de administrador a continuación:

runas /user:Administrator "python your_script.py" 
+12

careful, elevation! = Ejecutándose como administrador – Kugel

+0

Soy nuevo en Python ... ¿puedes decirme dónde pondré ese código? –

+0

@RahatIslamKhan: abra una ventana del símbolo del sistema y guárdelo donde: el comando ejecuta 'your_script.py' como usuario de administrador. Asegúrese de entender [comentario de @Kugel] (http://stackoverflow.com/questions/130763/request-uac-elevation-from-within-a-python-script/138970?noredirect=1#comment4664946_138970). – jfs

58

Me tomó un poco de tiempo para obtener su respuesta en dguaraglia de trabajo, por lo que en el interés de ahorrar tiempo de los demás , esto es lo que hice para poner en práctica 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) 
+1

esto parece elevarse y luego salir ... si pongo algunas declaraciones impresas no se ejecutan por segunda vez –

+2

Este es uno de los mejores trucos que he visto en mi vida. –

+6

@JoranBeasley, no verá ningún resultado. ShellExecuteEx no publica su STDOUT de nuevo en el shell de origen. En ese sentido, la depuración será ... desafiante. Pero el truco de izar privilegios definitivamente funciona. –

1

puede crear un acceso directo en alguna parte y como el uso de destino: pitón yourscript.py luego bajo propiedades y selección avanzada ejecutar como administrador.

Cuando el usuario ejecuta el acceso directo, le pedirá que eleve la aplicación. Hace

4

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

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  

Este utiliza la interfaz COM y automáticamente indica que los privilegios de administrador son necesarios con el cuadro de 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 de archivo típico durante la operación de copia.

+0

Ver respuesta por @frmdstryr aquí: http: //stackoverflow.com/a/19989764/281545 –

1

Una variación en la obra de Jorenko anterior permite que el proceso de elevación a utilizar la misma consola (pero ver 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 
+0

Lo siento. la misma opción de consola (SEE_MASK_NO_CONSOLE) solo funciona si ya está elevado. Mi error. – Berwyn

1

El siguiente ejemplo se basa en MARTIN DE LA FUENTE SAAVEDRA excelente trabajo y respuesta aceptada. En particular, se introducen dos enumeraciones. El primero permite una fácil especificación de cómo se debe abrir un programa elevado, y el segundo ayuda cuando los errores deben identificarse fácilmente. Tenga en cuenta que si desea que todos los argumentos de la línea de comando 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() 
Cuestiones relacionadas