VBA で管理者権限に昇格する必要のある外部アプリを実行する
ShellExecuteEx で "runas" する。
こんな感じになる。
Dim ei As SHELLEXECUTEINFO
ei.cbSize = LenB(ei)
ei.fMask = SEE_MASK_NOCLOSEPROCESS ' プロセスを終了させない
ei.hwnd = GetActiveWindow() ' シートのハンドル
ei.lpVerb = "runas"
ei.lpFile = ActiveWorkbook.Path & "\ConsoleApplication1.exe"
ei.lpParameters = "" ' コマンドライン引数(ある場合)
ei.lpDirectory = ""
ei.nShow = SW_HIDE ' コンソールを非表示にする
ShellExecuteEx ei ' 実行たとえばレジストリを読み取るようなプログラムを作って
Public Class Class1
Public Shared Sub Main(ByVal args() As String)
Try
Dim regkey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\Microsoft\Windows NT\CurrentVersion", True)
Dim stringValue As String = DirectCast(regkey.GetValue("ProductName"), String)
regkey.Close()
MsgBox("ProductName: " & stringValue)
Catch ex As Exception
MsgBox(ex.ToString())
Environment.ExitCode = 1
Return
End Try
Environment.ExitCode = 0
End Sub
End Classこれを VBA から呼ぼうとして、
こんな風にやっても
Dim r As Long
Dim s: Set s = CreateObject("Wscript.Shell")
r = s.Run(ActiveWorkbook.Path & "\ConsoleApplication1.exe", vbHide, True)
Set s = Nothingだから ShellExecuteEx しなければならない。
まとめ
左を CommandButton1、右を CommandButton2 として、Sheet1 に以下記述する。
Sheet1
' =========================================================
' UAC なし
' =========================================================
Private Sub CommandButton1_Click()
Dim r As Long
Dim s: Set s = CreateObject("Wscript.Shell")
r = s.Run(ActiveWorkbook.Path & "\ConsoleApplication1.exe", vbHide, True)
Set s = Nothing
MsgBox ("ExitCode: " & r)
End Sub
' =========================================================
' UAC あり
' =========================================================
Private Sub CommandButton2_Click()
Dim ei As SHELLEXECUTEINFO
ei.cbSize = LenB(ei)
ei.fMask = SEE_MASK_NOCLOSEPROCESS ' プロセスを終了させない
ei.hwnd = GetActiveWindow() ' シートのハンドル
ei.lpVerb = "runas"
ei.lpFile = ActiveWorkbook.Path & "\ConsoleApplication1.exe"
ei.lpParameters = "" ' コマンドライン引数(ある場合)
ei.lpDirectory = ""
ei.nShow = SW_HIDE ' コンソールを非表示にする
ShellExecuteEx ei ' 実行
Dim r As Long
r = WaitForSingleObject(ei.hProcess, INFINITE) ' コマンド終了まで待機(タイムアウトなし)
If r <> WAIT_OBJECT_0 Then
MsgBox "異常終了"
Exit Sub
End If
GetExitCodeProcess ei.hProcess, r ' ExitCode 取得
CloseHandle ei.hProcess ' プロセス終了
MsgBox ("ExitCode: " & r)
End Sub
「標準モジュール」
Public Const SW_HIDE = 0
Public Const SEE_MASK_NOCLOSEPROCESS = &H40
Public Const INFINITE = &HFFFF
Public Const STATUS_WAIT_0 = 0&
Public Const WAIT_OBJECT_0 = ((STATUS_WAIT_0) + 0&)
Type SHELLEXECUTEINFO
cbSize As Long
fMask As Long
hwnd As Long
lpVerb As String
lpFile As String
lpParameters As String
lpDirectory As String
nShow As Long
hInstApp As Long
lpIDList As Long
lpClass As String
hkeyClass As Long
dwHotKey As Long
hIcon As Long
hProcess As Long
End Type
Declare Function ShellExecuteEx Lib "shell32.dll" Alias "ShellExecuteExA" (lpExecInfo As SHELLEXECUTEINFO) As Long
Declare Function GetActiveWindow Lib "user32.dll" () As Long
Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, ByRef lpdwExitCode As Long) As Long
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
ConsoleApplication1
Public Class Class1
Public Shared Sub Main(ByVal args() As String)
Try
Dim regkey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\Microsoft\Windows NT\CurrentVersion", True)
Dim stringValue As String = DirectCast(regkey.GetValue("ProductName"), String)
regkey.Close()
MsgBox("ProductName: " & stringValue)
Catch ex As Exception
MsgBox(ex.ToString())
Environment.ExitCode = 1
Return
End Try
Environment.ExitCode = 0
End Sub
End Class

