The ShellExecute API call will spawn a new process, it won't elevate permissions for the current process running.
Let's analyze this code snippet:
if is_admin():
main()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
When you first run this Python script without privileges, this initial process will jump to the last line of the code because is_admin returns False. Once there, the UAC prompt is displayed.
- If the UAC prompt is accepted, then a completely new process (with different process ID) is created and the code is executed again (from the beginning), but this time with admin privileges. Now
is_adminshould returnTrueandmainshould be called. - If the UAC prompt is rejected, no new process is created.
Regardless of the UAC response, the initial process will get the return code back, but its privileges will remain unaltered.
If you want to try this yourself, add an input() at the end of the file and you should be able to see two different windows after accepting the UAC prompt.
To avoid having your code being executed twice be sure to keep everything inside the main function. If you want to take an action based on the return code, this only makes sense for failures (code <= 32). If the return code is successfull (> 32), then the process should end gracefully and let the new spawned process do its job.
The ShellExecute API call will spawn a new process, it won't elevate permissions for the current process running.
Let's analyze this code snippet:
if is_admin():
main()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
When you first run this Python script without privileges, this initial process will jump to the last line of the code because is_admin returns False. Once there, the UAC prompt is displayed.
- If the UAC prompt is accepted, then a completely new process (with different process ID) is created and the code is executed again (from the beginning), but this time with admin privileges. Now
is_adminshould returnTrueandmainshould be called. - If the UAC prompt is rejected, no new process is created.
Regardless of the UAC response, the initial process will get the return code back, but its privileges will remain unaltered.
If you want to try this yourself, add an input() at the end of the file and you should be able to see two different windows after accepting the UAC prompt.
To avoid having your code being executed twice be sure to keep everything inside the main function. If you want to take an action based on the return code, this only makes sense for failures (code <= 32). If the return code is successfull (> 32), then the process should end gracefully and let the new spawned process do its job.
import ctypes
import sys
import platform
def admin() -> "Admin Bool":
"""Requests UAC Admin on Windows with a prompt"""
if platform.system() == "Windows":
ctypes.windll.shell32.ShellExecuteW(
None,
'runas',
sys.executable,
' '.join(sys.argv),
None,
None
)
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
else:
raise OSError("admin() only works for windows.")
This will request admin with an admin prompt.
If True is returned, then admin has been granted by the user. Else, False will be returned.
As of 2017, an easy method to achieve this is the following:
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)
If you are using Python 2.x, then you should replace the last line for:
ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1)
Also note that if you converted you python script into an executable file (using tools like py2exe, cx_freeze, pyinstaller) then you should use sys.argv[1:] instead of sys.argv in the fourth parameter.
Some of the advantages here are:
- No external libraries required. It only uses
ctypesandsysfrom standard library. - Works on both Python 2 and Python 3.
- There is no need to modify the file resources nor creating a manifest file.
- If you don't add code below if/else statement, the code won't ever be executed twice.
- You can get the return value of the API call in the last line and take an action if it fails (code <= 32). Check possible return values here.
- You can change the display method of the spawned process modifying the sixth parameter.
Documentation for the underlying ShellExecute call is here.
It took me a little while to get dguaraglia's answer working, so in the interest of saving others time, here's what I did to implement this 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)
It looks like types.TupleType and types.ListType do not exist in Python 3. Try the following instead:
elif type(cmdLine) not in (tuple, list)
The value error after saying that "cmdLine is not a sequence" is not exactly accurate because strings are sequences, but should indeed raise a ValueError. I might reword it to "cmdLine should be a non-empty tuple or list, or None." You could update it to more broadly check whether cmdLine is a non-string iterable, but that might be overkill.
The following example demonstrates a simple way to have a program run with elevated privileges on Windows. The enumerations are meant to simplify some values needed when interacting with the operating system. The first allows for easy specification of how an elevated program is to be opened, and the second helps when errors need to be easily identified. Please note that if you want all command line arguments passed to the new process, sys.argv[0] should probably be replaced with a function call: 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()