VBA から VBA を呼び出したかったけどできなかったから、代替案で我慢した

VBA 内で他の ExcelVBA を呼び出したかったのですが(Excel 側のセキュリティを緩めないと)ダメくさいので、代替え案でいいやと思いました。という話です。

目次

本当はこういう構成でやりたかった

もはや bat と PowerShell いらんやんけ~なのですが、要するに VBA から VBA を呼び出したかったんですね。こんなんです。

bat
  → PowerShell
     → Excel/VBA   ★VBA 内でマクロ実行
        → Excel/VBA ★NG, 呼び出せず

で、やってみたらこれですよ。

実行時エラー '1004': マクロ 'C:\~\aaa.xlsm!Hello' を実行できません。このブックでマクロが使用できないか、またはすべてのマクロが無効になっている可能性があります。

これを実行できるようにするには Excel のセキュリティセンターを開いて、マクロの実行レベルを緩めなければいけない模様でした。

代替え案

そんなことはしたくないので、運用(というよりは別の実現方法)でなんとかカバーできないかと、方向性を変えました。で、PowerShell からのマクロ実行は可能だったので、以下のように変えてみました。

bat
  → PowerShell
     → Excel/VBA ★PowerShellからマクロ実行、再計算させる
     → Excel/VBA ★PowerShellからマクロ実行、更新されたセル値を読み込む

以下ソースです。

run.bat

@echo off
echo PowerShell から VBA を実行しています...
powershell -NoProfile -ExecutionPolicy Unrestricted .\test.ps1

echo 処理が完了しました!...
pause

test.ps1

$file1 = "C:\Users\User\~\test1.xlsm"
$file2 = "C:\Users\User\~\test2.xlsm"


$excel = New-Object -ComObject Excel.Application
try {
    # 計算処理させる
    $book = $excel.Workbooks.Open($file1)
    $excel.Run("Calc", "2", "3")
    $book.Save()
    $book.Close()
    
} finally {
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($excel) | Out-Null
}

$excel = New-Object -ComObject Excel.Application
try {
    # test2.xlsm 側で、test1.xlsm を読み込み結果データの値を取得
    $book = $excel.Workbooks.Open($file2)
    
    # Run は2回目はだめくさい?
    # $excel.Run("SetData")
    $result = $excel.Run("GetData")
    
    $book.Save()
    $book.Close()
    
    write-Host $result
    
} finally {
    $excel.Quit()
    [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($excel) | Out-Null
}

test1.xlsm

Sub Calc(ByVal i As String, ByVal j As String)
    
    Range("A1").Value = CLng(i) + CLng(j)
    
End Sub

test2.xlsm

Function GetData()
    
    Dim bok1 As Workbook
    Dim sht1 As Worksheet
    Dim bok2 As Workbook
    Dim sht2 As Worksheet
    Dim path As String
    
    path = ThisWorkbook.path + "\test1.xlsm"
    Set bok1 = Workbooks.Open(path)
    Set sht1 = bok1.Sheets("Sheet1")
    
    path = ThisWorkbook.path + "\test2.xlsm"
    Set bok2 = Workbooks.Open(path)
    Set sht2 = bok2.Sheets("Sheet1")
    
    sht2.Range("A1").Value = sht1.Range("A1").Value
    ' MsgBox sht2.Range("A1").Value
    GetData = sht2.Range("A1").Value
    
    Set sht1 = Nothing
    Set bok1 = Nothing
    Set sht2 = Nothing
    Set bok2 = Nothing
    
End Function

出力結果

PowerShell で、2 と 3 を渡して、test1.xlsm を経由して test2.xlsm に渡って、計算結果を 元の PowerShell で受け取れました!やったー!

PowerShell から VBA を実行しています...
5
処理が完了しました!...
続行するには何かキーを押してください . . .