Pythonスクリプト内からUAC昇格をリクエストしますか?


91

PythonスクリプトでVista上のファイルをコピーする必要があります。通常のcmd.exeウィンドウから実行すると、エラーは生成されませんが、ファイルはコピーされません。私が実行している場合はcmd.exe、「administatorとして」とし、私のスクリプトを実行し、それが正常に動作します。

ユーザーアカウント制御(UAC)は通常、多くのファイルシステムアクションを防止するため、これは理にかなっています。

Pythonスクリプト内からUAC昇格要求を呼び出す方法はありますか(「このようなアプリには管理者アクセスが必要ですが、これで問題ありませんか?」というようなダイアログ)

それが不可能な場合、スクリプトが昇格されていないことをスクリプトが少なくとも検出して、正常に失敗する方法はありますか?


3
stackoverflow.com/a/1445547/1628132この回答に続いて、py2exeを使用して.pyスクリプトから.exeを作成し、「uac_info」と呼ばれるフラグを使用して、それはかなり素晴らしいソリューションです
foxcoreg

回答:


96

2017年現在、これを実現する簡単な方法は次のとおりです。

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を使用している場合は、次の最終行を置き換える必要があります。

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

また、あなたが実行可能ファイルにあなたのPythonスクリプトを変換した場合ことに注意してください(のようなツールを使用するにはpy2execx_freezepyinstaller)あなたが使用する必要があるsys.argv[1:]のではなく、sys.argv第四パラメータで。

ここにはいくつかの利点があります:

  • 外部ライブラリは必要ありません。標準ライブラリのみを使用ctypessysます。
  • Python 2とPython 3の両方で動作します。
  • ファイルリソースを変更したり、マニフェストファイルを作成したりする必要はありません。
  • if / elseステートメントの下にコードを追加しない場合、コードが2回実行されることはありません。
  • 最後の行でAPI呼び出しの戻り値を取得し、失敗した場合にアクションを実行できます(コード<= 32)。可能な戻り値をここで確認してください。
  • 6番目のパラメーターを変更して、生成されたプロセスの表示方法を変更できます。

基本的なShellExecute呼び出しのドキュメントはこちらです。


9
これを実行するには、UnicodeインスタンスをShellExecuteWのパラメーター(u'runas 'やunicode(sys.executable)など)として使用する必要がありました。
ジャノッシュ2017

6
@Janosch、それは私のコードがPython 3(すべての文字列がユニコードとして扱われる)にある間にPython 2.xを使用しているためです。しかし、言及するのは良いことです、ありがとう!
マルティン・デ・ラ・フエンテ

2
@MartinこのコードをWindowsコマンドラインから次のように実行している場合: "python yourcode.py"これは単にpython.exeを開きます。それを修正する方法はありますか?
user2978216 2017

1
@ user2978216同じ問題が発生しました。この行でctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1) sys.executableは、Pythonインタープリターのみに解決さC:\Python27\Python.exeれます(例:)解決策は、実行中のスクリプトを引数として追加することです(を置き換える"")。ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)また、これがpython 2.xで機能するためには、すべての文字列引数がUnicode(つまりu"runas"unicode(sys.executable)およびunicode(__file__))である必要があります
Javier Ubillos

2
両方を@HrvojeT、ShellExecuteWおよびShellExecuteAへの呼び出しですShellExecuteのWindows API内の関数。ユニコードフォーマット、後者であると文字列がANSI形式で使用され、前者義務付け
マルティンデ・ラ・フェンテ

69

dguaragliaの回答が機能するまで少し時間がかかりました。他の人の時間を節約するために、このアイデアを実装するために私がしたことは次のとおりです。

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
これは昇格して終了するようです...いくつかの印刷ステートメントを入力すると、2回目に実行されません
Joran Beasley

6
@JoranBeasley、出力は表示されません。ShellExecuteExは、STDOUTを元のシェルにポストしません。その点で、デバッグは困難です... しかし、特権を引き上げるトリックは確実に機能します。
Tim Keating

1
@ TimKeating、ActiveStateには、デバッグを少し簡単にするためのレシピがあり ます。標準のPythonロギングでDebugViewユーティリティを使用します
samwyse

1
同じコンソールで出力を取得することは不可能に思われますが、ShellExecuteExに引数nShow = 5を指定すると、新しいコマンドウィンドウが開き、昇格したスクリプトからの出力が表示されます。
Emil Styrke、2014

2
引用のために、あなたsubprocess.list2cmdlineはそれを適切に行うために使うことができます。
coderforlife 2015

29

特定のタスクを実行するために、しばらくの間アプリケーション特権を昇格させる方法はないようです。Windowsは、プログラムの開始時に、アプリケーションが特定の特権を必要とするかどうかを知る必要があり、アプリケーションそれらの特権を必要とするタスクを実行するときにユーザーに確認を求めます。これを行うには2つの方法があります。

  1. アプリケーションにいくつかの特権が必要になる可能性があることをWindowsに通知するマニフェストファイルを書き込む
  2. 別のプログラム内から昇格された特権でアプリケーションを実行する

この2つの 記事では、この仕組みについて詳しく説明しています。

CreateElevatedProcess APIの厄介なctypesラッパーを記述したくない場合は、コードプロジェクトの記事で説明されているShellExecuteExトリックを使用します(Pywin32にはShellExecuteのラッパーが付属しています)。どうやって?このようなもの:

プログラムが起動すると、管理者権限があるかどうかをチェックし、そうでない場合はShellExecuteトリックを使用して自分自身を実行し、すぐに終了します。そうであれば、手元のタスクを実行します。

プログラムを「スクリプト」として説明しているので、それで十分でしょう。

乾杯。


それらのリンクをありがとう、それらは私がUACのものについて多くを知るのに非常に役に立ちました。
Colen

4
これについて注意したいことは、os.startfile($ EXECUTABLE、 "runas")を使用して、PyWin32なしでShellExecuteを実行できることです(インストールに問題がありました)。
Mike McQuaid

@マイク-しかしrunas、新しいプロンプトが表示されます。そして、startfileはコマンドライン引数を受け入れません$EXECUTABLE.
Sridhar Ratnakumar

私はこのテクニックの完全な実装で別の回答を追加しました。これは、Pythonスクリプトの先頭に追加できるはずです。
Jorenko、2012

2番目のリンクの記事は、「MSDNマガジン2007年1月」の「最小の特権:Windows Vistaのユーザーアカウント制御でアプリを適切に再生するように教える」でしたが、現在、この問題は.chmファイルとしてのみ入手可能です。
Peter

6

他の人が私と同じようにGoogle検索によってここに指示された場合に備えて、この回答を追加するだけです。このelevateモジュールをPythonスクリプトで使用し、スクリプトをWindows 10の管理者権限で実行しました。

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


elevateモジュールを使用してみたところ、「システムからファイルにアクセスできません」というエラーが発生しました。
paxos1977

@ paxos1977そのエラーを示すコードスニペットを投稿できますか?ありがとう!
アーヴィングモイ

4

次の例は、MARTIN DE LA FUENTE SAAVEDRAの優れた成果と受け入れられた回答に基づいています。特に、2つの列挙が導入されています。1つ目は、昇格されたプログラムを開く方法を簡単に指定できるようにし、2つ目は、エラーを簡単に特定する必要がある場合に役立ちます。すべてのコマンドライン引数を新しいプロセスに渡す場合sys.argv[0]は、おそらく関数呼び出しに置き換える必要があります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

この質問が数年前に尋ねられたことを認識して、より洗練されたソリューションがgithubで提供されていると思います彼のモジュールpywinutilsを使用してfrmdstryrによって:

抜粋:

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    

これはCOMインターフェイスを利用し、管理者権限が必要なディレクトリにコピーしているかどうかを確認するおなじみのダイアログプロンプトで管理者権限が必要であることを自動的に示し、コピー操作中に典型的なファイル進行ダイアログも提供します。



2

どこかにショートカットを作成し、ターゲットとして使用することができます:python yourscript.py次に、プロパティと詳細設定で、管理者として実行を選択します。

ユーザーがショートカットを実行すると、アプリケーションを昇格するように求められます。


1

スクリプトで常に管理者権限が必要な場合は、次のようにします。

runas /user:Administrator "python your_script.py"

15
注意、標高!=管理者として実行
Kugel

私はpythonが初めてです...そのコードをどこに置くか教えていただけますか?
Rahat Islam Khan

@RahatIslamKhan:コマンドプロンプトウィンドウを開き、次の場所に配置しyour_script.pyます。コマンドは管理者ユーザーとして実行されます。@Kugelのコメントを必ず理解してください。
jfs 2015

1

上記のJorenkoの作業のバリエーションにより、昇格したプロセスで同じコンソールを使用できます(ただし、以下の私のコメントを参照してください)。

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

ごめんなさい。同じコンソールオプション(SEE_MASK_NO_CONSOLE)は、すでに昇格している場合にのみ機能します。私の悪い。
バーウィン2016年

1

これは主に、Windowsのスペースでパラメータを使用することができますJorenkoの答え、へのアップグレードですが、また、我々が使用していないので、cx_freezeやpy2exeで動作します、また:) Linux上でかなりよく動作するはずです__file__が、sys.argv[0]実行可能ファイルと

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:])
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.