I'm attempting to create a utility tool via Python 3.x for the Windows 10 command-line. Since it will better format general command-line commands into more user-friendly menus, I want it to require elevated permissions through UAC when it runs.
I'm using the ctypes
method described here, and it does indeed have Python's executable request UAC elevation.
However, since a lot of the things I'll be writing menus and the like for will require (or be heavily limited without) these elevated permissions, I want the script to exit (preferably through sys.exit
) if it doesn't find any.
In the ctypes
method I mentioned, it should run as follows;
It defines a function is_admin()
, which gets the value of ctypes.windll.shell32.IsUserAnAdmin()
, and if it's 0, returns false instead.
is_admin()
is called in a conditional, and if it gets false, it attempts to execute the command-line command to re-run the script as an executable using ShellExecuteW
and some variables from sys
;
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv[0]), None, 1)
In my code, I have the above conditional with the addition of a variable elevReq
that I set to true;
if is_admin():
success("Already running as administrator!") # "success" and "warn" are defined earlier
elevReq = True
else:
warn("Requesting administrative permissions...", False)
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv[0]), None, 1)
elevReq = True
I follow it up with another conditional that checks to see if elevReq
is true and is_admin()
is false, to see if the user selected "no" on UAC's pop-up -- if it is, it should throw an error about the lack of elevated privileges, and then quit;
if elevReq and is_admin() == False:
error("[FATAL] Elevation was not given! Stopping...", True)
sys.exit(1)
The problem I'm having is that the given method doesn't seem to actually be elevating permissions of Python. UAC does pop up, but when any option is selected, it doesn't seem to matter, as the above condition fires anyway. Manually running the script in an elevated command prompt from the start doesn't have this issue.
Is this an issue with the script not re-loading when it should? If not, why is it exiting anyway?
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.
is_admin
should return True
and main
should be called.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.