「UACが有効の時、アプリケーションを管理者に昇格させて起動する」ではアプリケーション起動時に管理者に昇格させる方法を紹介しました。ここでは、必要な処理を行なうときだけ昇格させる方法を紹介します。
「ユーザー アカウント制御 (UAC: User Account Control) - Windows 7 対応アプリケーションの互換性」によるとその方法は2つあり、「ShellExecute() もしくは ShellExecuteEx() を使用して、親プログラムから起動する」方法と、「COM オブジェクトとして分離する」方法です。ここでは簡単な前者の方法のみを紹介します。後者の方法は、「this.Pose() as Expert」などを参考にしてください。
メインのプログラム(親プログラム)では管理者権限が必要ない処理だけを行い、管理者権限が必要な処理は別のプログラム(子プログラム)として分離します。そして管理者権限が必要な処理を行う時は、親プログラムから子プログラムをProcess.Startメソッドで起動します。
管理者権限が必要な子プログラムは、「UACが有効の時、アプリケーションを管理者に昇格させて起動する」で紹介している方法で起動時に昇格されるようにします。つまり、マニュフェストを追加するか、runas動詞を付けて起動するかのどちらか(両方でも問題はありません)をします。
親プログラムから子プログラムを起動した時、モーダルになるようにする(子プログラムが終了するまで親プログラムが停止されるようにする)には、WaitForExitメソッドを使います(詳しくは「外部アプリケーションを起動して終了まで待機する」で説明しています)。
また、「ユーザーアカウント制御」ダイアログが親プログラム上に表示されるようにするには、ProcessStartInfoのErrorDialogプロパティをtrueにして、ErrorDialogParentHandleプロパティに親プログラムのウィンドウハンドルを設定します。
なお「User Account Control」のガイドラインによれば、「ユーザーアカウント制御」ダイアログでユーザーにキャンセルされて起動できなかった時でもエラーメッセージを出してはいけません。それ以外にも幾つかのガイドラインが書かれていますので(昇格は設定ごとではなく、タスクごとに行なわれるようにするなど)、一度目を通しておくことをお勧めします。
以下の例では、この方法で管理者に昇格してプログラムを起動するメソッドを作成し、それをButton1のClickイベントハンドラで呼び出しています。このコードは、ボタンコントロール「Button1」が配置されているフォームのクラス内に記述されているものとします。
'Button1のClickイベントハンドラ Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'setclock.exeを昇格して実行する RunElevated(System.IO.Path.Combine(Application.StartupPath, "setclock.exe"), _ "2010/10/10 00:00:00", Me, True) End Sub ''' <summary> ''' 管理者権限が必要なプログラムを起動する ''' </summary> ''' <param name="fileName">プログラムのフルパス。</param> ''' <param name="arguments">プログラムに渡すコマンドライン引数。</param> ''' <param name="parentForm">親プログラムのウィンドウ。</param> ''' <param name="waitExit">起動したプログラムが終了するまで待機する。</param> ''' <returns>起動に成功した時はtrue。 ''' 「ユーザーアカウント制御」ダイアログでキャンセルされた時はfalse。</returns> Public Shared Function RunElevated(ByVal fileName As String, _ ByVal arguments As String, _ ByVal parentForm As Form, _ ByVal waitExit As Boolean) As Boolean 'プログラムがあるか調べる If Not System.IO.File.Exists(fileName) Then Throw New System.IO.FileNotFoundException() End If Dim psi As New System.Diagnostics.ProcessStartInfo() 'ShellExecuteを使う。デフォルトtrueなので、必要はない。 psi.UseShellExecute = True '昇格して実行するプログラムのパスを設定する psi.FileName = fileName '動詞に「runas」をつける psi.Verb = "runas" '子プログラムに渡すコマンドライン引数を設定する psi.Arguments = arguments If parentForm IsNot Nothing Then 'UACダイアログが親プログラムに対して表示されるようにする psi.ErrorDialog = True psi.ErrorDialogParentHandle = parentForm.Handle End If Try '起動する Dim p As System.Diagnostics.Process = System.Diagnostics.Process.Start(psi) If waitExit Then '終了するまで待機する p.WaitForExit() End If Catch generatedExceptionName As System.ComponentModel.Win32Exception '「ユーザーアカウント制御」ダイアログでキャンセルされたなどによって '起動できなかった時 Return False End Try Return True End Function
//Button1のClickイベントハンドラ private void Button1_Click(object sender, EventArgs e) { //setclock.exeを昇格して実行する RunElevated(System.IO.Path.Combine(Application.StartupPath, "setclock.exe"), "2010/10/10 00:00:00", this, true); } /// <summary> /// 管理者権限が必要なプログラムを起動する /// </summary> /// <param name="fileName">プログラムのフルパス。</param> /// <param name="arguments">プログラムに渡すコマンドライン引数。</param> /// <param name="parentForm">親プログラムのウィンドウ。</param> /// <param name="waitExit">起動したプログラムが終了するまで待機する。</param> /// <returns>起動に成功した時はtrue。 /// 「ユーザーアカウント制御」ダイアログでキャンセルされた時はfalse。</returns> public static bool RunElevated(string fileName, string arguments, Form parentForm, bool waitExit) { //プログラムがあるか調べる if (!System.IO.File.Exists(fileName)) { throw new System.IO.FileNotFoundException(); } System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); //ShellExecuteを使う。デフォルトtrueなので、必要はない。 psi.UseShellExecute = true; //昇格して実行するプログラムのパスを設定する psi.FileName = fileName; //動詞に「runas」をつける psi.Verb = "runas"; //子プログラムに渡すコマンドライン引数を設定する psi.Arguments = arguments; if (parentForm != null) { //UACダイアログが親プログラムに対して表示されるようにする psi.ErrorDialog = true; psi.ErrorDialogParentHandle = parentForm.Handle; } try { //起動する System.Diagnostics.Process p = System.Diagnostics.Process.Start(psi); if (waitExit) { //終了するまで待機する p.WaitForExit(); } } catch (System.ComponentModel.Win32Exception) { //「ユーザーアカウント制御」ダイアログでキャンセルされたなどによって //起動できなかった時 return false; } return true; }
上の例では、Button1をクリックすると「ユーザーアカウント制御」ダイアログが表示され、昇格が必要になります。「User Account Control」のガイドラインによると、UACが有効になっている時に昇格が必要なタスクを開始するボタン(あるいはその他のコントロール)には、盾アイコンを表示することが推奨されています。
コントロールに盾アイコンを表示する方法は、「コントロールにUAC盾アイコンを表示する」で説明しています。