PMPの流儀

PMPの流儀

エンジニアのページ。主にPC関係で便利な事を中心に記事を書いています。

MENU

VBA から PowerShell スクリプトをウインドウ非表示で起動する

VBAからPowerShellスクリプトをウインドウ非表示で起動する方法を紹介します。
PowerShellを非表示で起動するには、こちらで紹介している通りWSHを経由させます。

pmp-style.hatenablog.com その結果、VBAWSHPowerShell というコール順になります。

 

サンプル

PowerShellスクリプト "C:\temp\test dir\test.ps1"を、引数 "C:\temp\test dir" を指定して、ウインドウ非表示で起動する。

空白ありのパスなので、ダブルコーテーションが必要です。
VB, WSH, PowerShellの3言語が要求するダブルコーテーションを満たしてあげなければならず、半日かかって行きついた結果がこれです。もはや暗号にしか見えないコードですね。
最初は1行ですべて書こうとしたらはまってしまい、変数に分離したのとダブルコーテーションをASCIIコードで書くことで動かすことができました。

    ps = Chr(34) & "C:\temp\test dir\test.ps1" & Chr(34)
    arg = Chr(34) & "C:\temp\test dir" & Chr(34)
    cmdline = """powershell"" -ExecutionPolicy RemoteSigned -File " & ps & " " & arg
    
    Dim objWSH As Object
    Set objWSH = CreateObject("WScript.Shell")
    objWSH.Run cmdline, 0, True

こりゃサイクロマティック複雑度は低いが、ダブルコーテーション複雑度はMAXだよ。VBAなんて大っ嫌いだ~

しかし、アプリケーション環境としては UIはEXCEL、コードビハインドでVBAと、EXCELがあれば完結してしまうため捨てられない。最近 PowerShell に目覚めて、とても面白いのでロジックは PowerShell で書くようになってきました。

関数化

これでは使いにくいので関数化します。
なるべく汎用化したいので、ウインドウの表示やスクリプトの終了待ちもパラメータ化しました。また、PowerShellスクリプトへの引数の数も可変引数にすることでいくつでも渡せるようにしてあります。もちろんパス名など半角空白が入っていても大丈夫ですw

関数 RunPowerShell

引数
intWindowStyle
Wsh のRun コマンドの第2引数です

意味
0 ウィンドウを非表示にし、別のウィンドウをアクティブにする
1 ウィンドウをアクティブにして表示する。ウィンドウが最小化または最大化されている場合は、元のサイズと位置に戻る
2 ウィンドウをアクティブにし、最小化ウィンドウとして表示する
3 ウィンドウをアクティブにし、最大化ウィンドウとして表示する
4 ウィンドウを最新のサイズと位置で表示する。アクティブ・ウィンドウは切り替わらない
5 ウィンドウをアクティブにし、現在のサイズと位置で表示する
6 指定したウィンドウを最小化し、Zオーダー上で次に最上位となるウィンドウをアクティブにする
7 ウィンドウを最小化ウィンドウとして表示する。アクティブ・ウィンドウは切り替わらない
8 ウィンドウを現在の状態で表示する。アクティブ・ウィンドウは切り替わらない
9 ウィンドウをアクティブにして表示する。ウィンドウが最小化または最大化されている場合は、元のサイズと位置に戻る
10 アプリケーションを起動したプログラムの状態に基づいて表示状態を設定する

bWaitOnReturn
Wsh のRun コマンドの第3引数です
 True: スクリプト実行の終了を待つ(同期実行)
 False: スクリプト実行の終了を待たない(非同期実行)

psFilePath
PowerShell スクリプトのファイルパス

args
PowerShellスクリプトへの引数
 可変引数なのでいくつでも渡せます

戻り値
 bWaitOnReturn がTrueのとき
  PowerShellスクリプトの戻り値
 bWaitOnReturn がFalseのとき
  WshのRun の戻り値

Function RunPowerShell(intWindowStyle As Long, bWaitOnReturn As Boolean, psFilePath As String, ParamArray args())
    Dim cmdline As String
    cmdline = Chr(34) & "powershell" & Chr(34) & " -ExecutionPolicy RemoteSigned -File " & Chr(34) & psFilePath & Chr(34)
    
    Dim arg As Variant
    For Each arg In args
        cmdline = cmdline & " " & Chr(34) & arg & Chr(34)
    Next
    
    Dim objWSH As Object
    Set objWSH = CreateObject("WScript.Shell")
    
    Dim ret As Variant
    ret = objWSH.Run(cmdline, intWindowStyle, bWaitOnReturn)

    RunPowerShell = ret
End Function

'呼び出し例  test.ps1をウインドウを表示して同期実行させる
    ret = RunPowerShell(1, True, "c:\temp dir\test.ps1", "c:\temp dir")

次回は、PowerShell の実行結果をVBAに伝達する方法について記載します。