ChatGPT解决这个技术问题 Extra ChatGPT

Request UAC elevation from within a Python script?

I want my Python script to copy files on Vista. When I run it from a normal cmd.exe window, no errors are generated, yet the files are NOT copied. If I run cmd.exe "as administator" and then run my script, it works fine.

This makes sense since User Account Control (UAC) normally prevents many file system actions.

Is there a way I can, from within a Python script, invoke a UAC elevation request (those dialogs that say something like "such and such app needs admin access, is this OK?")

If that's not possible, is there a way my script can at least detect that it is not elevated so it can fail gracefully?

stackoverflow.com/a/1445547/1628132 following this answer you create a .exe from the .py script using py2exe and using an flag called 'uac_info' it's pretty neat solution

M
Martín De la Fuente

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 ctypes and sys from 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.


I had to use unicode instances as parameters for ShellExecuteW (like u'runas' and unicode(sys.executable)) to get this running.
@Janosch, that's because you are using Python 2.x, while my code is in Python 3 (where all strings are treated as unicodes). But it is good to mention, thanks!
@Martin if I'm running this code from Windows command line like this: "python yourcode.py" it just opens python.exe. Is there a way fix it?
@user2978216 I had the same issue. In the line ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1) sys.executable resolves to only the python interpreter (e.g. C:\Python27\Python.exe) The solution is to add the running script as an argument (replacting ""). ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1) Also note, for this to work in python 2.x, all string arguments need to be unicode (i.e. u"runas", unicode(sys.executable) and unicode(__file__))
@HrvojeT Both, ShellExecuteW and ShellExecuteA are calls to the ShellExecute function in the Windows API. The former obligates the strings to be in unicode format and the latter is used with ANSI format
J
Jorenko

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)

this just seems to elevate and then exit ... if i put in some print statements they dont get executed a second time
@JoranBeasley, you will not see any output. ShellExecuteEx doesn't post its STDOUT back to the originating shell. In that respect, debugging will be... challenging. But the privilege-hoisting trick definitely works.
@TimKeating, ActiveState has a recipe that should make debugging a bit easier: Use DebugView utility with standard python logging
it does seem impossible to get the output in the same console, but with the argument nShow=5 to ShellExecuteEx, a new command window will open with the output from the elevated script.
For the quoting, you can use subprocess.list2cmdline to do it properly.
d
dguaraglia

It seems there's no way to elevate the application privileges for a while for you to perform a particular task. Windows needs to know at the start of the program whether the application requires certain privileges, and will ask the user to confirm when the application performs any tasks that need those privileges. There are two ways to do this:

Write a manifest file that tells Windows the application might require some privileges Run the application with elevated privileges from inside another program

This two articles explain in much more detail how this works.

What I'd do, if you don't want to write a nasty ctypes wrapper for the CreateElevatedProcess API, is use the ShellExecuteEx trick explained in the Code Project article (Pywin32 comes with a wrapper for ShellExecute). How? Something like this:

When your program starts, it checks if it has Administrator privileges, if it doesn't it runs itself using the ShellExecute trick and exits immediately, if it does, it performs the task at hand.

As you describe your program as a "script", I suppose that's enough for your needs.

Cheers.


Thanks for those links, they were very useful for me finding out a lot about UAC stuff.
Something you might want to note on this is that you can do ShellExecute without PyWin32 (I had problems getting it installed) by using os.startfile($EXECUTABLE, "runas").
@Mike - but runas brings up a new prompt though. And startfile doesn't accept command line arguments to $EXECUTABLE.
I added another answer with a full implementation of this technique that should be able to be added to the start of any python script.
The article to the second link was "Least Privilege: Teach Your Apps To Play Nicely With Windows Vista User Account Control" in "MSDN Magazine January 2007", but this issue is now only available as .chm file.
I
Irving Moy

Just adding this answer in case others are directed here by Google Search as I was. I used the elevate module in my Python script and the script executed with Administrator Privileges in Windows 10.

https://pypi.org/project/elevate/


Hey, I tried using elevate module and I'm getting "The file cannot be accessed by the system" error, any ideas why that would happen?
@paxos1977 Can you post a code snippet that demonstrates that error? Thanks!
A simple and neat solution - works great in Windows 10 (Version 10.0.19043.1415)
N
Noctis Skytower

The following example builds on MARTIN DE LA FUENTE SAAVEDRA's excellent work and accepted answer. In particular, two enumerations are introduced. 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 subprocess
import sys

# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx


# noinspection SpellCheckingInspection
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:
       # noinspection SpellCheckingInspection
        hinstance = ctypes.windll.shell32.ShellExecuteW(
            None,
            'runas',
            sys.executable,
            subprocess.list2cmdline(sys.argv),
            None,
            SW.SHOWNORMAL
        )
        if hinstance <= 32:
            raise RuntimeError(ERROR(hinstance))


def main():
    # Your Code Here
    print(input('Echo: '))


if __name__ == '__main__':
    bootstrap()

J
Joe

Recognizing this question was asked years ago, I think a more elegant solution is offered on github by frmdstryr using his module pywinutils:

Excerpt:

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    

This utilizes the COM interface and automatically indicates that admin privileges are needed with the familiar dialog prompt that you would see if you were copying into a directory where admin privileges are required and also provides the typical file progress dialog during the copy operation.


T
TinyGrasshopper

This may not completely answer your question but you could also try using the Elevate Command Powertoy in order to run the script with elevated UAC privileges.

http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx

I think if you use it it would look like 'elevate python yourscript.py'


o
officialhopsof

You can make a shortcut somewhere and as the target use: python yourscript.py then under properties and advanced select run as administrator.

When the user executes the shortcut it will ask them to elevate the application.


B
Berwyn

A variation on Jorenko's work above allows the elevated process to use the same console (but see my comment below):

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

Sorry. the same console option (SEE_MASK_NO_CONSOLE) only works if you're already elevated. My bad.
O
Orsiris de Jong

This is mostly an upgrade to Jorenko's answer, that allows to use parameters with spaces in Windows, but should also work fairly well on Linux :) Also, will work with cx_freeze or py2exe since we don't use __file__ but sys.argv[0] as executable

[EDIT] Disclaimer: The code in this post is outdated. I have published the elevation code as a python package. Install with pip install command_runner

Usage:

from command_runner.elevate import elevate

def main():
    """My main function that should be elevated"""
    print("Who's the administrator, now ?")

if __name__ == '__main__':
    elevate(main)

[/EDIT]

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:])

B
BaiJiFeiLong

For one-liners, put the code to where you need UAC.

Request UAC, if failed, keep running:

import ctypes, sys

ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
    None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and exit()


Request UAC, if failed, exit:

import ctypes, sys

ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
    None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, exit())

Function style:

# Created by BaiJiFeiLong@gmail.com at 2022/6/24
import ctypes
import sys


def request_uac_or_skip():
    ctypes.windll.shell32.IsUserAnAdmin() or ctypes.windll.shell32.ShellExecuteW(
        None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32 and sys.exit()


def request_uac_or_exit():
    ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(
        None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, sys.exit())


j
jfs

If your script always requires an Administrator's privileges then:

runas /user:Administrator "python your_script.py"

careful, elevation != running as administrator
I am new to python...can you tell me where will i put that code?
@RahatIslamKhan: Open a Command Prompt window and put it where: the command runs your_script.py as an Administrator user. Make sure you understand @Kugel's comment.