UAC-Erhöhung innerhalb eines Python-Skripts anfordern?

91

Ich möchte, dass mein Python-Skript Dateien unter Vista kopiert. Wenn ich es in einem normalen cmd.exeFenster ausführe , werden keine Fehler generiert, die Dateien werden jedoch NICHT kopiert. Wenn ich cmd.exe"als Administrator" ausführe und dann mein Skript ausführe, funktioniert es einwandfrei.

Dies ist sinnvoll, da die Benutzerkontensteuerung (User Account Control, UAC) normalerweise viele Dateisystemaktionen verhindert.

Gibt es eine Möglichkeit, wie ich innerhalb eines Python-Skripts eine UAC-Erhöhungsanforderung aufrufen kann (diese Dialoge, die so etwas wie "Diese und jene App benötigen Administratorzugriff, ist das in Ordnung?" Sagen)

Wenn dies nicht möglich ist, kann mein Skript zumindest feststellen, dass es nicht erhöht ist, sodass es ordnungsgemäß fehlschlagen kann?

jwfearn
quelle
3
stackoverflow.com/a/1445547/1628132 Nach dieser Antwort erstellen Sie eine .exe aus dem .py-Skript mit py2exe und einem Flag namens 'uac_info'. Es ist eine ziemlich nette Lösung
foxcoreg

Antworten:

96

Ab 2017 ist dies eine einfache Methode, um dies zu erreichen:

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)

Wenn Sie Python 2.x verwenden, sollten Sie die letzte Zeile ersetzen für:

ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1)

Beachten Sie, dass , wenn Sie Sie Python - Skript in eine ausführbare Datei umgewandelt (mit Tools wie py2exe, cx_freeze, pyinstaller) , dann sollten Sie sys.argv[1:]statt sys.argvim vierten Parameter.

Einige der Vorteile hier sind:

  • Keine externen Bibliotheken erforderlich. Es verwendet nur ctypesund sysvon Standard - Bibliothek.
  • Funktioniert sowohl mit Python 2 als auch mit Python 3.
  • Es ist nicht erforderlich, die Dateiressourcen zu ändern oder eine Manifestdatei zu erstellen.
  • Wenn Sie den folgenden Code nicht hinzufügen, wird der Code niemals zweimal ausgeführt.
  • Sie können den Rückgabewert des API-Aufrufs in der letzten Zeile abrufen und eine Aktion ausführen, wenn dies fehlschlägt (Code <= 32). Prüfen Sie mögliche Rückgabewerte hier .
  • Sie können die Anzeigemethode des erzeugten Prozesses ändern, indem Sie den sechsten Parameter ändern.

Die Dokumentation für den zugrunde liegenden ShellExecute-Aufruf finden Sie hier .

Martín De la Fuente
quelle
8
Ich musste Unicode-Instanzen als Parameter für ShellExecuteW verwenden (wie u'runas 'und unicode (sys.executable)), um dies zum Laufen zu bringen.
Janosch
6
@Janosch, das liegt daran, dass Sie Python 2.x verwenden, während sich mein Code in Python 3 befindet (wo alle Zeichenfolgen als Unicodes behandelt werden). Aber es ist gut zu erwähnen, danke!
Martín De la Fuente
2
@Martin Wenn ich diesen Code über die Windows-Befehlszeile wie folgt ausführe: "python yourcode.py", wird nur python.exe geöffnet. Gibt es eine Möglichkeit, das Problem zu beheben?
user2978216
1
@ user2978216 Ich hatte das gleiche Problem. In der Zeile wird ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1) sys.executablenur der Python-Interpreter aufgelöst (z. B. C:\Python27\Python.exe). Die Lösung besteht darin, das laufende Skript als Argument hinzuzufügen (Replacting ""). ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)Beachten Sie auch, für diese Arbeit in Python 2.x, alle String - Argumente müssen Unicode sein (dh u"runas", unicode(sys.executable)und unicode(__file__))
Javier Ubillos
2
@HrvojeT Beide ShellExecuteWund ShellExecuteAsind Aufrufe der ShellExecuteFunktion in der Windows-API. Ersteres verpflichtet die Zeichenfolgen im Unicode-Format und letzteres wird im ANSI-Format verwendet
Martín De la Fuente
69

Es hat eine Weile gedauert, bis die Antwort von dguaraglia funktioniert hat. Um anderen Zeit zu sparen, habe ich Folgendes getan, um diese Idee umzusetzen:

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)
Jorenko
quelle
1
Dies scheint nur zu erhöhen und dann zu beenden ... wenn ich einige Druckanweisungen eingebe, werden sie nicht ein zweites Mal ausgeführt
Joran Beasley
6
@JoranBeasley, Sie werden keine Ausgabe sehen. ShellExecuteEx sendet sein STDOUT nicht zurück an die ursprüngliche Shell. In dieser Hinsicht wird das Debuggen ... eine Herausforderung sein. Aber der Trick, Privilegien zu heben, funktioniert definitiv.
Tim Keating
1
@ TimKeating, ActiveState hat ein Rezept, das das Debuggen ein bisschen einfacher machen soll: Verwenden Sie das DebugView-Dienstprogramm mit Standard-Python-Protokollierung
samwyse
1
Es scheint unmöglich, die Ausgabe in derselben Konsole abzurufen, aber mit dem Argument nShow = 5 für ShellExecuteEx wird ein neues Befehlsfenster mit der Ausgabe des erhöhten Skripts geöffnet.
Emil Styrke
2
Für das Zitat können Sie subprocess.list2cmdlinees richtig machen.
Coderforlife
29

Es scheint, dass es für eine Weile keine Möglichkeit gibt, die Anwendungsberechtigungen zu erhöhen, damit Sie eine bestimmte Aufgabe ausführen können. Windows muss zu Beginn des Programms wissen, ob für die Anwendung bestimmte Berechtigungen erforderlich sind, und den Benutzer auffordern, zu bestätigen, wann die Anwendung Aufgaben ausführt, für die diese Berechtigungen erforderlich sind . Es gibt zwei Möglichkeiten, dies zu tun:

  1. Schreiben Sie eine Manifestdatei, die Windows mitteilt, dass für die Anwendung möglicherweise einige Berechtigungen erforderlich sind
  2. Führen Sie die Anwendung mit erhöhten Berechtigungen in einem anderen Programm aus

Diese beiden Artikel erklären viel detaillierter, wie dies funktioniert.

Wenn Sie keinen bösen ctypes-Wrapper für die CreateElevatedProcess-API schreiben möchten, verwenden Sie den im Code Project-Artikel erläuterten ShellExecuteEx-Trick (Pywin32 wird mit einem Wrapper für ShellExecute geliefert). Wie? Etwas wie das:

Wenn Ihr Programm gestartet wird, prüft es, ob es über Administratorrechte verfügt. Wenn es dies nicht tut, führt es sich selbst mit dem ShellExecute-Trick aus und wird sofort beendet. Wenn dies der Fall ist, führt es die vorliegende Aufgabe aus.

Wenn Sie Ihr Programm als "Skript" beschreiben, reicht das wahrscheinlich für Ihre Anforderungen aus.

Prost.

Dguaraglia
quelle
Vielen Dank für diese Links, sie waren sehr nützlich für mich, um viel über UAC-Sachen herauszufinden.
Colen
4
Vielleicht möchten Sie darauf hinweisen, dass Sie ShellExecute ohne PyWin32 (ich hatte Probleme bei der Installation) mit os.startfile ($ EXECUTABLE, "runas") ausführen können.
Mike McQuaid
@Mike - runasbringt aber eine neue Eingabeaufforderung. Und Startdatei akzeptiert keine Befehlszeilenargumente für$EXECUTABLE.
Sridhar Ratnakumar
Ich habe eine weitere Antwort mit einer vollständigen Implementierung dieser Technik hinzugefügt, die zum Start eines Python-Skripts hinzugefügt werden kann.
Jorenko
Der Artikel zum zweiten Link lautete "Das geringste Privileg: Bringen Sie Ihren Apps bei, mit der Windows Vista-Benutzerkontensteuerung gut zu spielen" im "MSDN Magazine January 2007". Diese Ausgabe ist jetzt nur noch als .chmDatei verfügbar .
Peter
5

Fügen Sie einfach diese Antwort hinzu, falls andere wie ich von der Google-Suche hierher geleitet werden. Ich habe das elevateModul in meinem Python-Skript verwendet und das Skript mit Administratorrechten in Windows 10 ausgeführt.

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

Irving Moy
quelle
Hey, ich habe versucht, das elevateModul zu verwenden, und es wird der Fehler "Auf die Datei kann vom System nicht zugegriffen werden" angezeigt. Gibt es Ideen, warum dies passieren würde?
paxos1977
@ paxos1977 Können Sie ein Code-Snippet veröffentlichen, das diesen Fehler demonstriert? Vielen Dank!
Irving Moy
4

Das folgende Beispiel baut auf der hervorragenden Arbeit und der akzeptierten Antwort von MARTIN DE LA FUENTE SAAVEDRA auf. Insbesondere werden zwei Aufzählungen eingeführt. Das erste ermöglicht eine einfache Angabe, wie ein Programm mit erhöhten Rechten geöffnet werden soll, und das zweite hilft, wenn Fehler leicht identifiziert werden müssen. Bitte beachten Sie, dass, wenn alle Befehlszeilenargumente an den neuen Prozess übergeben werden sys.argv[0]sollen, diese wahrscheinlich durch einen Funktionsaufruf ersetzt werden sollten : 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()
Noctis Skytower
quelle
4

Angesichts der Tatsache, dass diese Frage vor Jahren gestellt wurde, denke ich, dass auf Github eine elegantere Lösung angeboten wird frmdstryr mit seinem Modul pywinutils anbietet:

Auszug:

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    

Dies verwendet die COM-Schnittstelle und zeigt automatisch an, dass Administratorrechte erforderlich sind, und zwar mit der bekannten Eingabeaufforderung, die Sie sehen würden, wenn Sie in ein Verzeichnis kopieren würden, in dem Administratorrechte erforderlich sind, und bietet auch den typischen Dialog zum Dateifortschritt während des Kopiervorgangs.

KenV99
quelle
2

Dies beantwortet Ihre Frage möglicherweise nicht vollständig, aber Sie können auch versuchen, das Elevate Command Powertoy zu verwenden, um das Skript mit erhöhten UAC-Berechtigungen auszuführen.

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

Ich denke, wenn Sie es verwenden, würde es so aussehen, als ob Sie "Python yourcript.py erhöhen".

TinyGrasshopper
quelle
2

Sie können irgendwo eine Verknüpfung erstellen und als Ziel verwenden: python yourscript.py, dann unter Eigenschaften und erweiterte Auswahl als Administrator ausführen.

Wenn der Benutzer die Verknüpfung ausführt, wird er aufgefordert, die Anwendung zu erhöhen.

offizielle Hopfen von
quelle
1

Wenn für Ihr Skript immer Administratorrechte erforderlich sind, gehen Sie wie folgt vor:

runas /user:Administrator "python your_script.py"
jfs
quelle
14
Vorsicht, Höhe! = läuft als Administrator
Kugel
Ich bin neu in Python ... können Sie mir sagen, wo ich diesen Code ablegen soll?
Rahat Islam Khan
@RahatIslamKhan: Öffnen Sie ein Eingabeaufforderungsfenster und platzieren Sie es dort, wo: Der Befehl wird your_script.pyals Administrator ausgeführt. Stellen Sie sicher, dass Sie den Kommentar von @ Kugel verstehen .
JFS
1

Eine Variation von Jorenkos Arbeit oben ermöglicht es dem erhöhten Prozess, dieselbe Konsole zu verwenden (siehe jedoch meinen Kommentar unten):

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
Berwyn
quelle
Es tut uns leid. Die gleiche Konsolenoption (SEE_MASK_NO_CONSOLE) funktioniert nur, wenn Sie bereits erhöht sind. Mein Fehler.
Berwyn
1

Dies ist meistens ein Upgrade von Jorenkos Antwort, das die Verwendung von Parametern mit Leerzeichen in Windows ermöglicht, aber auch unter Linux ziemlich gut funktionieren sollte :) Funktioniert auch mit cx_freeze oder py2exe, da wir es nicht verwenden, __file__sondern sys.argv[0]als ausführbare Datei

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:])
Orsiris de Jong
quelle
1

Ich verwende die Elevate-Bibliothek in meinem Python für Administratorrechte, aber wenn ich eine Exe-Datei daraus mache, ist die generierte Exe-Größe sehr groß

Muhammad Mohtasham
quelle
1
Können Sie bitte genauer auf die Frage antworten? Dies ist keine Antwort auf die Frage "Wie kann eine UAC-Erhöhung angefordert werden?". Bitte bearbeiten Sie, um die Antwort so spezifisch und hilfreich wie möglich zu gestalten. Sie können auch ein Beispiel für die Verwendung von Elevate geben.
Chris