ねらい:Excelの外からVBAを呼び出し、ヘッドレスで安全に実務を回す
「Excel外からVBA呼び出し」は、ユーザー操作なしに処理を起動するための基礎です。代表手段はCOMオートメーション(VBScript・PowerShell・他言語)、起動イベント(Workbook_Open/Auto_Open)、タスクスケジューラ連携です。肝は入口Subの型を整え、Excelを「開く→実行→保存→終了」まで自動で完了させ、失敗時も確実に復帰すること。初心者向けに、貼ってすぐ使えるテンプレートと、運用の勘所を丁寧に解説します。
重要ポイントの深掘り
外部起動では「Excelを正しく閉じる」「COM参照を解放する」「マクロをヘッドレスで動かす」設計が必須です。入口Subはメッセージを出さず、例外復帰・ログ記録・開始/終了枠を持たせ、引数受け取り(ファイル・日付など)をサポートすると、スクリプトからの制御が容易になります。
入口の型:ヘッドレス実行に耐えるRun_XXXX
標準モジュールの公共入口と枠の整備
外部から呼ぶ前提で、メッセージボックスや選択依存を排し、開始/終了枠・ログ・エラー復帰を備えます。Application.Runから引数を渡せるよう設計します。
' ModApp.bas(開始/終了の共通枠)
Option Explicit
Public Sub AppEnter(Optional ByVal status As String = "")
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
If Len(status) > 0 Then Application.StatusBar = status
End Sub
Public Sub AppLeave()
Application.StatusBar = False
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
VB' ModLog.bas(簡易ログ:Logシートへ)
Option Explicit
Public Sub LogInfo(ByVal action As String, ByVal detail As String)
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Worksheets("Log")
If ws Is Nothing Then Set ws = ThisWorkbook.Worksheets.Add: ws.Name = "Log"
On Error GoTo 0
Dim r As Long: r = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row + 1
ws.Cells(r, 1).Value = Format(Now, "yyyy-mm-dd HH:NN:SS")
ws.Cells(r, 2).Value = action
ws.Cells(r, 3).Value = detail
End Sub
VB' ModService.bas(外部呼び出し入口)
Option Explicit
Public Sub Run_DailyExport(ByVal inPath As String, ByVal outCsv As String, ByVal yyyymmdd As String)
On Error GoTo EH
AppEnter "DailyExport"
LogInfo "Start", yyyymmdd
Dim arr As Variant: arr = ReadCsvToArray(inPath)
Dim out As Variant: out = NormalizeAndFilterByDate(arr, yyyymmdd)
WriteArrayToCsv outCsv, out
LogInfo "Finish", outCsv
AppLeave
Exit Sub
EH:
LogInfo "Error", Err.Number & " - " & Err.Description
AppLeave
End Sub
VB' ModCsvUtil.bas(CSVユーティリティ・配列I/O)
Option Explicit
Public Sub WriteArrayToCsv(ByVal path As String, ByVal data As Variant)
Dim st As Object: Set st = CreateObject("ADODB.Stream")
st.Type = 2: st.Charset = "UTF-8": st.Open
Dim r As Long, c As Long, line As String
For r = 1 To UBound(data, 1)
line = ""
For c = 1 To UBound(data, 2)
Dim s As String: s = Replace(CStr(data(r, c)), """", """""")
line = line & IIf(c > 1, ",", "") & """" & s & """"
Next
st.WriteText line & vbCrLf
Next
st.SaveToFile path, 2
st.Close: Set st = Nothing
End Sub
Public Function ReadCsvToArray(ByVal path As String) As Variant
Dim st As Object: Set st = CreateObject("ADODB.Stream")
st.Type = 2: st.Charset = "UTF-8": st.Open: st.LoadFromFile path
Dim text As String: text = st.ReadText: st.Close: Set st = Nothing
Dim lines() As String: lines = Split(text, vbCrLf)
If UBound(lines) < 0 Then Exit Function
Dim head() As String: head = ParseCsvLine(lines(0))
Dim cols As Long: cols = UBound(head) + 1
Dim arr() As Variant: ReDim arr(1 To UBound(lines) + 1, 1 To cols)
Dim c As Long: For c = 1 To cols: arr(1, c) = head(c - 1): Next
Dim r As Long
For r = 2 To UBound(lines) + 1
If Len(lines(r - 1)) = 0 Then Exit For
Dim rec() As String: rec = ParseCsvLine(lines(r - 1))
For c = 1 To cols: arr(r, c) = IIf(c - 1 <= UBound(rec), rec(c - 1), ""): Next
Next
ReadCsvToArray = arr
End Function
Private Function ParseCsvLine(ByVal line As String) As String()
Dim res() As String, buf As String, i As Long, inQ As Boolean
ReDim res(0 To 0)
For i = 1 To Len(line)
Dim ch As String: ch = Mid$(line, i, 1)
If ch = """" Then
If inQ And i < Len(line) And Mid$(line, i + 1, 1) = """" Then buf = buf & """": i = i + 1 Else inQ = Not inQ
ElseIf ch = "," And Not inQ Then res(UBound(res)) = buf: buf = "": ReDim Preserve res(0 To UBound(res) + 1)
Else buf = buf & ch
End If
Next
res(UBound(res)) = buf
ParseCsvLine = res
End Function
VB重要ポイントの深掘り
外部起動では「対話」を排し、ログとステータスバーだけで進捗・結果を残します。引数は文字列で渡すのが基本で、ファイルパス・日付・フラグなどを受け取れる形にしておくとスクリプト側が柔軟に制御できます。
VBScriptでCOMオートメーション:最短・定番の外部呼び出し
Excelを起動し、Application.Runで入口Subを呼ぶ
VBScriptはWindows標準で動きます。Excelを不可視で開き、マクロを実行して保存・終了します。引数はApplication.Runで渡せます。
' run_export.vbs(ダブルクリック可/タスクスケジューラも容易)
Option Explicit
Dim xl, wb, bookPath, inPath, outCsv, yyyymmdd
bookPath = CreateObject("Scripting.FileSystemObject").GetAbsolutePathName(".") & "\Book1.xlsm"
inPath = CreateObject("Scripting.FileSystemObject").GetAbsolutePathName(".") & "\input.csv"
outCsv = CreateObject("Scripting.FileSystemObject").GetAbsolutePathName(".") & "\output.csv"
yyyymmdd = Year(Date) & Right("0" & Month(Date), 2) & Right("0" & Day(Date), 2)
Set xl = CreateObject("Excel.Application")
xl.Visible = False
xl.DisplayAlerts = False
Set wb = xl.Workbooks.Open(bookPath)
xl.Run "Book1.xlsm!Run_DailyExport", inPath, outCsv, yyyymmdd
wb.Close True ' 保存して閉じる
xl.Quit
Set wb = Nothing
Set xl = Nothing
VB重要ポイントの深掘り
「ブック修飾つきのマクロ名(Book!ModuleOrPublicSub)」で呼ぶのが安定します。Visible=Falseでヘッドレス、DisplayAlerts=Falseでダイアログ抑止。終了時にClose→Quit→参照解放まで確実に行えば、バックグラウンド実行でもリソースが残りません。
PowerShellでCOMオートメーション:引数や日付操作が柔軟
Excelを非表示で開いてRunを呼び、最後に解放
PowerShellは文字列・日時・パス操作が楽です。引数を柔軟に組み立てて渡せます。
# run_export.ps1
$bookPath = Join-Path (Get-Location) "Book1.xlsm"
$inPath = Join-Path (Get-Location) "input.csv"
$outCsv = Join-Path (Get-Location) "output.csv"
$yyyymmdd = (Get-Date).ToString('yyyyMMdd')
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $false
$xl.DisplayAlerts = $false
$wb = $xl.Workbooks.Open($bookPath)
$xl.Run("Book1.xlsm!Run_DailyExport", $inPath, $outCsv, $yyyymmdd)
$wb.Close($true)
$xl.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl) | Out-Null
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
重要ポイントの深掘り
PowerShellはCOM解放を明示できるため、長期運用でも安定です。ReleaseComObject→GCの流れを入れるとプロセス残りを防ぎやすくなります。タスクスケジューラから直接呼べるのも利点です。
起動イベントで自動実行:Excel起動時に走らせる
Workbook_OpenやAuto_Openを使う(簡易だが運用要件に注意)
Excelを開いたタイミングで自動実行させたい場合に使います。スクリプトでブックを開くだけで処理が走ります。
' ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
On Error Resume Next
Run_DailyExport ThisWorkbook.Path & "\input.csv", ThisWorkbook.Path & "\output.csv", Format(Date, "yyyymmdd")
On Error GoTo 0
End Sub
VB重要ポイントの深掘り
開いたら「必ず動く」ため、開発中や手作業と相性が悪い場面があります。業務では、起動イベントよりCOMから明示的にRunで呼ぶ方が事故が少ないです。使う場合は「フラグセル」や「引数要求」で条件付き実行にするのが安全です。
タスクスケジューラ連携:時間指定で自動起動する
VBS/PS1をスケジュール登録し、夜間・早朝に回す
WindowsタスクスケジューラにVBScriptやPowerShellを登録すれば、Excelを開かずとも自動運用できます。アカウントのログオン/最上位の特権・実行時フォルダ・ネットワークパス権限などの前提を確認し、失敗時の再試行・ログ保存パスをセットしておくと安心です。
重要ポイントの深掘り
ExcelはGUIアプリのため、完全なサーバー常駐用途には不向きです。とはいえ、単純バッチならタスクスケジューラ+COMで十分に運用可能です。出力先はローカル/権限のある共有を選び、ファイルロックや競合を避ける運用ルールを添えるとトラブルを減らせます。
引数と結果の受け渡し:外部→VBA→外部の往復を整える
引数は文字列で渡し、VBA側で変換・検証する
Application.Runは複数引数を渡せます。VBA側で型検証(IsDate/IsNumeric)を行い、誤引数ならFail Fastでログに残しつつ終了します。複雑な設定はJSON/CSV/Configシートを読むようにし、外部は「パスだけ渡す」方針が安全です。
結果はファイル/セル/ログへ出す
ヘッドレスではメッセージを使いません。結果はCSV/Excel出力、Logシート、外部ログファイル(ADODB.StreamでTXT)へ残す形にします。外部スクリプト側は終了コードの代わりに「結果ファイル存在」や「ログ末尾」を見て成功判定できます。
実務の落とし穴と対策(深掘り)
マクロセキュリティでブロックされる
信頼済みの場所にブックを置く、または署名を付けると安定します。企業環境ではITポリシーに従い、実行許可の前提を確認してください。
ダイアログで処理が止まる
DisplayAlerts=Falseにして上書き確認を抑止。自動保存が絡む処理は「SaveCopyAs」で別名保存にするなど、ダイアログ不要の設計にします。
Excelが終了しない/プロセスが残る
Close→Quit→参照解放まで必ず行い、例外時でも必ず到達するように外部スクリプト側もTry/Finally風に組むと残留を防げます。PowerShellならReleaseComObject/GCを併用します。
競合・二重起動
簡易ロック(名前定義 nm_Lock)や一時ファイルロックを入口で取得し、取れなければスキップします。時刻やユーザー名も記録して調査可能にしておくと安心です。
例題の通し方と確認ポイント
入口Subを直接実行して動作を確認する
Excel内で Run_DailyExport を手動実行し、CSVの入出力・ログ記録・枠復帰が正しく動くか確認します。対話不要で完了することが重要です。
VBScript/PowerShellから呼び、Excelが自動で閉じるかを見る
run_export.vbs/run_export.ps1 を実行し、Excelプロセスが終了するか、出力とLogシートが更新されるか確認します。タスクスケジューラに登録して時間実行も試します。
想定外引数・失敗時の挙動を検証する
意図的に不正な日付・存在しないパスで実行し、Fail Fastでログに残して終了できるかを確認します。外部スクリプト側が後片付け(Quit/解放)まで到達することもチェックします。
まとめ:入口の型+COM呼び出し+枠とログで「Excel外起動」を堅牢にする
標準モジュールのRun_XXXXをヘッドレス対応に整え、VBScript/PowerShellからCOMで開く→Run→保存→終了の流れを固定する。開始/終了枠・ログ・引数検証を徹底すれば、夜間バッチや定時取り込みが安定して回ります。

