2013-10-30 533 views
37

我在寫一個需要執行管理任務的pyqt應用程序。我寧願用提升特權啓動我的腳本。我知道這個問題在SO或其他論壇中被多次提及。但人們建議的解決方案是看看這個SO問題 Request UAC elevation from within a Python script?如何在Windows上使用提升權限運行python腳本

但是,我無法執行鏈接中給出的示例代碼。我已經把這段代碼放在主文件的頂部,並試圖執行它。

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) 
print "I am root now." 

它實際上要求允許提升但打印行從未執行。有人可以幫助我成功運行上述代碼。提前致謝。

+1

刪除'SYS一個理想的選擇 刪除的Unicode。退出(0)'並將'print'放入'if'塊中 –

+0

謝謝。這工作。如果您可以發佈爲答案,我會接受答案。 –

+0

我的第一條評論有誤。打印語句的位置在放入if塊之後是正確的,當腳本由asadmin命令運行時,它不會被執行。 –

回答

57

謝謝大家的回覆。我的腳本使用了Preston Landers在2010年編寫的模塊/腳本。在瀏覽互聯網兩天後,我可以找到腳本,因爲它深深隱藏在pywin32郵件列表中。通過這個腳本,更容易檢查用戶是否是管理員,如果不是則請求UAC /管理員權限。它確實在單獨的窗口中提供輸出以查找代碼正在執行的操作。關於如何使用代碼的示例也包含在腳本中。爲了所有人在Windows上尋找UAC的好處,請查看此代碼。我希望它能幫助尋找相同解決方案的人。它可用於這樣的事情從你的主腳本: -

import admin 
if not admin.isUserAdmin(): 
     admin.runAsAdmin() 

實際的代碼是: -

#!/usr/bin/env python 
# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- 
# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4 

# (C) COPYRIGHT © Preston Landers 2010 
# Released under the same license as Python 2.6.5 


import sys, os, traceback, types 

def isUserAdmin(): 

    if os.name == 'nt': 
     import ctypes 
     # WARNING: requires Windows XP SP2 or higher! 
     try: 
      return ctypes.windll.shell32.IsUserAnAdmin() 
     except: 
      traceback.print_exc() 
      print "Admin check failed, assuming not an admin." 
      return False 
    elif os.name == 'posix': 
     # Check for root on Posix 
     return os.getuid() == 0 
    else: 
     raise RuntimeError, "Unsupported operating system for this module: %s" % (os.name,) 

def runAsAdmin(cmdLine=None, wait=True): 

    if os.name != 'nt': 
     raise RuntimeError, "This function is only implemented on Windows." 

    import win32api, win32con, win32event, win32process 
    from win32com.shell.shell import ShellExecuteEx 
    from win32com.shell import shellcon 

    python_exe = sys.executable 

    if cmdLine is None: 
     cmdLine = [python_exe] + sys.argv 
    elif type(cmdLine) not in (types.TupleType,types.ListType): 
     raise ValueError, "cmdLine is not a sequence." 
    cmd = '"%s"' % (cmdLine[0],) 
    # XXX TODO: isn't there a function or something we can call to massage command line params? 
    params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]]) 
    cmdDir = '' 
    showCmd = win32con.SW_SHOWNORMAL 
    #showCmd = win32con.SW_HIDE 
    lpVerb = 'runas' # causes UAC elevation prompt. 

    # print "Running", cmd, params 

    # ShellExecute() doesn't seem to allow us to fetch the PID or handle 
    # of the process, so we can't get anything useful from it. Therefore 
    # the more complex ShellExecuteEx() must be used. 

    # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd) 

    procInfo = ShellExecuteEx(nShow=showCmd, 
           fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, 
           lpVerb=lpVerb, 
           lpFile=cmd, 
           lpParameters=params) 

    if wait: 
     procHandle = procInfo['hProcess']  
     obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE) 
     rc = win32process.GetExitCodeProcess(procHandle) 
     #print "Process handle %s returned code %s" % (procHandle, rc) 
    else: 
     rc = None 

    return rc 

def test(): 
    rc = 0 
    if not isUserAdmin(): 
     print "You're not an admin.", os.getpid(), "params: ", sys.argv 
     #rc = runAsAdmin(["c:\\Windows\\notepad.exe"]) 
     rc = runAsAdmin() 
    else: 
     print "You are an admin!", os.getpid(), "params: ", sys.argv 
     rc = 0 
    x = raw_input('Press Enter to exit.') 
    return rc 


if __name__ == "__main__": 
    sys.exit(test()) 
+0

非常感謝你的回答。我有一個問題,即我的Qt GUI在用shellexecuteEX執行時沒有完全顯示出來,你使用showcommand的廣泛命令幫助我使它工作。謝謝! :) – Ecno92

+0

謝謝,必須添加nShow和fMask參數才能與Qt gui配合使用。 – frmdstryr

+0

非常好,我正試圖弄清楚如何爲我的應用程序編寫安裝程序。 –

6

answer you took the code from發表評論有人說ShellExecuteEx不會將其STDOUT發佈回原始shell。所以即使代碼可能正常工作,您也不會看到「我現在是root」。

而不是打印的東西,嘗試寫入文件:

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) 
with open("somefilename.txt", "w") as out: 
    print >> out, "i am root" 

,然後看看在文件中。

+0

但它永遠不會彈出我的pyqt窗口。使用ZetCode http://zetcode.com/gui/pyqt4/firstprograms/ –

+4

測試代碼,這是一種奇怪的方式來表示感謝,但不用客氣。 –

+0

但這提示權限。有沒有什麼辦法可以跳過這個 – deenbandhu

5

下面是一個標準輸出重定向一個解決方案:

def elevate(): 
    import ctypes, win32com.shell.shell, win32event, win32process 
    outpath = r'%s\%s.out' % (os.environ["TEMP"], os.path.basename(__file__)) 
    if ctypes.windll.shell32.IsUserAnAdmin(): 
     if os.path.isfile(outpath): 
      sys.stderr = sys.stdout = open(outpath, 'w', 0) 
     return 
    with open(outpath, 'w+', 0) as outfile: 
     hProc = win32com.shell.shell.ShellExecuteEx(lpFile=sys.executable, \ 
      lpVerb='runas', lpParameters=' '.join(sys.argv), fMask=64, nShow=0)['hProcess'] 
     while True: 
      hr = win32event.WaitForSingleObject(hProc, 40) 
      while True: 
       line = outfile.readline() 
       if not line: break 
       sys.stdout.write(line) 
      if hr != 0x102: break 
    os.remove(outpath) 
    sys.stderr = '' 
    sys.exit(win32process.GetExitCodeProcess(hProc)) 

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

我找到了解決這個問題的非常簡單的方法。

  1. 在快捷方式的屬性面板中創建python.exe
  2. 更改快捷方式目標弄成C:\xxx\...\python.exe your_script.py
  3. 點擊快捷鍵「前進......」,然後點擊選項「以管理員身份運行」

我不確定這些選項的法術是否正確,因爲我使用的是中文版的Windows。

1

這是一個只需要ctypes模塊的解決方案。支持pyinstaller包裝程序。

#!python 
# coding: utf-8 
import sys 
import ctypes 

def run_as_admin(argv=None, debug=False): 
    shell32 = ctypes.windll.shell32 
    if argv is None and shell32.IsUserAnAdmin(): 
     return True 

    if argv is None: 
     argv = sys.argv 
    if hasattr(sys, '_MEIPASS'): 
     # Support pyinstaller wrapped program. 
     arguments = map(unicode, argv[1:]) 
    else: 
     arguments = map(unicode, argv) 
    argument_line = u' '.join(arguments) 
    executable = unicode(sys.executable) 
    if debug: 
     print 'Command line: ', executable, argument_line 
    ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1) 
    if int(ret) <= 32: 
     return False 
    return None 


if __name__ == '__main__': 
    ret = run_as_admin() 
    if ret is True: 
     print 'I have admin privilege.' 
     raw_input('Press ENTER to exit.') 
    elif ret is None: 
     print 'I am elevating to admin privilege.' 
     raw_input('Press ENTER to exit.') 
    else: 
     print 'Error(ret=%d): cannot elevate privilege.' % (ret,) 
+0

你好,它確實要求提升,但是一旦我點擊yes,程序出於某種奇怪的原因就會運行兩次。對此有何意見? – tomSurge

+0

它運行兩次是因爲它第一次以管理員身份啓動程序的新實例。如果他的功能沒有返回True,你需要退出程序。或者,你可能會改變這一點來啓動一個不同的程序,使用這個只是一個「發射臺」。 – BuvinJ

+0

謝謝!有沒有辦法在原始終端中獲得新進程的輸出並且不打開額外的窗口? –

0

我可以確認,通過delphifirst作品的解決方案,是運行一個python腳本使用提升的權限問題的最簡單,最簡單的解決方案。

我創建了python可執行文件(python.exe)的快捷方式,然後通過在調用python.exe之後添加我的腳本名稱來修改快捷方式。接下來,我在快捷方式的「兼容性選項卡」上選中「以管理員身份運行」。當執行快捷方式時,您會收到一條提示,詢問是否以管理員身份運行腳本。

我特別的python應用程序是一個安裝程序。該程序允許安裝和卸載另一個python應用程序。在我的情況下,我創建了兩個快捷方式,一個名爲「appname install」,另一個名爲「appname uninstall」。這兩個快捷方式之間的唯一區別是跟在python腳本名稱後面的參數。在安裝程序版本中,參數是「安裝」。在卸載版本中,參數是「卸載」。安裝程序腳本中的代碼將評估提供的參數,並根據需要調用相應的函數(安裝或卸載)。

我希望我的解釋能夠幫助其他人更快地弄清楚如何以提升的權限運行python腳本。

0

此外,如果你的工作目錄是不同於你可以使用lpDirectory

procInfo = ShellExecuteEx(nShow=showCmd, 
          lpVerb=lpVerb, 
          lpFile=cmd, 
          lpDirectory= unicode(direc), 
          lpParameters=params) 

會來方便的,如果改變的路徑是不是蟒蛇3.X

相關問題