VBAで開発する人のための汎用プロシージャ9選

[こちらの記事：個人マクロブック等に保存して利用するVBAの汎用プロシージャ](https://qiita.com/nanaco0424/items/709b58f422860bbbef30)で個人マクロブックやアドインにコードを記載して、アクティブブック等に対して利用する汎用プロシージャを紹介しました。

「個人マクロブック等に保存して利用するVBAの汎用プロシージャ」の記事は、開発する人以外にも配布することで、便利に利用していただけます。
※基礎的なマクロの実行方法等は、利用者に最低限、身につけていただく必要はありますが…。

今回の記事は、利用する人というよりも、VBAの開発する人にとって便利なコードを紹介します。（一部は、「個人マクロブック等に保存して利用するVBAの汎用プロシージャ」で紹介している記事と重複します。）
なお、自分が作成したものではないものについては、作成者様のサイトのリンクをご紹介いたしますので、そちらで実際のコードをご確認ください。

# 1. セル参照のA1形式・R1C1形式の表示を切り替えるマクロ
Excelの表示設定では普段は「A1形式」を利用している方が多数派だと思います。
しかし、VBAの開発を行っているとアルファベットで表記されている列が何列目かを整数で知りたい場合があります。
そういうときには、「R1C1形式」に切り替えするのですが、リボンにマクロを登録してワンタッチでA1形式・R1C1形式を切り替えできるようにしておくと便利です。
[【コード】amacoda blog－セル参照のA1形式・R1C1形式の表示を切り替えるマクロ](https://amacoda.net/blog/2018/09/toggle_reference_style_a1_to_from_r1c1/)

# 2. 画面更新や自動計算を停止・再開するマクロ
どんなプロシージャも、高速化のために画面更新や自動計算は停止することが多いと思います。例えば、下記のように。

<details open><summary>コード</summary>

```vb:ExcelVBA

Sub sbプロシージャ例()

    '初期処理
    Application.ScreenUpdating = False                  '画面更新の停止
    Application.Calculation = xlCalculationManual       '自動計算の停止（手動計算）
    Application.DisplayAlerts = False                   '画面警告の表示の停止
    Application.EnableEvents = False                    'イベント機能の停止
    'メイン処理
    
    '終了処理
    Application.ScreenUpdating = True                   '画面更新の開始
    Application.Calculation = xlCalculationAutomatic    '自動計算開始
    Application.DisplayAlerts = True                    '画面警告の表示の開始
    Application.EnableEvents = True                     'イベント機能の開始

End Sub
```
</details>

『初期処理』や『終了処理』等のコメントを記載しないとしても、行数が8行もありコードが長くなる原因になります。
そのため、画面更新の停止や開始等をまとめて設定する下記のようなプロシージャをマクロ有効ブックに保存しておきます。
この汎用プロシージャを利用すれば、メインプロシージャの方で記載する際の行数は8行から2行に減らすことができます。

<details open><summary>コード</summary>

```vb:ExcelVBA
Public Sub sbSetting(flg As Boolean)
  If flg Then
    Application.ScreenUpdating = False                  '画面更新の停止
    Application.Calculation = xlCalculationManual       '自動計算の停止（手動計算）
    Application.DisplayAlerts = False                   '画面警告の表示の停止
    Application.EnableEvents = False                    'イベント機能の停止
  Else
    Application.ScreenUpdating = True                   '画面更新の開始
    Application.Calculation = xlCalculationAutomatic    '自動計算開始
    Application.DisplayAlerts = True                    '画面警告の表示の開始
    Application.EnableEvents = True                     'イベント機能の開始
  End If
End Sub
 
Sub sbSettingの使用例()
    Call sbSetting(True)  '画面更新OFF等の初期設定ON
    'メイン処理
    Call sbSetting(False) '画面更新OFF等の初期設定OFF
End Sub
```
</details>

よく利用するコードを別のプロシージャに細分化する話は、[こちらの記事](https://qiita.com/nanaco0424/items/4b3e9edd5d42bbf22a9d)でもう少し詳しく解説しています。ご興味があればこちらもご一読ください。

# 3. 選択範囲のセルのサイズ・位置にマクロボタンを挿入する
選択しているセルのサイズと位置に、マクロボタンのオブジェクトを挿入します。
一つのセルだけではなく、複数セルを選択している場合は、複数セルのサイズでマクロボタンが挿入されます。

<details open><summary>コード</summary>

```vb:ExcelVBA
Sub sbマクロボタン作成()
    With ActiveSheet.Buttons.Add(Selection.Left, Selection.Top, Selection.Width, Selection.Height)
      '.OnAction "sbダミー" '実行されるマクロの指定
      .Font.Name = "Meiryo UI"
      .Font.Size = 14
      .Characters.text = "ボタン"
    End With
End Sub
```
</details>

# 4. Enumをセルの選択範囲から自動作成する
Enumは整数のみを取り扱うことができる定数の集合体です。
Excelのデータにあるヘッダーを範囲選択した状態にして、マクロを実行することで、Enumを自動的に作成するコードです。
マクロを実行後に、Enumのコードがクリップボードに格納されているので、VBE画面でペーストすれば、任意の箇所にEnumのコードを貼り付けすることができます。

<details open><summary>コード</summary>

```vb:ExcelVBA
Sub sb選択範囲よりEnum自動作成しクリップボードへ出力()
'用途：Excelのデータのヘッダーを選択してEnumをクリップボードに出力する
 
    Dim myMsg As String 'メッセージボックス用変数
    'enumの名前をmsgboxで入力
    Dim enumName As String
    enumName = InputBox("enumの名前を入力してください。", "enum作成")
   
    'enumの名前が空白の場合は終了
    If enumName = "" Then
        MsgBox "enumの名前が入力されませんでした。", vbExclamation, "enum作成"
        Exit Sub
    End If
   
    'データベースのヘッダーを範囲選択
    Dim rng As Range
    Set rng = Selection
   
    '列数と行数を取得
    Dim colNum  As Long
    Dim rowNum  As Long
    colNum = rng.Columns.Count
    rowNum = rng.Rows.Count

    '選択範囲の最左列を取得
    Dim startCol As Long
    startCol = rng.Column '選択範囲の左端の列番号
   
    'enumの値をヘッダーから取得
    Dim enumValue  As String
    Dim i          As Long
    Dim j          As Long
    Dim k          As Long: k = 1
    Dim isFirst    As Boolean '最初の要素かどうかを判定する変数
    enumValue = ""
    isFirst = True '最初の要素のフラグをTrueにする
   
    If rowNum > 1 Then GoTo Continue  '選択している行が2行の場合には処理中止
   
    For i = 1 To colNum
        For j = 1 To rowNum
            '空白のセルは無視
            If rng.Cells(j, i).Value <> "" Then
               
                '括弧（）などの記号は使用できないため、『_（アンダースコア）』に変換するようにコード改修
                '※閉じる方『）』は、空白に置換
                Dim cellValue As String
                cellValue = rng.Cells(j, i).Value
                cellValue = Replace(cellValue, "(", "_")
                cellValue = Replace(cellValue, ")", "")
                cellValue = Replace(cellValue, "（", "_")
                cellValue = Replace(cellValue, "）", "")
                cellValue = Replace(cellValue, "【", "_")
                cellValue = Replace(cellValue, "】", "")
                cellValue = Replace(cellValue, "/", "_")
                cellValue = Replace(cellValue, "・", "_")
                cellValue = Replace(cellValue, "･", "_")
                cellValue = Replace(cellValue, "-", "_")
               
                If isFirst Then '最初の要素の場合は、値の後に「=startCol」を追加する（startColは選択範囲の左端の列数）
                    enumValue = enumValue & vbTab & cellValue & " =" & startCol & vbNewLine
                    isFirst = False '最初の要素のフラグをFalseにする
                ElseIf i = colNum Then '最終列なら
                    enumValue = enumValue & vbTab & cellValue & vbNewLine
                    enumValue = enumValue & vbTab & "[_最終項目]" & vbTab & "'疑似的最終項目" & vbNewLine
                    enumValue = enumValue & vbTab & "Count = [_最終項目] - 1" & vbNewLine
                Else '最初の要素以外の場合
                    enumValue = enumValue & vbTab & cellValue & vbNewLine
                End If
                
            ElseIf rng.Cells(j, i).Value = "" Then '空白の場合はインテリセンスが働かないダミー項目名を追加
                enumValue = enumValue & vbTab & "[_dummy_" & k & "]" & vbNewLine
                k = k + 1
            End If
        Next j
    Next i
   
    'enumの値の末尾の改行を削除
    enumValue = Left(enumValue, Len(enumValue) - 2)
   
    'enumのコードを作成
    Dim enumCode As String
    enumCode = "Enum ze" & enumName & vbNewLine & enumValue & vbNewLine & "End Enum" & vbNewLine
   
    'enumのコードをクリップボードにセット
     sbクリップボードへ文字列セット enumCode
   
    myMsg = "クリップボードにEnumの出力が完了しました。" & vbCrLf + myMsg
    MsgBox myMsg, , "処理結果通知"
    Exit Sub
 
Continue:              'GoTo Continueの後はここから処理が行われる
    myMsg = "選択行が2行以上のため処理を中止しました。"
    MsgBox myMsg, , "処理結果通知"
   
End Sub

Public Sub sbクリップボードへ文字列セット(ByVal a_text As String)
'クリップボードへ文字列を送信
'必要な参照設定：Microsoft Forms 2.0 Object Library
    With CreateObject("Forms.TextBox.1")
      .MultiLine = True
      .Text = a_text
      .SelStart = 0
      .SelLength = .TextLength
      .Copy
    End With
End Sub
```
</details>

より詳しい解説は、下記の記事で行っています。
[【コード】ExcelVBAで定数の代わりにEnum使う & Enumの自動作成](https://qiita.com/nanaco0424/items/19b08c70e5d10946289b)

# 5. VBA用のコードスニペット集からテンプレートを呼び出すマクロ

こちらの記事を参考に、コードスニペット集を作成しています。
- [御子柴 きゅう様‐【VBA】VBEでコードスニペットを扱う仕組み](https://qiita.com/Mikoshiba_Kyu/items/4b3ebba0da8ae7cc6589)
- [t @leau様‐コードスニペット作成マクロ](https://qiita.com/taleau/items/b462ed151566989520ed)

例えば、下記のようなテンプレートを登録し、呼び出して使いたいときに、イミディエイトウィンドウでSNIまで入力してCTRL+スペースで呼び出ししてコードウィンドウへコピーペーストしています。

<details><summary>コードスニペット例１</summary>

```vb:ExcelVBA
Sub SNI初回コードテンプレート()
 
  Debug.Print "Sub sbテンプレート()"
  Debug.Print "'用途  ："                               '用途
  Debug.Print "'作成日：" & Format(Now(), "yyyy/mm/dd") '日付
  Debug.Print "'作成者：" & Application.username        'Officeのユーザー名を取得
  Debug.Print "    "
  Debug.Print "'***********************************************************************************************************************************************"
  Debug.Print "    '初期処理"
  Debug.Print "'***********************************************************************************************************************************************"
  Debug.Print "    Call sbSetting(True)             '画面更新OFF等の初期設定ON"
  Debug.Print "    Dim StartTime As Double          '開始時間（処理時間計算用）"
  Debug.Print "    StartTime = Time                 '開始時間の記録"
  Debug.Print "    "
  Debug.Print "'***********************************************************************************************************************************************"
  Debug.Print "    '主処理"
  Debug.Print "'***********************************************************************************************************************************************"
  Debug.Print ""
  Debug.Print ""
  Debug.Print ""
  Debug.Print ""
  Debug.Print "'***********************************************************************************************************************************************"
  Debug.Print "    '終了処理"
  Debug.Print "'***********************************************************************************************************************************************"
  Debug.Print "    Dim EndTime As Double            '終了時間（処理時間計算用）"
  Debug.Print "    EndTime = Time                   '終了時間の記録"
  Debug.Print "    Call sbSetting(False)            '画面更新OFF等の初期設定OFF"
  Debug.Print "    Dim myMsg As String 'メッセージボックス用変数"
  Debug.Print "    myMsg = ""処理が終了しました。"" & vbCrLf"
  Debug.Print "    myMsg = myMsg + Format(StartTime - EndTime, ""h時間mm分ss秒"") & vbCrLf"
  Debug.Print "    MsgBox myMsg, , ""処理結果通知"""
  Debug.Print ""
  Debug.Print "End Sub"
 
End Sub
```
</details>
<details><summary>コードスニペット例2</summary>

```vb:ExcelVBA
Sub SNIテストモード()
 
  Debug.Print "    'テストモードを指定"
  Debug.Print "    Dim isTesting As Boolean"
  Debug.Print "    isTesting = False"
  Debug.Print ""
  Debug.Print "    If isTesting Then Debug.Print ""テスト"""
 
End Sub
```
</details>
<details><summary>コードスニペット例3</summary>

```vb:ExcelVBA
Sub SNI全シートループ()
  Debug.Print "Sub sb全シート〇〇()"
  Debug.Print "    Dim wb As Workbook"
  Debug.Print "    Dim ws As Worksheet"
  Debug.Print "   "
  Debug.Print "    Call sbSetting(True) '画面更新OFF等の初期設定ON"
  Debug.Print "   "
  Debug.Print "    ' 現在のアクティブワークブックを取得"
  Debug.Print "    Set wb = ActiveWorkbook"
  Debug.Print "   "
  Debug.Print "    ' 各ワークシートに対してループ"
  Debug.Print "    For Each ws In wb.Worksheets"
  Debug.Print "        "
  Debug.Print "    Next ws"
  Debug.Print "   "
  Debug.Print "    Call sbSetting(False)  '画面更新OFF等の初期設定OFF"
  Debug.Print "   "
  Debug.Print "    Dim myMsg As String 'メッセージボックス用変数"
  Debug.Print "    myMsg = ""処理が終了しました。"""
  Debug.Print "    MsgBox myMsg, , ""処理結果通知"""
  Debug.Print "End Sub"
End Sub
```
</details>

# 6. アクティブブックの標準モジュールをすべてエクスポートする
アクティブブックのすべての標準モジュールをブックと同じフォルダへエクスポートするマクロです。個人マクロブックやアドインにプロシージャを保存して利用する前提です。
マクロ有効ブック内に複数の標準モジュールがある場合に、１つ１つエクスポートするのは面倒なので、一括でエクスポートします。標準モジュールをエクスポートするのは、バックアップ等の目的です。
<details open><summary>コード</summary>

```vb:ExcelVBA
Sub sbアクティブブックの標準モジュールをすべて出力()
'※1 エクスポートする前に、Excelの設定で「VBAプロジェクトオブジェクトモデルへのアクセスを信頼する」にチェックを入れる必要がある。
'※2 VBA画面のツールメニューから「Microsoft Visual Basic for Application Extensibility 5.3」に参照設定をする必要がある。
    Dim module       As vbComponent  ' モジュール
    Dim moduleFound  As Boolean      ' 標準モジュールが見つかったフラグ
    Dim extension    As String       ' モジュールの拡張子
    Dim sPath        As String       ' 処理対象ブックのパス
    Dim sFilePath    As String       ' エクスポートファイルパス
    Dim TargetBook   As Workbook     ' 処理対象ブックオブジェクト
   
    ' アクティブブックを対象とする
    Set TargetBook = ActiveWorkbook
   
    sPath = TargetBook.Path ' 処理対象ブックのパスを取得
   
   '標準モジュールが見つかったかどうかのフラグを初期化
    moduleFound = False
   
    ' VBAプロジェクトに含まれる全てのモジュールをループ
    For Each module In TargetBook.VBProject.VBComponents
        ' 標準モジュールのみを対象
        If module.Type = vbext_ct_StdModule Then
            ' モジュールの拡張子を設定
            extension = ".bas"
            ' エクスポートファイルパスを設定
            sFilePath = sPath & "\" & module.Name & extension
            ' モジュールをエクスポート
            module.Export sFilePath
            ' 標準モジュールが見つかったフラグを設定
            moduleFound = True
        End If
    Next
   
    ' 標準モジュールが見つからなかった場合、メッセージを表示する
    If Not moduleFound Then
        MsgBox "対象の標準モジュールがありませんでした。", , "処理結果通知"
    Else
        MsgBox "処理が終了しました。", , "処理結果通知"
    End If
   
End Sub
```

</details>

# 7. 個人マクロブックの標準モジュールをすべてエクスポートする
6.の個人マクロブック版で、個人マクロブックのすべての標準モジュールをダウンロードフォルダへエクスポートするマクロです。個人マクロブックやアドインにプロシージャを保存して利用する前提です。

<details open><summary>コード</summary>

```vb:ExcelVBA

Sub sb個人マクロブックの全標準モジュール出力()
'個人マクロブックのすべての標準モジュールをダウンロードフォルダへエクスポートする
'※1 エクスポートする前に、Excelの設定で「VBAプロジェクトオブジェクトモデルへのアクセスを信頼する」にチェックを入れる必要がある。
'※2 VBA画面のツールメニューから「Microsoft Visual Basic for Application Extensibility 5.3」に参照設定をする必要がある。
    Dim module      As vbComponent  'モジュール
    Dim extension   As String       'モジュールの拡張子
    Dim sPath       As String       '処理対象ブックのパス
    Dim sFilePath   As String       'エクスポートファイルパス
    Dim TargetBook  As Workbook     '処理対象ブックオブジェクト
   
    ' 個人用マクロブックを対象とする
    Set TargetBook = ThisWorkbook
   
    'sPath = TargetBook.Path ' 処理対象ブックのパスを取得
    sPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
    sPath = Replace(sPath, "\Desktop", "\Downloads", 1, 1)  ' ダウンロードフォルダに変更
    sPath = Replace(sPath, "\OneDrive", "", 1, 1)   'OneDriveを利用している場合はそのままだと「C:\Users\dummy\OneDrive\Downloads」となりアクセスできないため
    
    ' VBAプロジェクトに含まれる全てのモジュールをループ
    For Each module In TargetBook.VBProject.VBComponents
        ' 標準モジュールのみを対象
        If module.Type = vbext_ct_StdModule Then
            ' モジュールの拡張子を設定
            extension = ".bas"
            ' エクスポートファイルパスを設定
            sFilePath = sPath & "\" & module.Name & extension
            ' モジュールをエクスポート
            module.Export sFilePath
        End If
    Next
     MsgBox "処理が終了しました。", , "処理結果通知"
End Sub
```
</details>

# 8. 標準モジュールを置換するマクロ

例ではアクティブブックで使用する前提で記載していますが、マクロ有効ブックに記載して、ThisWorkbook.Pathで利用することが多いです。
6～7等の方法でエクスポートした標準モジュールのバックアップ等を一括で取り込みます。
同じ内容の標準モジュールを利用しているマクロ有効ブックがある場合に、コードを更新したときに、他のマクロ有効ブックにも変更内容を反映させるのに便利です。
（共通の標準モジュールを利用していることが前提です。）

<details open><summary>コード</summary>

```vb:ExcelVBA
Sub sb標準モジュール一括置換()
'アクティブブックの標準モジュールを、所定フォルダ内から一括で置き換え（元々ない標準モジュールは新規インポート）
'必要な参照設定：「Microsoft Scripting Runtime」（FileSystemObjectを使用するために必要）
    Dim fso As Scripting.FileSystemObject
    Dim fldr As Scripting.Folder
    Dim fl As Scripting.File
    Dim targetPath As String
    Dim vbProj As Object
    Dim vbComp As Object
    Dim moduleName As String
 
    'レファレンス設定でMicrosoft Scripting Runtimeを追加
    Set fso = New Scripting.FileSystemObject
   
    '入れ替えたい標準モジュールの保存場所★可変箇所★
    targetPath = "C:\Users\dummy\Documents\標準モジュールインポート"
   
    '選択したフォルダ内の.BASファイルのみ扱う。
    Set fldr = fso.GetFolder(targetPath)
   
    'アクティブブックのVBProjectを取得する。
    Set vbProj = ActiveWorkbook.VBProject
 
    '指定したフォルダ内の各.BASファイルについて処理を行う。
    For Each fl In fldr.Files
        If Right(fl.Name, 4) = ".bas" Then
            moduleName = Left(fl.Name, Len(fl.Name) - 4)
           
            'もし既に同じ名前のモジュールが存在するならば、それを削除する。
            For Each vbComp In vbProj.VBComponents
                If vbComp.Name = moduleName Then
                    vbProj.VBComponents.Remove vbComp
                    Exit For
                End If
            Next vbComp
           
            '新たなモジュールをインポート
            vbProj.VBComponents.Import targetPath & "\" & fl.Name
           
        End If
    Next fl
   
    Dim myMsg As String 'メッセージボックス用変数
    myMsg = "処理が終了しました。"
    MsgBox myMsg, , "処理結果通知"
   
End Sub
```

</details>

# 9. アクティブなマクロボタンに表示するテキストを入力するマクロ
マクロボタンのテキストを編集したいことがあると思います。
しかし、横方向にウィンドウ枠の固定をしている場合は、マクロボタン内のテキストにカーソルが表示されない、という事象が発生します。

![スクリーンショット 2024-10-19 165639.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3479442/16fbb5bc-a089-f9ea-a1b3-3522de6a0ba0.png)

この場合は、テキストをうまく編集しようとしても、カーソルの位置が視認できないため、編集が難しいです。
ウィンドウ枠の固定を解除すれば、カーソルの位置が見えるようになりますが、いちいちウィンドウ枠の固定を解除するのが難しい場合は、
アクティブなマクロボタンのテキストを、インプットボックスに入力したテキストへ変更するマクロです。
<details open><summary>コード</summary>

```vb:ExcelVBA
Sub sbマクロボタンへテキスト入力()
'アクティブなマクロボタンのテキストを、インプットボックスに入力したテキストにする

    Dim shpRange  As ShapeRange
    Dim myRange   As Shape
   
    If (TypeName(Selection) <> "Button") Then  'マクロボタン判定
        MsgBox "選択しているものがマクロボタンではないか、複数選択しています。", , "処理終了通知"
        Exit Sub
    End If
    
    Dim defaultText As String
    defaultText = Selection.Characters.text   '元々入力されているテキスト初期値としてセット
    
    Dim zMacroText As Variant     '連番を開始するアクティブセルの番号をInputBoxで入力するための変数（キャンセル時にFalseとなるためVariant型）
    zMacroText = Application.InputBox( _
              PROMPT:="マクロボタンに表示するテキストを入力してください。", _
              TITLE:="マクロボタンテキスト入力", _
              Default:=defaultText, _
              Type:=2)
    If TypeName(zMacroText) = "Boolean" Then
        MsgBox "マクロ実行をキャンセルします"
        Exit Sub
    End If
   
    With Selection
      '.OnAction "sbダミー" '実行されるマクロの指定
'      .Font.Name = "Meiryo UI"
'      .Font.Size = 14
      .Characters.text = zMacroText
    End With
 
End Sub
```

</details>

以上、いかがでしたでしょうか？お役に立てたなら幸いです。