que - ¿Solicitar elevación de UAC desde dentro de un script de Python?
que es argv en python (10)
A partir de 2017, un método fácil de 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 por:
ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(__file__), None, 1)
También tenga en cuenta que si convirtió su secuencia de comandos python en un archivo ejecutable (utilizando herramientas como py2exe
, cx_freeze
, pyinstaller
), entonces debe reemplazar el cuarto parámetro por una cadena vacía ( ""
).
Algunas de las ventajas aquí son:
- No se requieren bibliotecas externas (ni la extensión de Python para Windows). Solo utiliza los
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 el código debajo de la declaración if / else, el código nunca se ejecutará dos veces.
- Puede modificarlo fácilmente para que tenga un comportamiento especial si el usuario rechaza el aviso de 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 está here .
Quiero que mi script de Python copie archivos en Vista. Cuando lo ejecuto desde una ventana cmd.exe
normal, no se generan errores, pero los archivos NO se copian. Si ejecuto cmd.exe
"como administrador" y luego ejecuto 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, desde dentro de un script de Python, invoque una solicitud de elevación de UAC (esos diálogos que dicen algo como "tal o cual aplicación necesita acceso de administrador, ¿está bien?")
Si eso 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?
El siguiente ejemplo se basa en el excelente trabajo de MARTIN DE LA FUENTE SAAVEDRA y la 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()
Es posible que esto no responda completamente a su pregunta, pero también podría intentar usar Elevate Command Powertoy para ejecutar el script con privilegios elevados de UAC.
http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx
Creo que si lo usas se vería como ''elevar python yourscript.py''
Esto es principalmente una actualización de 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:])
Me llevó un poco de tiempo lograr que la respuesta de dguaraglia funcionara, por lo que, con el interés de ahorrarles 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)
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 cuándo la aplicación realiza cualquier tarea que necesite esos privilegios. Hay dos maneras de hacer esto:
- Escriba un archivo de manifiesto que le indique a Windows que la aplicación podría requerir algunos privilegios
- Ejecute la aplicación con privilegios elevados desde dentro de otro programa
Estos two articles explican con mucho más detalle cómo funciona esto.
Lo que haría, si no quiere escribir un contenedor de tipos desagradable para la API CreateElevatedProcess, es usar el truco ShellExecuteEx explicado en el artículo de Code Project (Pywin32 viene con un contenedor para ShellExecute). ¿Cómo? Algo como esto:
Cuando se inicia el programa, comprueba si tiene privilegios de Administrador, si no se ejecuta con el truco ShellExecute y sale inmediatamente, si lo hace, realiza la tarea en cuestión.
Al describir su programa como un "guión", supongo que eso es suficiente para sus necesidades.
Aclamaciones.
Puede hacer un atajo en alguna parte y como destino: python 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.
Reconociendo que esta pregunta se hizo hace años, creo que se ofrece una solución más elegante 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 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.
Si su script siempre requiere los privilegios de un Administrador, entonces:
runas /user:Administrator "python your_script.py"
Una variación del trabajo anterior de Jorenko 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