Minta elevasi UAC dari dalam skrip Python?

94

Saya ingin skrip Python saya menyalin file di Vista. Ketika saya menjalankannya dari cmd.exejendela normal , tidak ada kesalahan yang dihasilkan, namun file TIDAK disalin. Jika saya menjalankan cmd.exe"sebagai administrator" dan kemudian menjalankan skrip saya, itu berfungsi dengan baik.

Ini masuk akal karena Kontrol Akun Pengguna (UAC) biasanya mencegah banyak tindakan sistem file.

Adakah cara saya bisa, dari dalam skrip Python, memanggil permintaan elevasi UAC (dialog yang mengatakan sesuatu seperti "aplikasi ini dan itu memerlukan akses admin, apakah ini OK?")

Jika itu tidak memungkinkan, apakah ada cara agar skrip saya setidaknya dapat mendeteksi bahwa itu tidak dinaikkan sehingga dapat gagal dengan baik?

jwfearn
sumber
3
stackoverflow.com/a/1445547/1628132 mengikuti jawaban ini Anda membuat .exe dari skrip .py menggunakan py2exe dan menggunakan bendera yang disebut 'uac_info' itu solusi yang cukup rapi
foxcoreg

Jawaban:

100

Pada 2017, metode mudah untuk mencapainya adalah sebagai berikut:

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)

Jika Anda menggunakan Python 2.x, maka Anda harus mengganti baris terakhir untuk:

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

Juga catatan bahwa jika Anda dikonversi Anda skrip python menjadi sebuah file executable (menggunakan alat-alat seperti py2exe, cx_freeze, pyinstaller) maka Anda harus menggunakan sys.argv[1:]bukannya sys.argvdi parameter keempat.

Beberapa keunggulannya di sini adalah:

  • Tidak ada perpustakaan eksternal yang diperlukan. Itu hanya menggunakan ctypesdan sysdari perpustakaan standar.
  • Bekerja pada Python 2 dan Python 3.
  • Tidak perlu mengubah sumber file atau membuat file manifes.
  • Jika Anda tidak menambahkan kode di bawah pernyataan if / else, kode tidak akan pernah dijalankan dua kali.
  • Anda bisa mendapatkan nilai kembali dari panggilan API di baris terakhir dan mengambil tindakan jika gagal (kode <= 32). Periksa kemungkinan nilai kembali di sini .
  • Anda dapat mengubah metode tampilan dari proses pemijahan dengan memodifikasi parameter keenam.

Dokumentasi untuk panggilan ShellExecute yang mendasari ada di sini .

Martín De la Fuente
sumber
9
Saya harus menggunakan instance unicode sebagai parameter untuk ShellExecuteW (seperti u'runas 'dan unicode (sys.executable)) untuk menjalankannya.
Janosch
6
@Janosch, itu karena Anda menggunakan Python 2.x, sedangkan kode saya menggunakan Python 3 (di mana semua string diperlakukan sebagai unicodes). Tapi bagus untuk disebutkan, terima kasih!
Martín De la Fuente
2
@Martin jika saya menjalankan kode ini dari baris perintah Windows seperti ini: "python yourcode.py" itu hanya membuka python.exe. Apakah ada cara untuk memperbaikinya?
pengguna2978216
1
@ user2978216 Saya mengalami masalah yang sama. Pada baris tersebut ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1) sys.executablememutuskan hanya interpreter python (misalnya C:\Python27\Python.exe) Solusinya adalah menambahkan skrip yang sedang berjalan sebagai argumen (replacting ""). ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)Perhatikan juga, agar ini berfungsi di python 2.x, semua argumen string harus unicode (yaitu u"runas", unicode(sys.executable)dan unicode(__file__))
Javier Ubillos
2
@HrvojeT Keduanya, ShellExecuteWdan ShellExecuteAmerupakan panggilan ke ShellExecutefungsi di Windows API. Yang pertama mewajibkan string dalam format unicode dan yang terakhir digunakan dengan format ANSI
Martín De la Fuente
71

Saya butuh sedikit waktu untuk mendapatkan jawaban dguaraglia berfungsi, jadi untuk menghemat waktu orang lain, inilah yang saya lakukan untuk menerapkan gagasan ini:

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
sumber
1
ini sepertinya meningkat dan kemudian keluar ... jika saya memasukkan beberapa pernyataan cetak, mereka tidak dieksekusi untuk kedua kalinya
Joran Beasley
6
@ JoranBeasley, Anda tidak akan melihat output apa pun. ShellExecuteEx tidak memposting STDOUT-nya kembali ke shell asal. Dalam hal ini, debugging akan ... menantang. Tapi trik mengangkat hak istimewa pasti berhasil.
Tim Keating
1
@TimKeating, ActiveState memiliki resep yang seharusnya membuat debugging sedikit lebih mudah: Gunakan utilitas DebugView dengan logging python standar
samwyse
1
tampaknya tidak mungkin untuk mendapatkan keluaran di konsol yang sama, tetapi dengan argumen nShow = 5 ke ShellExecuteEx, jendela perintah baru akan terbuka dengan keluaran dari skrip yang ditinggikan.
Emil Styrke
2
Untuk mengutip, Anda dapat menggunakannya subprocess.list2cmdlinedengan benar.
coderforlife
29

Tampaknya tidak ada cara untuk meningkatkan hak istimewa aplikasi untuk sementara waktu bagi Anda untuk melakukan tugas tertentu. Windows perlu mengetahui di awal program apakah aplikasi memerlukan hak istimewa tertentu, dan akan meminta pengguna untuk mengonfirmasi saat aplikasi melakukan tugas apa pun yang memerlukan hak istimewa tersebut. Ada dua cara untuk melakukan ini:

  1. Tulis file manifes yang memberi tahu Windows bahwa aplikasi mungkin memerlukan beberapa hak istimewa
  2. Jalankan aplikasi dengan hak istimewa dari dalam program lain

Ini dua artikel menjelaskan lebih detail cara kerja ini.

Apa yang akan saya lakukan, jika Anda tidak ingin menulis pembungkus ctypes yang buruk untuk API CreateElevatedProcess, adalah menggunakan trik ShellExecuteEx yang dijelaskan dalam artikel Proyek Kode (Pywin32 dilengkapi dengan pembungkus untuk ShellExecute). Bagaimana? Sesuatu seperti ini:

Ketika program Anda mulai, ia memeriksa apakah ia memiliki hak istimewa Administrator, jika tidak, ia berjalan sendiri menggunakan trik ShellExecute dan segera keluar, jika ya, ia melakukan tugas yang ada.

Saat Anda menggambarkan program Anda sebagai "skrip", saya rasa itu sudah cukup untuk kebutuhan Anda.

Bersulang.

dguaraglia.dll
sumber
Terima kasih untuk tautan-tautan itu, mereka sangat berguna bagi saya untuk mengetahui banyak hal tentang UAC.
Colen
4
Sesuatu yang mungkin ingin Anda catat tentang hal ini adalah Anda dapat melakukan ShellExecute tanpa PyWin32 (saya mengalami masalah saat menginstalnya) dengan menggunakan os.startfile ($ EXECUTABLE, "runas").
Mike McQuaid
@ Mike - tetapi runasmenampilkan prompt baru. Dan startfile tidak menerima argumen baris perintah ke$EXECUTABLE.
Sridhar Ratnakumar
Saya menambahkan jawaban lain dengan implementasi penuh dari teknik ini yang seharusnya dapat ditambahkan ke awal skrip python apa pun.
Jorenko
Artikel ke tautan kedua adalah "Hak Istimewa Terkecil: Ajarkan Aplikasi Anda Untuk Bermain Dengan Baik Dengan Kontrol Akun Pengguna Windows Vista" di "Majalah MSDN Januari 2007", tetapi masalah ini sekarang hanya tersedia sebagai .chmfile.
Peter
6

Hanya menambahkan jawaban ini seandainya orang lain diarahkan ke sini oleh Google Penelusuran seperti saya. Saya menggunakan elevatemodul dalam skrip Python saya dan skrip dijalankan dengan Hak Istimewa Administrator di Windows 10.

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

Irving Moy
sumber
Hei, saya mencoba menggunakan elevatemodul dan saya mendapatkan kesalahan "File tidak dapat diakses oleh sistem", ada ide mengapa hal itu terjadi?
paxos1977
@ paxos1977 Dapatkan Anda mengirim potongan kode yang menunjukkan kesalahan itu? Terima kasih!
Irving Moy
5

Contoh berikut dibuat berdasarkan hasil kerja MARTIN DE LA FUENTE SAAVEDRA yang sangat baik dan jawaban yang diterima. Secara khusus, dua pencacahan diperkenalkan. Yang pertama memungkinkan spesifikasi yang mudah tentang bagaimana program yang ditinggikan akan dibuka, dan yang kedua membantu saat kesalahan perlu diidentifikasi dengan mudah. Perlu diketahui bahwa jika Anda ingin semua argumen baris perintah dilewatkan ke proses baru, sys.argv[0]mungkin harus diganti dengan panggilan fungsi: 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
sumber
4

Menyadari pertanyaan ini telah ditanyakan bertahun-tahun yang lalu, saya pikir solusi yang lebih elegan ditawarkan di github oleh frmdstryr menggunakan modul pywinutils:

Kutipan:

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    

Ini menggunakan antarmuka COM dan secara otomatis menunjukkan bahwa hak istimewa admin diperlukan dengan prompt dialog yang sudah dikenal yang akan Anda lihat jika Anda menyalin ke direktori di mana hak istimewa admin diperlukan dan juga menyediakan dialog kemajuan file yang khas selama operasi penyalinan.

KenV99
sumber
2

Ini mungkin tidak sepenuhnya menjawab pertanyaan Anda, tetapi Anda juga dapat mencoba menggunakan Powertoy Perintah Elevate untuk menjalankan skrip dengan hak istimewa UAC yang ditinggikan.

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

Saya pikir jika Anda menggunakannya akan terlihat seperti 'elevate python yourscript.py'

TinyGrasshopper
sumber
2

Anda dapat membuat pintasan di suatu tempat dan sebagai target menggunakan: python yourscript.py lalu di bawah properti dan pilih lanjutan jalankan sebagai administrator.

Saat pengguna menjalankan pintasan, ia akan meminta mereka untuk meninggikan aplikasi.

toko resmi
sumber
1

Jika skrip Anda selalu membutuhkan hak istimewa Administrator, maka:

runas /user:Administrator "python your_script.py"
jfs
sumber
15
hati-hati, elevasi! = berjalan sebagai administrator
Kugel
Saya baru mengenal python ... dapatkah Anda memberi tahu saya di mana saya akan meletakkan kode itu?
Rahat Islam Khan
@RahatIslamKhan: Buka jendela Command Prompt dan letakkan di mana: perintah berjalan your_script.pysebagai pengguna Administrator. Pastikan Anda memahami komentar @ Kugel .
jfs
1

Variasi pada pekerjaan Jorenko di atas memungkinkan proses yang ditinggikan untuk menggunakan konsol yang sama (tetapi lihat komentar saya di bawah):

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
sumber
Maaf. opsi konsol yang sama (SEE_MASK_NO_CONSOLE) hanya berfungsi jika Anda sudah diangkat. Salahku.
Berwyn
1

Ini sebagian besar merupakan peningkatan ke jawaban Jorenko, yang memungkinkan untuk menggunakan parameter dengan spasi di Windows, tetapi juga harus bekerja cukup baik di Linux :) Selain itu, akan berfungsi dengan cx_freeze atau py2exe karena kami tidak menggunakan __file__tetapi sys.argv[0]sebagai dapat dieksekusi

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
sumber