Bir Python betiği içinden UAC yükseltmesi talep ediyor musunuz?


97

Python komut dizimin Vista'da dosya kopyalamasını istiyorum. Normal bir cmd.exepencereden çalıştırdığımda hiçbir hata oluşturulmuyor, ancak dosyalar kopyalanmıyor. Koşarsam cmd.exe"Administator olarak" ve sonra benim komut dosyasını çalıştırın, bu cezayı çalışır.

Kullanıcı Hesabı Denetimi (UAC) normalde birçok dosya sistemi eylemini engellediği için bu mantıklıdır.

Bir Python komut dosyası içinden bir UAC yükseltme isteği çağırabilmemin bir yolu var mı ("şu ve bu tür uygulamanın yönetici erişimine ihtiyacı var, bu uygun mu?" Gibi bir şey söyleyen iletişim kutuları)

Bu mümkün değilse, betiğimin en azından yükseltilmediğini tespit edebilmesi ve böylece sorunsuz bir şekilde başarısız olabilmesi için bir yol var mı?


3
stackoverflow.com/a/1445547/1628132 bu yanıtı takiben py2exe ve 'uac_info' adı verilen bir bayrak kullanarak .py betiğinden bir .exe oluşturursunuz bu oldukça temiz bir çözüm
foxcoreg

Yanıtlar:


104

2017 itibariyle, bunu başarmanın kolay bir yöntemi şudur:

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)

Python 2.x kullanıyorsanız, son satırı şunun için değiştirmelisiniz:

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

Ayrıca bir yürütülebilir dosya içine size piton senaryoyu dönüştürülen eğer notu (gibi araçlar kullanılarak py2exe, cx_freeze, pyinstaller) ardından kullanmak gerektiğini sys.argv[1:]yerine sys.argvdördüncü parametresinde.

Buradaki avantajlardan bazıları:

  • Harici kitaplık gerekmez. Sadece kullanımları ctypesve sysstandart kütüphaneden.
  • Hem Python 2 hem de Python 3 üzerinde çalışır.
  • Dosya kaynaklarını değiştirmeye veya bir bildirim dosyası oluşturmaya gerek yoktur.
  • İf / else ifadesinin altına kod eklemezseniz, kod hiçbir zaman iki kez çalıştırılmaz.
  • API çağrısının dönüş değerini son satırda alabilir ve başarısız olursa işlem yapabilirsiniz (kod <= 32). Olası dönüş değerlerini buradan kontrol edin .
  • Altıncı parametreyi değiştirerek ortaya çıkan işlemin görüntüleme yöntemini değiştirebilirsiniz.

Temeldeki ShellExecute çağrısı için belgeler burada .


9
Bunu çalıştırmak için Unicode örneklerini ShellExecuteW için parametre olarak (u'runas 've unicode (sys.executable) gibi) kullanmak zorunda kaldım.
Janosch

6
@Janosch, çünkü benim kodum Python 3'teyken (tüm dizeler unicodes olarak değerlendirilir) Python 2.x kullanıyorsunuz. Ama bahsetmek güzel, teşekkürler!
Martín De la Fuente

2
@Martin bu kodu Windows komut satırından şu şekilde çalıştırıyorsam: "python yourcode.py" sadece python.exe'yi açar. Düzeltmenin bir yolu var mı?
user2978216

1
@ user2978216 Aynı sorunu yaşadım. Satırda ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1) sys.executablesadece python yorumlayıcısına çözümlenir (örn. C:\Python27\Python.exe) Çözüm, çalışan betiği bir argüman olarak eklemektir (replacting ""). ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)Ayrıca, bunun python u"runas"unicode(sys.executable)unicode(__file__)
2.x'te

2
@HrvojeT Both ShellExecuteWve Windows API'deki işleve ShellExecuteAyapılan çağrılardır ShellExecute. İlki, dizelerin unicode biçiminde olmasını zorunlu kılar ve ikincisi ANSI biçiminde kullanılır
Martín De la Fuente

71

Dguaraglia'nın cevabının işe yaraması biraz zaman aldı, bu yüzden başkalarına zaman kazandırmak adına, işte bu fikri uygulamak için yaptım:

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)

1
bu sadece yükseliyor ve sonra çıkıyor ... bazı basılı ifadeler koyarsam, ikinci kez idam
edilmiyorlar

6
@JoranBeasley, herhangi bir çıktı görmeyeceksiniz. ShellExecuteEx, STDOUT'unu başlangıçtaki kabuğa geri göndermez. Bu bakımdan, hata ayıklama ... zor olacak. Ancak ayrıcalık kaldırma hilesi kesinlikle işe yarıyor.
Tim Keating

1
@TimKeating, ActiveState'in hata ayıklamayı biraz daha kolaylaştıracak bir tarifi var: Standart python günlüğü ile DebugView yardımcı programını kullanın
samwyse

1
çıktıyı aynı konsolda almak imkansız görünüyor, ancak ShellExecuteEx'e nShow = 5 bağımsız değişkeni ile, yükseltilmiş komut dosyasının çıktısıyla yeni bir komut penceresi açılacaktır.
Emil Styrke

2
Alıntı subprocess.list2cmdlineyapmak için, bunu doğru şekilde yapmak için kullanabilirsiniz .
coderforlife

29

Belirli bir görevi gerçekleştirmeniz için uygulama ayrıcalıklarını bir süreliğine yükseltmenin bir yolu yok gibi görünüyor. Windows'un, programın başlangıcında uygulamanın belirli ayrıcalıklar gerektirip gerektirmediğini bilmesi gerekir ve kullanıcıdan, uygulamanın bu ayrıcalıklara ihtiyaç duyan herhangi bir görevi ne zaman gerçekleştirdiğini onaylamasını ister . Bunu yapmanın iki yolu vardır:

  1. Windows'a uygulamanın bazı ayrıcalıklar gerektirebileceğini söyleyen bir bildirim dosyası yazın
  2. Uygulamayı başka bir programın içinden yükseltilmiş ayrıcalıklarla çalıştırın

Bu iki makale, bunun nasıl çalıştığını çok daha ayrıntılı olarak açıklıyor.

CreateElevatedProcess API'si için kötü bir ctype sarmalayıcısı yazmak istemiyorsanız yapacağım şey, Kod Projesi makalesinde açıklanan ShellExecuteEx hilesini kullanmaktır (Pywin32, ShellExecute için bir sarmalayıcı ile birlikte gelir). Nasıl? Bunun gibi bir şey:

Programınız başladığında, Yönetici ayrıcalıklarına sahip olup olmadığını kontrol eder, yoksa ShellExecute hilesini kullanarak kendi kendini çalıştırır ve hemen çıkar, varsa elindeki görevi gerçekleştirir.

Programınızı bir "script" olarak tanımlarken, sanırım bu sizin ihtiyaçlarınız için yeterli.

Şerefe.


Bu bağlantılar için teşekkürler, UAC hakkında çok şey öğrenmem için çok faydalı oldular.
Colen

4
Bununla ilgili not almak isteyebileceğiniz bir şey, os.startfile ($ EXECUTABLE, "runas") kullanarak PyWin32 olmadan ShellExecute yapabileceğinizdir (onu kurarken sorun yaşadım).
Mike McQuaid

@Mike - ama runasyine de yeni bir uyarı getiriyor. Ve startfile komut satırı argümanlarını kabul etmiyor$EXECUTABLE.
Sridhar Ratnakumar

Herhangi bir python betiğinin başlangıcına eklenebilecek bu tekniğin tam uygulamasıyla başka bir yanıt ekledim.
Jorenko

İkinci bağlantıdaki makale, "MSDN Magazine Ocak 2007" deki "En Az Ayrıcalık: Uygulamalarınızı Windows Vista Kullanıcı Hesabı Denetimi ile Güzelce Çalmayı Öğretin" idi, ancak bu sayı artık yalnızca .chmdosya olarak mevcuttur .
Peter

6

Başkalarının da benim gibi Google Arama tarafından buraya yönlendirilmesi durumunda bu yanıtı ekledim. Kullandığım elevatebenim Python komut modülü ve komut, Windows 10 Yönetici Ayrıcalıklarla gerçekleştirdik.

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


Hey, elevatemodülü kullanmayı denedim ve "Dosyaya sistem tarafından erişilemiyor" hatası alıyorum, bunun neden olacağı hakkında bir fikriniz var mı?
paxos1977

@ paxos1977 Bu hatayı gösteren bir kod parçacığı gönderebilir misiniz? Teşekkürler!
Irving Moy

5

Aşağıdaki örnek, MARTIN DE LA FUENTE SAAVEDRA'nın mükemmel çalışmasına ve kabul edilen cevabına dayanmaktadır . Özellikle, iki numaralandırma tanıtıldı. Birincisi, yükseltilmiş bir programın nasıl açılacağına dair kolay spesifikasyona izin verir ve ikincisi, hataların kolayca tanımlanması gerektiğinde yardımcı olur. Yeni sürecin geçirilen tüm komut satırı argümanları istiyorsanız, lütfen unutmayın sys.argv[0]muhtemelen bir işlev çağrısı ile değiştirilmelidir: 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()

4

Sorulan bu soruyu edildi tanıma yıl önce, daha zarif bir çözüm sunulmaktadır düşünüyorum github onun modül pywinutils kullanarak frmdstryr tarafından:

Alıntı:

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    

Bu, COM arayüzünü kullanır ve yönetici ayrıcalıklarının gerekli olduğu bir dizine kopyaladığınızda görebileceğiniz tanıdık bir iletişim kutusu istemiyle yönetici ayrıcalıklarının gerekli olduğunu otomatik olarak belirtir ve ayrıca kopyalama işlemi sırasında tipik dosya ilerleme iletişim kutusunu sağlar.



2

Bir yerde ve hedef kullanım olarak bir kısayol oluşturabilirsiniz: python yourscript.py, ardından özellikler altında ve gelişmiş yönetici olarak çalıştır seçeneğini seçin.

Kullanıcı kısayolu çalıştırdığında, ondan uygulamayı yükseltmesini isteyecektir.


1

Komut dosyanız her zaman bir Yönetici ayrıcalıklarına ihtiyaç duyuyorsa:

runas /user:Administrator "python your_script.py"

15
dikkatli, yükselme! = yönetici olarak çalışıyor
Kugel

Python'da yeniyim ... bu kodu nereye koyacağımı söyler misin?
Rahat Islam Khan

@RahatIslamKhan: Bir Komut İstemi penceresi açın ve nereye koyun: komut your_script.pybir Yönetici kullanıcı olarak çalışır . @ Kugel'in yorumunu anladığınızdan emin olun .
jfs

1

Jorenko'nun yukarıdaki çalışmasının bir varyasyonu, yükseltilmiş işlemin aynı konsolu kullanmasına izin verir (ancak aşağıdaki yorumuma bakın):

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

Afedersiniz. aynı konsol seçeneği (SEE_MASK_NO_CONSOLE) yalnızca zaten yükseltilmişseniz çalışır. Benim hatam.
Berwyn

1

Bu çoğunlukla Windows boşluklu parametreleri kullanmanızı sağlar Jorenko cevabı, bir yükseltme değil, aynı zamanda biz kullanmayın çünkü cx_freeze veya py2exe ile çalışacaktır, ayrıca :) Linux üzerinde oldukça iyi çalışması gerekir __file__ama sys.argv[0]çalıştırılabilir olarak

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:])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.