【ExcelVBA】マクロが呼び出したバッチファイルの終了を待ってから次のバッチファイルを呼び出すには

本記事では、マクロが呼び出したバッチファイルの終了を待ってから次のバッチファイルを呼び出す方法についてご説明します。

事例と懸念

例えばバッチファイルを2つ呼び出すマクロを作成したとします。(一つ目はバッチファイルA、もう一つはバッチファイルBとします)

このマクロは、バッチファイルAが先に、バッチファイルBはその次に呼び出されるよう作られています。

このマクロを実行する際、次の懸念が出てきます。

  • 初めに呼び出したバッチファイルの処理が終わらないまま、次のバッチファイルが呼び出されるのは困る
  • バッチファイルの処理で取得した値を次のバッチファイルに渡して処理させたいけど、バッチファイルの処理が終わる前に次のバッチファイルを呼び出してしまうかもしれないから値が渡せないのは問題
  • バッチファイルの処理が終わってないのに、マクロ側の処理が終わるのは困る

マクロとバッチファイルはそれぞれ独立しており、実行されればそれぞれの処理が流れていくため、以上のことが懸念されます。

なので、マクロ側でバッチファイルの処理が終わるまで待機する、といった制御が必要になります。

具体的にはどのように処理されるのが良いのかというと、次の通りです。

STEP.1
マクロがバッチファイルを呼び出す
マクロがバッチファイルを呼び出します。
STEP.2
バッチファイルの処理が完了するまでマクロが待機
バッチファイルの処理が完了するまでマクロが待機します。
(バッチファイルの処理が行われている間、マクロは何も処理をしない)
STEP.3
バッチファイルの処理が完了したら次のバッチファイルを呼び出す
バッチファイルの処理が完了したら、マクロが次のバッチファイルを呼び出します。

では、上記のように処理させるにはどうすればよいのか、その方法をご説明します。

方法は2つある

マクロが呼び出したバッチファイルの終了を待ってから次のバッチファイルを呼び出す方法は次の2つです。

  1. 同期モードでバッチファイルを呼び出す
  2. バッチファイルの処理が完了したことを知らせるファイルをバッチファイルが生成し、マクロがバッチファイルの呼び出しを制御する
    (バッチを非同期モードで呼び出す場合)
「同期モード/非同期モード」とは?
「同期/非同期」とは、呼び出したプログラムの終了をマクロ側が待つかどうかのことを指します。
同期(True):呼び出したプログラムの終了をExcelのマクロ側が待つ
非同期(False):呼び出したプログラムの終了をExcelのマクロ側が待たない

【方法1】同期モードでバッチファイルを呼び出す

一つ目の方法は、同期モードでバッチファイルを呼び出すことです。

同期モードでバッチファイルを呼び出すことで、マクロが呼び出したバッチファイルの終了を待ってから次のバッチファイルを呼び出すことができます。

呼び出したバッチファイルが終了しない限り、マクロ側の処理は中断されたままです。

Excelのマクロのコード(例)

    'WshShellオブジェクト
    Dim obj As WshShell
    
    'バッチファイル
    Const batPathFileName As String = "C:\work\exec.bat"
    
    'WshShellオブジェクトからインスタンスを生成する
    Set obj = New WshShell
    
    '選択されたバッチファイルをパラメタに指定して、バッチファイルを呼び出す
    Call obj.Run(batPathFileName & " " & "C:\work", 0, WaitOnReturn:=True)

コードの解説

まずは11行目を見てください。

11行目が同期モードでバッチファイルを呼び出している箇所になります。

この11行目でバッチファイルを呼び出すと、バッチファイルの処理が終わらない限り、12行目以降の処理は行われません。

Runメソッドには、「バッチファイル名」と「バッチファイル実行に必要な引数」を半角スペースでつないた文字列を、第1引数に指定しています。

この「バッチファイル実行に必要な引数」をバッチファイルが受け取り、バッチファイルの処理に使われます。

そして、第2引数にはバッチファイル実行時のコマンドプロンプトのウィンドウの表示方法を指定(0は非表示にする設定値)、第3引数には「同期/非同期」の設定値(Trueは同期)を指定しています。

【方法2】バッチファイルの処理が完了したことを知らせるファイルをバッチファイルが生成し、マクロがバッチファイルの呼び出しを制御する

2つ目の方法は、バッチファイルの処理が完了したことを知らせるファイルをバッチファイルが生成し、マクロがバッチファイルの呼び出しを制御することです。

ただし、これは、バッチファイルを非同期で呼び出した場合です。

バッチファイルを非同期で呼び出した場合は、マクロ側はバッチファイルの終了を待たずに後続の処理を行ってしまいます。

なので、マクロがバッチファイルの終了を待つようにマクロ側に制御させます。

制御させるイメージは次のとおりです。

STEP.1
マクロがバッチファイルを呼び出す
マクロがバッチファイルを呼び出します。
STEP.2
バッチファイルの処理が完了したことを知らせるファイルの存在確認
バッチファイルの処理が完了したことを知らせるファイルが存在するか確認します。
※このファイルはバッチファイル側で生成します。
STEP.3
バッチファイルの処理が完了したことを知らせるファイルが存在しない場合はSTEP2に戻る
バッチファイルの処理が完了したことを知らせるファイルが存在しない場合はSTEP2に戻ります。
STEP.4
バッチファイルの処理が完了したことを知らせるファイルが存在する場合は後続の処理実行
バッチファイルの処理が完了したことを知らせるファイルが存在する場合は、後続の処理(もう一つのバッチファイル実行を含む)を実行します。

バッチファイル側で処理が完了したタイミングで適当にファイルをバッチファイルが生成(空のテキストファイルでOK)し、マクロがそのファイルが存在しているかをループで繰り返しチェックします。

このファイルが存在すればバッチファイルの処理が完了していますし、存在していなければまだバッチファイルの処理は完了していない、ということが分かります。

Excelのマクロのコード(例)

    Dim batName() As Variant
    Dim cellPos() As Variant

    Const execDoneCheckFilePath As String = "C:\work\execDoneCheck"
    Const execDoneCheckFileName As String = "execDone.txt"
    Const batFilePath As String = "C:\work"
    
    '各バッチファイルのファイル名
    batName = Array("pg1.bat", "pg2.bat")

    With CreateObject("Scripting.FileSystemObject")
        
        '実行結果出力ファイルが存在するか確認する
        If .FileExists(execDoneCheckFilePath & "\" & _
                      execDoneCheckFileName) Then
    
            '存在する場合は、実行結果出力ファイルを削除する
            Kill execDoneCheckFilePath & "\" & _
                      execDoneCheckFileName
        End If        
        
        For cnt = 0 To UBound(batName)
    
            'バッチファイルを呼び出す
            Call execBatFile(batFilePath & "\" & batName(cnt))
            
            Do
                
                '実行結果出力ファイルが存在するか確認する
                If .FileExists(execDoneCheckFilePath & "\" & _
                              execDoneCheckFileName) Then
                              
                    '存在する場合は、実行結果出力ファイルを削除する
                    Kill execDoneCheckFilePath & "\" & _
                              execDoneCheckFileName
                    
                    'doループを抜ける(※次のバッチファイルを実行するために抜ける)
                    Exit Do
                    
                End If
                
                DoEvents
            
            Loop
        
        Next
    
    End With

コードの解説

まずは28行目から45行目のループ処理のコードを見てください。

バッチファイルの処理が完了したことを知らせるファイルが存在しているかを、マクロがこのループ処理の中でチェックしています。

ファイルの存在のチェックは31行目の条件処理で行い、存在している場合は39行目でループを抜けます。

存在しなければまだバッチファイルの処理が行われている最中なので、再び28行目に戻り再度ファイルの存在チェックを行います。

このループ処理はファイルが存在するまで繰り返されるので、バッチファイルの処理が終わらない限り後続処理は行われず、2つ目のバッチファイルも実行されません。

バッチファイルの処理が完了すればループを抜けるので、ループ処理の後続処理が行われて2つ目のバッチファイルも実行されます。

バッチファイルのコード(例)

@echo off

rem データファイルの格納先とファイル名
set opFilePath=C:\work\output
set opFile=outputFileProc1.txt

rem 実行結果ファイルの格納先とファイル名
set edcFiledirPath=C:\work\execDoneCheck
set edcfilename=execDone.txt

rem データファイルの有無チェック(存在しなければ作成。存在すれば作成しない)
if not exist %opFilePath%\%opFile% (
	rem  データファイルを作成する(テスト用に1万行書き込み)
	for /l %%n in (1,1,10000) do (
		echo pg1.bat実行 >> %opFilePath%\%opFile%
	)
)

rem データファイルの有無チェック(存在しなければ作成。存在すれば作成しない)
if not exist %edcFiledirPath%\%edcfilename% (
  rem  データファイルを作成する
  echo 正常終了 > %edcFiledirPath%\%edcfilename%
)

コードの解説

まずは22行目を見てください。

この行で、バッチファイルの処理が完了したことを知らせるファイルを生成しています。

バッチファイルのメイン処理が終わって最後に、バッチファイルの処理が完了したことを知らせるファイルを作成しているのです。

バッチファイルの処理が完了したことを知らせるファイルが生成されれば、マクロ側がバッチファイルの処理が終わったと認識できるので、マクロの後続処理(2つ目のバッチファイルの実行を含む)が行われます。

最後に

本記事では、マクロが呼び出したバッチファイルの終了を待ってから次のバッチファイルを呼び出す方法についてご説明しました。

方法は次の2つです。

  1. 同期モードでバッチファイルを呼び出す
  2. バッチファイルの処理が完了したことを知らせるファイルをバッチファイルが生成し、マクロがバッチファイルの呼び出しを制御する
    (バッチを非同期モードで呼び出す場合)

1つ目の方法のほうが実装するステップ数が少ないのでおすすめです。

ただし、非同期モードをどうしても使わないといけない場合は2つ目の方法を参考にしてみてくださいね。

プログラミングのスキルを習得するなら

プログラミングのスキルを習得したい、今のスキルをもっと高めたい、そう考えているなら「プログラミングスクール」がおすすめです。

プログラミングのスキルの基礎を身につけるなら「TechAcademy」で1週間の無料体験があるので、これで「プログラミングの基礎」を学ぶのにおすすめですよ。

→ TechAcademyの「1週間 無料体験」はこちら