def execute_elevated(*args): # FIXME: support **kwargs from win32com.shell.shell import ShellExecuteEx from win32com.shell import shellcon import win32process, win32event import winxpgui import win32api import win32con try: hwnd = winxpgui.GetConsoleWindow() except winxpgui.error: hwnd = 0 parameters = "" if not hasattr(sys, "frozen"): # Not running under py2exe exe parameters += "\"" + sys.argv[0] + "\" " parameters += " ".join(map(lambda x: "\"" + str(x) + "\"", args)) print "Executing elevated with parameters " + parameters # TODO: capture output (maybe via named pipe) rc = ShellExecuteEx(hwnd=hwnd, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpVerb="runas", lpFile=sys.executable, lpParameters=parameters, nShow=win32con.SW_SHOW) hproc = rc['hProcess'] win32event.WaitForSingleObject(hproc, win32event.INFINITE) exit_code = win32process.GetExitCodeProcess(hproc) if exit_code: raise Exception("Error: subprocess failed (exit code %s)." % exit_code)
def UseCommandLine(*classes, **flags): unregisterInfo = '--unregister_info' in sys.argv unregister = '--unregister' in sys.argv flags['quiet'] = flags.get('quiet', 0) or '--quiet' in sys.argv flags['debug'] = flags.get('debug', 0) or '--debug' in sys.argv flags['unattended'] = flags.get('unattended', 0) or '--unattended' in sys.argv if unregisterInfo: return UnregisterInfoClasses(*classes, **flags) try: if unregister: UnregisterClasses(*classes, **flags) else: RegisterClasses(*classes, **flags) except win32api.error, exc: # If we are on xp+ and have "access denied", retry using # ShellExecuteEx with 'runas' verb to force elevation (vista) and/or # admin login dialog (vista/xp) if flags['unattended'] or exc[0] != winerror.ERROR_ACCESS_DENIED \ or sys.getwindowsversion()[0] < 5: raise from win32com.shell.shell import ShellExecuteEx from win32com.shell import shellcon import win32process, win32event import winxpgui # we've already checked we are running XP above if not flags['quiet']: print "Requesting elevation and retrying..." new_params = " ".join(['"' + a + '"' for a in sys.argv]) # specifying the parent means the dialog is centered over our window, # which is a good usability clue. # hwnd is unlikely on the command-line, but flags may come from elsewhere hwnd = flags.get('hwnd', None) if hwnd is None: try: hwnd = winxpgui.GetConsoleWindow() except winxpgui.error: hwnd = 0 rc = ShellExecuteEx(hwnd=hwnd, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpVerb="runas", lpFile=win32api.GetShortPathName(sys.executable), lpParameters=new_params, lpDirectory=os.getcwd(), nShow=win32con.SW_SHOW) # Output is lost to the new console which opens, so the # best we can do is get the exit code of the process. hproc = rc['hProcess'] win32event.WaitForSingleObject(hproc, win32event.INFINITE) exit_code = win32process.GetExitCodeProcess(hproc) if exit_code: # Even if quiet you get to see this error. print "Error: registration failed (exit code %s)." % exit_code print "Please re-execute this command from an elevated command-prompt" print "to see details about the error." else: if not flags['quiet']: print "Elevated process succeeded."
def ReExecuteElevated(flags): from win32com.shell.shell import ShellExecuteEx from win32com.shell import shellcon import win32process, win32event import winxpgui # we've already checked we are running XP above import tempfile if not flags['quiet']: print("Requesting elevation and retrying...") new_params = " ".join(['"' + a + '"' for a in sys.argv]) # If we aren't already in unattended mode, we want our sub-process to # be. if not flags['unattended']: new_params += " --unattended" # specifying the parent means the dialog is centered over our window, # which is a good usability clue. # hwnd is unlikely on the command-line, but flags may come from elsewhere hwnd = flags.get('hwnd', None) if hwnd is None: try: hwnd = winxpgui.GetConsoleWindow() except winxpgui.error: hwnd = 0 # Redirect output so we give the user some clue what went wrong. This # also means we need to use COMSPEC. However, the "current directory" # appears to end up ignored - so we execute things via a temp batch file. tempbase = tempfile.mktemp("pycomserverreg") outfile = tempbase + ".out" batfile = tempbase + ".bat" try: batf = open(batfile, "w") try: cwd = os.getcwd() print("@echo off", file=batf) # nothing is 'inherited' by the elevated process, including the # environment. I wonder if we need to set more? print("set PYTHONPATH=%s" % os.environ.get('PYTHONPATH', ''), file=batf) # may be on a different drive - select that before attempting to CD. print(os.path.splitdrive(cwd)[0], file=batf) print('cd "%s"' % os.getcwd(), file=batf) print('%s %s > "%s" 2>&1' % (win32api.GetShortPathName(sys.executable), new_params, outfile), file=batf) finally: batf.close() executable = os.environ.get('COMSPEC', 'cmd.exe') rc = ShellExecuteEx(hwnd=hwnd, fMask=shellcon.SEE_MASK_NOCLOSEPROCESS, lpVerb="runas", lpFile=executable, lpParameters='/C "%s"' % batfile, nShow=win32con.SW_SHOW) hproc = rc['hProcess'] win32event.WaitForSingleObject(hproc, win32event.INFINITE) exit_code = win32process.GetExitCodeProcess(hproc) outf = open(outfile) try: output = outf.read() finally: outf.close() if exit_code: # Even if quiet you get to see this message. print("Error: registration failed (exit code %s)." % exit_code) # if we are quiet then the output if likely to already be nearly # empty, so always print it. print(output, end=' ') finally: for f in (outfile, batfile): try: os.unlink(f) except os.error as exc: print("Failed to remove tempfile '%s': %s" % (f, exc))