2013-10-30 318 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

相关问题