【VBA】マクロから別のマクロを呼び出して処理を分割する方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク

完成イメージ

Before(1つの長いマクロ)


Sub 長いマクロ()
    ' データ取得の処理(30行)
    ' 加工の処理(50行)
    ' 出力の処理(40行)
    ' エラー処理(20行)
    ' → 合計140行、どこに何があるか分かりにくい
End Sub

After(処理を分割して呼び出し)


Sub メイン処理()
    Call データ取得      ' ← 30行の処理を1行で呼び出し
    Call データ加工      ' ← 50行の処理を1行で呼び出し
    Call 結果出力        ' ← 40行の処理を1行で呼び出し
End Sub
→ メイン処理はたった5行。各処理の中身は別のSubに書かれている
→ 修正したい処理だけ見ればよい

実務版の動作イメージ


マクロ実行(MainProcess)
→ 高速化設定ON
→ Call GetData(A列からデータを配列に読み込む)
→ Call ProcessData(データを加工する)
→ Call OutputResult(加工結果をB列に出力する)
→ 高速化設定OFF
→ 「完了(0.45秒)4件処理しました」とメッセージ表示
※ エラーが発生しても設定は自動で元に戻る

実行前の準備

バックアップを取る

処理の分割自体はデータを破壊する操作ではありませんが、実務版ではB列にデータを書き込みます。重要なブックは先にバックアップを取ってください。

Excelをマクロ有効ブック(.xlsm)で保存する

  1. Excelを開く(新規でも既存でもOK)
  2. 「ファイル」→「名前を付けて保存」
  3. ファイルの種類を 「Excel マクロ有効ブック (*.xlsm)」 に変更して保存

.xlsx のままだとマクロが保存されない。必ず .xlsm にすること。

テスト用データを用意する

A列にテスト用のデータを入力する。1行目はヘッダー、2行目以降がデータ。

A列(1行目) A列(2行目以降)
名前 田中太郎
佐藤花子
鈴木一郎
高橋美咲

手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

Alt + F11 キーを押すとVBE(Visual Basic Editor)が開く。

一般的にはAlt + F11で開けるが、企業のセキュリティ設定でVBAが無効化されている場合は、IT部門に確認すること。

標準モジュールを挿入する

  1. VBEのメニュー「挿入」→「標準モジュール」をクリック
  2. 右側に白い画面(コードウィンドウ)が表示される

コードを貼り付けて実行する

  1. 下の「コード(最小版)」をコピーして、コードウィンドウに貼り付ける
  2. Alt + F8 を押す(または VBE上で F5
  3. マクロ名「MainSample」を選択して「実行」

コード(最小版)— Callで別のSubを呼び出す

まずはこれだけで動く。Call で別のSubプロシージャを呼び出すだけのシンプルな例。


Sub MainSample()

    ' 処理1を呼び出す(引数なし)
    Call ShowStep1

    ' 処理2を呼び出す(引数あり)
    Call ShowStep2("VBA")

    ' 処理3を呼び出す(戻り値あり → Function)
    Dim result As String
    result = MakeGreeting("田中")

    MsgBox "処理3の結果:" & result

End Sub


' === 処理1:引数なしのSub ===
Sub ShowStep1()
    MsgBox "処理1が実行されました。"
End Sub


' === 処理2:引数ありのSub ===
Sub ShowStep2(ByVal keyword As String)
    MsgBox "処理2が実行されました。キーワード:" & keyword
End Sub


' === 処理3:戻り値ありのFunction ===
Function MakeGreeting(ByVal personName As String) As String
    MakeGreeting = personName & "さん、こんにちは!"
End Function

ポイント:

  • Call ShowStep1 — 別のSubプロシージャを呼び出す。Callキーワードは省略可能だが、明示した方が「ここで別の処理を呼んでいる」と一目で分かるのでおすすめ
  • Call ShowStep2("VBA") — 引数を渡して呼び出す。Call付きの場合は引数を括弧で囲む。Call無しなら ShowStep2 "VBA" と括弧を外す
  • result = MakeGreeting("田中") — Functionは戻り値を返す。変数で受け取れる
  • ByVal は「値渡し」。元の変数の値は変わらない(安全)
  • 3つのプロシージャはすべて同じモジュールに貼り付ければ動く

注意:この最小版にはエラー復帰処理(On Error GoTo)が含まれていない。 本番環境では、次の「実務版」を使うことを推奨する。エラー処理の基本は[記事022(エラー処理で止まらないマクロを作る方法)]を参照。


コード(実務版)— メイン→取得→加工→出力の4段階分割マクロ

実務で使う分割テンプレート。メイン処理が「何をするか」を制御し、各サブプロシージャが「どうやるか」を担当する。高速化設定([記事078])とエラー復帰処理を備えている。


' ============================================================
' 処理分割テンプレート(実務版)
' - メイン→データ取得→加工→出力の4段階構成
' - 高速化設定(ScreenUpdating等)対応
' - エラー時も安全に復帰
' ============================================================

Sub MainProcess()

    Dim ws As Worksheet
    Dim lastRow As Long
    Dim dataArr As Variant
    Dim startTime As Double

    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    ' データ存在チェック
    If lastRow < 2 Then
        MsgBox "データがありません(A列の2行目以降にデータを入力してください)。", vbExclamation
        Exit Sub
    End If

    startTime = Timer

    ' --- 高速化ON ---
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    On Error GoTo ErrorHandler

    ' --- STEP1:データ取得 ---
    ' Application.StatusBar = "STEP1: データ取得中..."  ' 進捗表示(記事026参照)
    dataArr = GetData(ws, lastRow)

    ' --- STEP2:データ加工 ---
    ' Application.StatusBar = "STEP2: データ加工中..."  ' 進捗表示(記事026参照)
    Call ProcessData(dataArr)

    ' --- STEP3:結果出力 ---
    ' Application.StatusBar = "STEP3: 結果出力中..."  ' 進捗表示(記事026参照)
    Call OutputResult(ws, dataArr)

CleanUp:
    ' --- 高速化OFF(エラー時もここに来る) ---
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    Application.StatusBar = False  ' ステータスバーを既定に戻す

    If Err.Number = 0 Then
        MsgBox "完了(" & Format(Timer - startTime, "0.00") & " 秒)" & vbCrLf & _
               (lastRow - 1) & " 件処理しました。", vbInformation
    End If
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました:" & vbCrLf & _
           "エラー番号:" & Err.Number & vbCrLf & _
           "内容:" & Err.Description, vbCritical
    Resume CleanUp

End Sub


' ============================================================
' STEP1:データ取得(Function — 戻り値で配列を返す)
' A列のデータを配列に読み込んで返す
' ============================================================
Function GetData(ByVal ws As Worksheet, ByVal lastRow As Long) As Variant

    Dim arr() As String
    Dim i As Long

    ReDim arr(1 To lastRow - 1)

    For i = 2 To lastRow
        arr(i - 1) = ws.Cells(i, 1).Value
    Next i

    GetData = arr

End Function


' ============================================================
' STEP2:データ加工(Sub — ByRefで配列を直接書き換える)
' 配列内の各データを加工する(例:空白除去+敬称付与)
' ============================================================
Sub ProcessData(ByRef arr As Variant)

    Dim i As Long

    For i = LBound(arr) To UBound(arr)
        ' 加工例:前後の空白を除去し、「様」を付ける
        arr(i) = Trim(arr(i)) & " 様"
    Next i

End Sub


' ============================================================
' STEP3:結果出力(Sub — 加工済みデータをB列に書き込む)
' ============================================================
Sub OutputResult(ByVal ws As Worksheet, ByRef arr As Variant)

    Dim i As Long

    ' ヘッダー出力
    ws.Cells(1, 2).Value = "加工結果"

    ' データ出力
    For i = LBound(arr) To UBound(arr)
        ws.Cells(i + 1, 2).Value = arr(i)
    Next i

End Sub

コードの読み方

意味
Sub MainProcess() メイン処理。全体の流れを制御する「司令塔」
dataArr = GetData(ws, lastRow) STEP1: Functionでデータを取得し、配列で受け取る
Call ProcessData(dataArr) STEP2: 配列をByRefで渡し、加工処理を実行
Call OutputResult(ws, dataArr) STEP3: 加工済み配列をシートに出力
Function GetData(...) As Variant 戻り値ありのFunction。配列を返す
Sub ProcessData(ByRef arr As Variant) ByRef(参照渡し)で配列を受け取り、直接書き換える
Sub OutputResult(ByVal ws ..., ByRef arr ...) 出力先シートと配列を受け取り、B列に書き込む

なお、呼び出し側の変数名(dataArr)と呼び出し先の引数名(arr)は異なっていても問題ない。引数は呼び出し時に位置で対応付けられる。

なぜ分割するのか

比較項目 分割前(1つのSub) 分割後(4つのSub/Function)
コードの長さ 100行以上が1つのSubに メインは20行。各処理は10〜20行
修正のしやすさ 全体を読む必要あり 該当する処理だけ見ればよい
再利用性 他のマクロから使えない GetDataやProcessDataを別のマクロからも呼べる
エラーの切り分け どこで止まったか分かりにくい どのSTEPで止まったか明確
テスト 全体を実行するしかない 各Subを単独で実行してテストできる

ポイント:

  • Functionは戻り値を返す: GetDataは配列を返す。「結果を呼び出し元で受け取りたい」処理はFunctionにする
  • ByRefは参照渡し(意図的に使う): ProcessDataはByRefで配列を受け取り、配列の中身を直接書き換える。メイン側のdataArrも書き換わる。「配列を加工してメインに返す」ことが目的なので、ByRefが適切
  • ByValは値渡し(元を変えたくないとき): 元の値を保持したい場合(例: 計算に使うが元の値は変えたくない)はByValにする
  • メインの MainProcess だけを実行すれば、STEP1→STEP2→STEP3 が順番に呼ばれる

分割したメインマクロにボタンを割り当てれば、ワンクリックで実行できる。ボタンの設定方法は[記事013(マクロをボタン1つで実行する方法)]を参照。

長時間処理で進捗を表示したい場合は、コメントアウトしている Application.StatusBar の行を有効にする。詳しくは[記事026(処理の進捗をステータスバーに表示する方法)]を参照。


マクロ呼び出しの4つの方法

1. Call ステートメント(基本)

同じモジュール内・同じプロジェクト内のSubプロシージャを呼び出す。最も基本的な方法。


' Callあり(引数は括弧で囲む)
Call SubName(arg1, arg2)

' Callなし(引数は括弧なし)
SubName arg1, arg2
  • 使い場面: 同じブック内のSubを呼ぶとき(最も多い)
  • Callの有無: 動作は同じ。Callを付けた方が「ここで別の処理を呼んでいる」と一目で分かるのでおすすめ
  • 括弧ルール: Call付き → 括弧必須。Call無し → 括弧不要。これを間違えると構文エラーになる

2. Function プロシージャ(戻り値あり)

値を返す処理にはFunctionを使う。SubとFunctionの違いは「戻り値があるかどうか」だけ。


' 呼び出し側
Dim result As Long
result = CalcTotal(100, 200)

' 定義側
Function CalcTotal(ByVal a As Long, ByVal b As Long) As Long
    CalcTotal = a + b
End Function
  • 使い場面: 計算結果を返す、判定結果(True/False)を返す、データを配列で返すなど
  • 注意: Function内で 関数名 = 戻り値 を記述しないと、数値なら0、文字列なら””が返る

3. 別モジュールのマクロ呼び出し

同じプロジェクト内であれば、別のモジュールに書かれたプロシージャもそのまま呼び出せる。同名のプロシージャがある場合はモジュール名を指定する。


' 同名がなければモジュール名は不要
Call SubName

' 同名がある場合はモジュール名を指定
Call Module2.SubName
  • 使い場面: 共通部品を別モジュールにまとめるとき(例: 高速化ON/OFF、ログ出力など)
  • おすすめ: 汎用的な処理(SpeedOn/SpeedOff、エラーログ出力など)は専用モジュールに分けると再利用しやすい

4. Application.Run(文字列でマクロ名指定)

マクロ名を文字列で指定して実行する。別ブックのマクロを呼ぶ場合に使う。


' 同じブック内のマクロを文字列で実行
Application.Run "SubName"

' 別ブックのマクロを実行(ブックが開いている必要あり)
Application.Run "'売上管理.xlsm'!月次集計"

' 引数を渡す場合
Application.Run "SubName", arg1, arg2

' Functionの戻り値を受け取る場合
Dim result As Variant
result = Application.Run("CalcTotal", 100, 200)
  • 使い場面: 別ブックのマクロを呼ぶ場合、マクロ名を動的に切り替えたい場合
  • 注意: マクロ名のスペルミスや対象ブックが閉じているとランタイムエラーになる
  • ブック名にスペースが含まれる場合: シングルクォーテーションで囲む 'ブック名.xlsm'!マクロ名

複数シートの一括処理で分割を活用する例は[記事015(複数シートを一括処理する方法)]を参照。


よくある落とし穴5選

# 症状 原因 対策
1 「Sub または Function が定義されていません」エラー 呼び出し先のプロシージャ名のスペルミス、またはプロシージャが未定義 プロシージャ名を正確に一致させる。VBEではSub/Function名は自動で大文字小文字が補正されるので、補正されない場合はスペルミスの可能性が高い
2 引数を渡したのに元の変数が勝手に変わった VBAのデフォルトがByRef(参照渡し)のため、呼び出し先で引数の値を変更すると元の変数も変わる 元の値を変えたくない場合は ByVal を明示する。意図的に書き換える場合のみ ByRef を使う
3 Call付きで引数に括弧を付けない(または逆)でエラーになった Call SubName arg は構文エラー。Call付きなら括弧が必須、Call無しなら括弧は不要 Call付き: Call SubName(arg) / Call無し: SubName arg。迷ったらCallを常に使い、括弧を付ける
4 Application.Runで「マクロ ‘xxx’ が見つかりません」エラー マクロ名の文字列が間違っている、または対象ブックが開いていない マクロ名を正確に指定する。別ブックの場合は "'ブック名.xlsm'!マクロ名" 形式で、先にWorkbooks.Openでブックを開いておく
5 Functionの戻り値が常に0や空文字になる Function内で FunctionName = 値 の代入を書き忘れた Function内で必ず 関数名 = 戻り値 を記述する。途中でExit Functionする場合も、その前に代入すること

FAQ

Q1: CallとCall無しの違いは?どちらを使うべき?

動作は同じ。Call SubName(arg)SubName arg は等価。ただしCallを付けた方が「ここで別の処理を呼んでいる」と一目で分かるため、可読性の観点からCallを付けるのがおすすめ。注意点として、Call付きなら引数を括弧で囲む、Call無しなら括弧を外す。

Q2: SubとFunctionはどう使い分ける?

戻り値が必要ならFunction、不要ならSub。判定基準は「呼び出し元で結果を使いたいか」。たとえば「計算結果を返す」「データを配列で返す」「処理の成否(True/False)を返す」ならFunction。「処理を実行するだけ」ならSub。

Q3: 別モジュールのマクロを呼ぶにはモジュール名が必要?

同じプロジェクト内なら、基本的にモジュール名なしで呼べる。モジュール名が必要になるのは、複数のモジュールに同名のプロシージャがある場合のみ。Call Module2.SubName のようにモジュール名をドットで付ける。

Q4: 別のブック(ファイル)のマクロを呼ぶ方法は?

Application.Run "'ブック名.xlsm'!マクロ名" で呼べる。ただし対象ブックが開いている必要がある。先に Workbooks.Open で開いてから呼び、処理後に Workbooks("ブック名.xlsm").Close で閉じるのが定番。エラー処理と組み合わせて安全に実装すること([記事022(エラー処理で止まらないマクロを作る方法)]を参照)。複数ブック間のマクロ連携は業務フローに応じた設計が必要です。ココナラで相談できます。

Q5: マクロを分割すると処理が遅くなる?

ならない。プロシージャ呼び出しのオーバーヘッド(追加の処理時間)は1回あたり数マイクロ秒レベルで、体感できる影響はまずない。むしろ分割した上で高速化設定([記事078(画面更新・再計算を止めてマクロを高速化する方法)])を組み合わせる方が、処理速度も可読性も向上する。


まとめ

この記事では、VBAで別のマクロ(プロシージャ)を呼び出して処理を分割する方法を解説した。

  • 最小版Call SubName で別のSubを呼び出すだけ。Function を使えば戻り値も返せる
  • 実務版:メイン→データ取得→加工→出力の4段階に分割した業務マクロテンプレート

処理の分割は「マクロが長くなったら考えること」の第一歩。1つのSubが50行を超えたら分割を検討するとよい。分割すると読みやすくなるだけでなく、部品の再利用やエラーの切り分けもしやすくなる。

関連記事:

  • [記事022(エラー処理で止まらないマクロを作る方法)] — 分割したマクロでのエラー処理
  • [記事078(画面更新・再計算を止めてマクロを高速化する方法)] — 高速化設定との組み合わせ
  • [記事026(処理の進捗をステータスバーに表示する方法)] — 長時間処理の進捗表示
  • [記事013(マクロをボタン1つで実行する方法)] — メインマクロにボタンを割り当て
  • [記事015(複数シートを一括処理する方法)] — 分割の実用的な活用場面

次にやりたくなること:

  • 分割したマクロを高速化したい → [記事078(画面更新・再計算を止めてマクロを高速化する方法)]
  • エラーが起きても止まらないマクロにしたい → [記事022(エラー処理で止まらないマクロを作る方法)]

もっとカスタマイズしたい場合

「長くなったマクロを整理したい」「複数ブックにまたがるマクロの連携を作りたい」「業務フローに合わせた処理分割を設計したい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できます。

相談時に以下の情報があるとスムーズです:

  • Excel のバージョン / OS
  • 現在のマクロの処理内容(何をしているか)
  • マクロの行数(目安)
  • 分割したい単位(処理の塊ごとか、モジュールごとか)
  • 複数ブック間の連携が必要かどうか

関連記事

コメント

タイトルとURLをコピーしました