¿Solicitar elevación de UAC desde un script de Python?
Quiero que mi script Python copie archivos en Vista. Cuando lo ejecuto desde una cmd.exe
ventana 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 impide muchas acciones del sistema de archivos.
¿Hay alguna manera de que pueda, desde un script de Python, invocar una solicitud de elevación de UAC (esos cuadros de diálogo que dicen algo como "tal o cual aplicación necesita acceso de administrador, está bien?")
Si eso no es posible, ¿hay alguna manera de que mi script pueda al menos detectar que no está elevado para que pueda fallar sin problemas?
A partir de 2017, un método sencillo para lograrlo 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, " ".join(sys.argv), None, 1)
Si está utilizando Python 2.x, debe reemplazar la última línea por:
ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1)
También tenga en cuenta que si convirtió su script de Python en un archivo ejecutable (usando herramientas como ,, py2exe
) , entonces debería usarlo en lugar de en el cuarto parámetro.cx_freeze
pyinstaller
sys.argv[1:]
sys.argv
Algunas de las ventajas aquí son:
- No se requieren bibliotecas externas. Solo usa
ctypes
ysys
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 declaración if/else, el código nunca se ejecutará dos veces.
- Puede obtener el valor de retorno de la llamada API en la última línea y realizar una acción si falla (código <= 32). Consulta los posibles valores de retorno aquí .
- Puede cambiar el método de visualización del proceso generado modificando el sexto parámetro.
La documentación para la llamada ShellExecute subyacente está aquí .
Me tomó un poco de tiempo hacer que la respuesta de dguaraglia funcionara, por lo que para ahorrar tiempo a los demás, 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 manera de elevar los privilegios de la aplicación por un tiempo para que puedas 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 alguna 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 dos artículos explican con mucho más detalle cómo funciona esto.
Lo que haría, si no quieres escribir un contenedor de 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, verifica si tiene privilegios de administrador, si no los tiene, se ejecuta solo usando el truco ShellExecute y sale inmediatamente; si los tiene, realiza la tarea en cuestión.
Como describe su programa como un "script", supongo que es suficiente para sus necesidades.
Salud.