Excel VBA VariableProcedureXref

Excel VBA のトップに戻る
Excel VBA 目次

VBA_VariableProcedureXref

索引


VBA_VariableProcedureXref

 デバッグの時に便利なツールです。
 editVBCs で抽出した、ExcelVBAのモジュールやシートから、VBAプログラムて使われている、変数一覧表を作るとともに、それらの変数が、どのサブルーチンやファンクションで使われているかを、対応表示します。

このマクロをダウンロードできます→VBA_VariableProcedureXref.xls

 私が、プログラムを作っていて、デバッグで引っかかる不具合の半分は、変数の処理の部分です。
 使い回す変数の初期化を忘れていたり、あるいは、別の所で、想定外の値が代入されていたりといったものです。
 こんなバグを見つけ易くするために自作しました。重宝しています。


'作成:2006/07/22 渡辺真
'更新:2006/07/24 trimを使って左寄せすることで、あいまいなInStrを減らし、簡単にする。
'更新:2006/07/25 途中からコメント「'」の場合、コメント部を削除。
'                 Sub 名が継続行(右端_)の対応追加。
'更新:2006/08/05 変数の宣言の場所を別表に追加
'更新:2006/08/12 変数の抽出の漏れを追加★★部。Property名取得追加
'更新:2006/08/20 recursive コール(再帰呼出し)の戻り時の変数出力の重複防止
'更新:2013/03/06 このブックのパスをデフォルトにする

Option Explicit

Dim ファイル名一覧(100) As String '一応、扱えるモジュール数は100ファイルとする。
Dim 変数名一覧(1000, 2) As String   '一応、扱える変数数は1,000個とする。
Dim i As Integer
Dim j As Integer
Dim パス名 As String
Dim ファイル数 As Integer
Dim 変数 As Integer
Dim 開始桁 As Integer
Dim 行データ As String
Dim 開始時刻 As Variant
Dim 終了時刻 As Variant
Dim 重複 As Integer
Dim 変数名文字数 As Integer
Dim Sub名 As String
Dim Function名 As String
Dim 直前Sub名 As String
Dim 直前Function名 As String
Dim Property名 As String
Dim 直前Property名 As String
Dim 対応数 As Integer
Dim プロシージャ区分 As String
Dim モジュール名 As String
Dim 変数宣言一覧順位 As Integer


'****************************************************
'*******     ★★   メイン・フロー  ★★     ********
Sub 変数リスト作成()
'****************************************************

    Call ファイル一覧取得
    開始時刻 = Now()
    Application.ScreenUpdating = False '処理速度向上のため画面更新を停止
    Call 変数一覧取得
    Call 変数Sub対応取得
    Application.ScreenUpdating = True '画面更新停止を再開
    終了時刻 = Now()
    MsgBox "処理が終了しました。" & Chr(13) & _
    "処理時間は、" & Format(終了時刻 - 開始時刻, "nn分ss秒") & " でした。", vbOKOnly
End Sub
'********************************************


'*****************★★★*********************
' 指定したフォルダ内のファイルの一覧を取得
Private Sub ファイル一覧取得()
'********************************************

   ChDrive ThisWorkbook.Path
   ChDir ThisWorkbook.Path

    'Excelでお仕事!(Excel全般の解説サイト)
    'http://www.asahi-net.or.jp/~ef2o-inue/
    '「フォルダ内のファイル一覧の取得」VBAの、部分修正版です。

    Const ファイル名ワイルドカード = "\*.*"
    Dim パス桁数 As Integer
    Dim ファイル名 As String

    パス名 = Application.GetOpenFilename("VBAエクスポートモジュール,*.bas;*.cls")
        If パス名 = "False" Then End
'    MsgBox パス名
    For i = 1 To Len(パス名)
        If InStr(i, パス名, "\") <> 0 Then
            パス桁数 = InStr(i, パス名, "\")
        End If
    Next i

    パス名 = Left(パス名, パス桁数)

    ' 先頭のファイル名の取得
    ファイル名 = Dir(パス名 & ファイル名ワイルドカード, vbNormal)                   ' B
    ' ファイルが見つからなくなるまで繰り返す
    ファイル数 = 0
    Do While ファイル名 <> ""                                          ' C
        ' 行を加算
        ファイル数 = ファイル数 + 1       ' 先頭は1行目
        'Cells(行, 1).Value = ファイル名                               ' D
        ファイル名一覧(ファイル数) = ファイル名                               ' D
        ' 次のファイル名を取得
        ファイル名 = Dir()                                             ' E
    Loop

End Sub
'********************************************


'*****************★★★*********************
Private Sub 変数一覧取得()
'********************************************

    変数 = 1
    変数宣言一覧順位 = 0
    プロシージャ区分 = "Module"
    Worksheets("変数宣言一覧").Activate
    Cells.Select
    Selection.ClearContents
    Range("A1").Select
    Range("A1").Value = ""
    Range("B1").Value = "モジュール名"
    Range("C1").Value = "Sub名"
    Range("D1").Value = "Function名"
    Range("E1").Value = "Property名"
    Range("F1").Value = "変数名"

    For i = 1 To ファイル数
        If Right(ファイル名一覧(i), 3) = "bas" Or Right(ファイル名一覧(i), 3) = "cls" Then
            '********************************
            'ファイルのオープン。
            Open パス名 & ファイル名一覧(i) For Input As #1
            '*************データの読み込み***********
            While (EOF(1) = 0)
                Line Input #1, 行データ
                行データ = Trim(行データ)  '2006/07/24★★★★

                    '************************ ここから、コメント削除 ***************
                If Left(行データ, 1) <> "'" And 行データ <> "" Then 'コメント行や空白行は、飛ばす。

                    If InStr(行データ, "'") <> 0 Then
                        行データ = Left(行データ, InStr(行データ, "'") - 2) '途中のコメント以降をカット
                    End If
                    '************************ ここから、Exit行の削除 ***************
                    If Left(行データ, 13) <> "Exit Function" _
                    And Left(行データ, 8) <> "Exit Sub" Then
                    '************************ ここまで、不要行の削除 ***************

                        '************************ ここから、変数宣言の単純出力用 *******
                        Call プロシージャ名取得
                        '************************ ここまで、変数宣言の単純出力用 *******

                        If Left(行データ, 4) = "Dim " Then

                            開始桁 = 5
                            Call 変数名取得
                           'Stop

                        ElseIf Left(行データ, 7) = "Public " _
                        And Left(行データ, 11) <> "Public Sub " _
                        And Left(行データ, 13) <> "Public Const " _
                        And Left(行データ, 16) <> "Public Function " _
                        And Left(行データ, 15) <> "Public Declare " _
                        And Left(行データ, 16) <> "Public Property " Then

                            開始桁 = 8
                            Call 変数名取得

                        ElseIf Left(行データ, 7) = "Global " Then

                            開始桁 = 8
                            Call 変数名取得
                            'Stop

                        ElseIf Left(行データ, 6) = "Const " Then

                            開始桁 = 7
                            If InStr(開始桁, 行データ, " ") <> 0 Then
                                変数名文字数 = InStr(開始桁, 行データ, " ") - 開始桁
                            End If
                            Call 変数名重複チェック

                        End If
                    End If '行データが、「Exit Sub」「Exit Function」以外
                End If      'コメント行以外
            Wend

            '**************終了処理*********************
            Close #1
            'Stop
        End If
    Next i 'モジュールファイルを「ファイル数」だけ読み込み続ける。

    '以下、変数宣言一覧シートの整形
    Worksheets("変数宣言一覧").Activate

    Cells.Select
    Cells.EntireColumn.AutoFit

    Range("A1").Select
    Selection.AutoFilter

    Range("C2").Select
    ActiveWindow.FreezePanes = True
    Range("B2").Select

End Sub
'********************************************


'*************変数宣言の単純出力用***************
Private Sub プロシージャ名取得()
    If Left(行データ, 7) = "End Sub" Or Left(行データ, 12) = "End Function" _
    Or Left(行データ, 5) = "GoSub" Then
        プロシージャ区分 = "Module"

    ElseIf InStr(行データ, "Sub ") <> 0 _
    And Right(行データ, 4) <> " Sub" _
    And InStr(行データ, Chr(34) & "Sub ") = 0 _
    And InStr(行データ, "Sub " & Chr(34)) = 0 Then
    'Subの後ろの空白まで含めてチェックして、subを含む単語を除外。行の最後がSubは対象外。
    ' "Sub や Sub " も除外
        プロシージャ区分 = "Sub"
        開始桁 = InStr(行データ, "Sub") + 4
        Call Sub名取得

    ElseIf InStr(行データ, "Function ") <> 0 _
    And Right(行データ, 9) <> " Function" _
    And InStr(行データ, Chr(34) & "Function ") = 0 _
    And InStr(行データ, "Function " & Chr(34)) = 0 Then

        プロシージャ区分 = "Function"
        開始桁 = InStr(行データ, "Function") + 9
        Call Function名取得

    ElseIf Left(行データ, 16) = "Public Property " Then

        プロシージャ区分 = "Property"
        開始桁 = InStr(行データ, "Property") + 9
        Call Property名取得

    End If
'    Stop
End Sub
'********************************************


'********************************************
Private Sub 変数名取得()
    If InStr(開始桁, 行データ, "(") <> 0 Then
        変数名文字数 = InStr(開始桁, 行データ, "(") - 開始桁 + 1
        '配列変数の場合は、「(」までを変数名として取得する。

    ElseIf InStr(開始桁, 行データ, ",") <> 0 Then

        '変数の宣言を「,」で2つ、つなげている場合の「,」より前。
        If InStr(開始桁, 行データ, " ") < InStr(開始桁, 行データ, ",") Then
            変数名文字数 = InStr(開始桁, 行データ, " ") - 開始桁
        Else
            変数名文字数 = InStr(開始桁, 行データ, ",") - 開始桁
        End If

        Call 変数名重複チェック

        '変数の宣言を「,」で2つ、つなげている場合の「,」より後ろ。

        開始桁 = InStr(開始桁, 行データ, ",") + 2
'        Stop
        Call 変数名取得   'recursive コール(再帰呼出し)

    ElseIf InStr(開始桁, 行データ, " ") <> 0 Then
        変数名文字数 = InStr(開始桁, 行データ, " ") - 開始桁

    Else
        変数名文字数 = Len(行データ) - 開始桁 + 1
    End If

'    Stop
    Call 変数名重複チェック

End Sub
'********************************************


'********************************************
Private Sub 変数名重複チェック()
    重複 = 0
    For j = 1 To 変数
        If 変数名一覧(j, 1) = Mid(行データ, 開始桁, 変数名文字数) Then
            重複 = 1
            Exit For
        End If
    Next j

    If 重複 = 0 Then

        変数名一覧(変数, 1) = Mid(行データ, 開始桁, 変数名文字数)
        変数名一覧(変数, 2) = ファイル名一覧(i)
        変数 = 変数 + 1

    End If

    '★★★以下は変数宣言の単純出力用★★★
    Worksheets("変数宣言一覧").Activate

    '*******************recursive コール(再帰呼出し)の戻り時の重複防止2008/08/20
    If Range("F2").Cells(変数宣言一覧順位, 1).Value <> Mid(行データ, 開始桁, 変数名文字数) _
    Or Range("B2").Cells(変数宣言一覧順位, 1).Value <> ファイル名一覧(i) _
    Or Range("C2").Cells(変数宣言一覧順位, 1).Value <> Sub名 _
    Or Range("D2").Cells(変数宣言一覧順位, 1).Value <> Function名 _
    Or Range("E2").Cells(変数宣言一覧順位, 1).Value <> Property名 _
    Then

        変数宣言一覧順位 = 変数宣言一覧順位 + 1
        Range("A2").Cells(変数宣言一覧順位, 1).Value = 変数宣言一覧順位
        Range("B2").Cells(変数宣言一覧順位, 1).Value = ファイル名一覧(i)

    '    If カッコ削除 = 1 Then 変数名文字数 = 変数名文字数 - 1

        Range("F2").Cells(変数宣言一覧順位, 1).Value = Mid(行データ, 開始桁, 変数名文字数)

    '    カッコ削除 = 0

        Select Case プロシージャ区分
            Case "Module"

            Case "Sub"
                Range("C2").Cells(変数宣言一覧順位, 1).Value = Sub名

            Case "Function"
                Range("D2").Cells(変数宣言一覧順位, 1).Value = Function名

            Case "Property"
                Range("E2").Cells(変数宣言一覧順位, 1).Value = Property名

        End Select
    End If

'    If 変数 > 13 Then Stop
End Sub
'********************************************


'*****************★★★*********************
Private Sub 変数Sub対応取得()
'********************************************

    Dim 検索前対応数 As Integer
    Dim 行 As Integer

    Worksheets("変数とSubの対応表").Activate
    Cells.Select
    Selection.ClearContents
    Range("A1").Select
    Range("A1").Value = ""
    Range("B1").Value = "変数名"
    Range("C1").Value = "モジュール名"
    Range("D1").Value = "Sub名"
    Range("E1").Value = "Function名"
    Range("F1").Value = "Property名"

    対応数 = 1

'    For j = 1 To 5

    For j = 1 To 変数 - 1 '★★★★★★★★★★★
        検索前対応数 = 対応数
        For i = 1 To ファイル数
            If Right(ファイル名一覧(i), 3) = "bas" Or Right(ファイル名一覧(i), 3) = "cls" Then
                '********************************
                'ファイルのオープン。
                Open パス名 & ファイル名一覧(i) For Input As #1
                '*************データの読み込み***********
                行 = 0
                Sub名 = ""
                直前Sub名 = ""
                Function名 = ""
                直前Function名 = ""
                Property名 = ""
                直前Property名 = ""
                While (EOF(1) = 0)

                    Line Input #1, 行データ
                    行データ = Trim(行データ)  '2006/07/24★★★★

                        '************************ ここから、コメント削除 ***************
                    If Left(行データ, 1) <> "'" And 行データ <> "" Then 'コメント行や空白行は、飛ばす。

                        If InStr(行データ, "'") <> 0 Then
                            行データ = Left(行データ, InStr(行データ, "'") - 2) '途中のコメント以降をカット
                        End If
                        '************************ ここまで、コメント削除 ***************

                        行 = 行 + 1

                        'プロシージャ名とプロシージャ区分取得:Sub

                        If Left(行データ, 4) = "Sub " Then
                            開始桁 = 5
                            プロシージャ区分 = "Sub"
                            Call Sub名取得
                        End If

                        If Left(行データ, 12) = "Private Sub " Then
                            開始桁 = 13
                            プロシージャ区分 = "Sub"
                            Call Sub名取得
                        End If

                        If Left(行データ, 11) = "Public Sub " Then
                            開始桁 = 12
                            プロシージャ区分 = "Sub"
                            Call Sub名取得
                        End If

                        'プロシージャ名とプロシージャ区分取得:Function

                        If Left(行データ, 9) = "Function " Then
                            開始桁 = 10
                            プロシージャ区分 = "Function"
                            Call Function名取得
                        End If

                        If Left(行データ, 16) = "Public Function " Then
                            開始桁 = 17
                            プロシージャ区分 = "Function"
                            Call Function名取得
                        End If

                        If Left(行データ, 17) = "Private Function " Then
                            開始桁 = 18
                            プロシージャ区分 = "Function"
                            Call Function名取得
                        End If

                        'プロシージャ名とプロシージャ区分取得:Property

                        If Left(行データ, 9) = "Property " Then
                            開始桁 = 10
                            プロシージャ区分 = "Property"
                            Call Property名取得
                        End If

                        If Left(行データ, 16) = "Public Property " Then
                            開始桁 = 17
                            プロシージャ区分 = "Property"
                            Call Property名取得
                        End If

                        '変数の使われているプロシージャを取得
                        '行頭がDimの行は対象外

                        If Left(行データ, 4) <> "Dim " Then

                            If Right(変数名一覧(j, 1), 1) = "(" Then '配列変数の場合
                                Call 配列変数対応取得
                            Else                                   '配列変数以外の場合
                                Call 一般変数対応取得
                            End If

                        End If

    '                     If 行 > 50 Then Stop '★★★★★★
    '                     If 変数名一覧(j) = "k" Then Application.ScreenUpdating = True: Stop

                    End If         'コメント行以外
                Wend

                '**************終了処理*********************
                Close #1
            End If
        Next i 'モジュールファイルを「ファイル数」だけ読み込み続ける。
'        Stop
        If 対応数 = 検索前対応数 Then
            Range("A2").Cells(対応数, 1) = 対応数
            Range("B2").Cells(対応数, 1) = 変数名一覧(j, 1)
            Range("C2").Cells(対応数, 1) = "★ " & 変数名一覧(j, 2) & " ★で、変数の宣言は有りますが、どのモジュールでも、使われていません。★"
            対応数 = 対応数 + 1
        End If

    Next j '変数1から最後(変数)まで繰り返す。
    Cells.Select
    Cells.EntireColumn.AutoFit

    Range("A1").Select
    Selection.AutoFilter

    Range("C1").Select           'ファイルサイズ
    Selection.ColumnWidth = 20

    Range("C2").Select
    ActiveWindow.FreezePanes = True
    Range("B2").Select

    'Stop
End Sub
'********************************************


'********************************************
Private Sub Sub名取得()
    If Right(行データ, 1) <> "_" Then
        Sub名 = Mid(行データ, 開始桁, InStr(開始桁, 行データ, ")") - 開始桁 + 1)
    Else
        Sub名 = Mid(行データ, 開始桁, InStr(開始桁, 行データ, " ") - 開始桁)
    End If
End Sub
'********************************************


'********************************************
Private Sub Function名取得()
    If Right(行データ, 1) <> "_" Then
        Function名 = Mid(行データ, 開始桁, InStr(開始桁, 行データ, "(") - 開始桁)
    Else
        Function名 = Mid(行データ, 開始桁, InStr(開始桁, 行データ, " ") - 開始桁)
    End If
End Sub
'********************************************


'********************************************
Private Sub Property名取得()
    If Right(行データ, 1) <> "_" Then
        Property名 = Mid(行データ, 開始桁, InStr(開始桁, 行データ, "(") - 開始桁)
    Else
        Property名 = Mid(行データ, 開始桁, InStr(開始桁, 行データ, " ") - 開始桁)
    End If
End Sub
'********************************************


'********************************************
Private Sub 配列変数対応取得()
    '変数を、行データと照合する方法。
    '(1).配列変数の場合は、
    ' @.行頭の場合は、ずばり一致
    ' A.行中の場合は、前に空白
    ' B.もしくは行中の場合は、前に(
    If (Left(行データ, Len(変数名一覧(j, 1))) = 変数名一覧(j, 1) _
    Or InStr(行データ, " " & 変数名一覧(j, 1)) <> 0 _
    Or InStr(行データ, "(" & 変数名一覧(j, 1)) <> 0) _
    And (直前Sub名 <> Sub名 Or 直前Function名 <> Function名) Then
        Range("A2").Cells(対応数, 1) = 対応数
        Range("B2").Cells(対応数, 1) = 変数名一覧(j, 1)
        Range("C2").Cells(対応数, 1) = ファイル名一覧(i)
        If プロシージャ区分 = "Sub" Then
            Range("D2").Cells(対応数, 1) = Sub名
            直前Sub名 = Sub名
        ElseIf プロシージャ区分 = "Function" Then
            Range("E2").Cells(対応数, 1) = Function名
            直前Function名 = Function名
        ElseIf プロシージャ区分 = "Function" Then
            Range("F2").Cells(対応数, 1) = Property名
            直前Property名 = Property名
        End If
        対応数 = 対応数 + 1
    End If
End Sub
'********************************************


'********************************************
Private Sub 一般変数対応取得()
    '(2).配列変数で無い場合は、
    ' @.行頭の場合は、後ろに空白
    ' A.行中の場合は、前後に空白
    ' B.もしくは行中の場合は、前に(、後ろに空白
    ' C.もしくは行中の場合は、前に(、後ろに,   ★
    ' D.もしくは行中の場合は、前に空白、後ろに)★
    ' E.もしくは行中の場合は、前に(、後ろに)★★
    ' F.もしくは行中の場合は、前に空白、後ろに,★
    ' G.もしくは行中の場合は、前に空白、後ろに.★★
    ' H.行末の場合は、前に空白
    If (Left(行データ, Len(変数名一覧(j, 1)) + 1) = 変数名一覧(j, 1) & " " _
    Or InStr(行データ, " " & 変数名一覧(j, 1) & " ") <> 0 _
    Or InStr(行データ, "(" & 変数名一覧(j, 1) & " ") <> 0 _
    Or InStr(行データ, "(" & 変数名一覧(j, 1) & ",") <> 0 _
    Or InStr(行データ, " " & 変数名一覧(j, 1) & ")") <> 0 _
    Or InStr(行データ, "(" & 変数名一覧(j, 1) & ")") <> 0 _
    Or InStr(行データ, " " & 変数名一覧(j, 1) & ",") <> 0 _
    Or InStr(行データ, " " & 変数名一覧(j, 1) & ".") <> 0 _
    Or Right(行データ, Len(変数名一覧(j, 1)) + 1) = " " & 変数名一覧(j, 1)) _
    And ((プロシージャ区分 = "Sub" And 直前Sub名 <> Sub名) _
    Or (プロシージャ区分 = "Function" And 直前Function名 <> Function名) _
    Or (プロシージャ区分 = "Property" And 直前Property名 <> Property名)) Then
        Range("A2").Cells(対応数, 1) = 対応数
        Range("B2").Cells(対応数, 1) = 変数名一覧(j, 1)
        Range("C2").Cells(対応数, 1) = ファイル名一覧(i)
        If プロシージャ区分 = "Sub" Then
            Range("D2").Cells(対応数, 1) = Sub名
            直前Sub名 = Sub名
        ElseIf プロシージャ区分 = "Function" Then
            Range("E2").Cells(対応数, 1) = Function名
            直前Function名 = Function名
        ElseIf プロシージャ区分 = "Property" Then
            Range("F2").Cells(対応数, 1) = Property名
            直前Property名 = Property名
        End If
        対応数 = 対応数 + 1
    End If
End Sub
'********************************************

 解説

この種類の目次に戻る↑ 索引へ↓ トップページに戻る



ホームページのトップに戻る