このサイトの記事内では「アフィリエイト広告」などの広告を掲載している場合があります。
消費者庁が問題としている「誇大な宣伝や表現」とならないよう配慮しコンテンツを制作しておりますのでご安心ください。
問題のある表現が見つかりましたらお問い合わせよりご一報いただけますと幸いです。

【VBADoc】ドキュメント自動生成ツール

2022年4月28日

今回はVBAのドキュメント自動生成ツールを紹介します。

Javaなどの言語だとJavadocなどの、ドキュメント自動生成ツールがあります。(コメントから情報を拾い上げてドキュメントが自動的に作成されます)

こういったツールに憧れていたんですが、VBAにはそういったものがないため自作してみました。

コメントの書き方は少しルールがあるのですが、各プロシージャごとに下記の4つの情報を自動的に拾い上げてくれます。

  • 参照設定
  • プロシージャ名
  • アノテーションコメント(プロシージャの処理内容を説明するコメント。"’でコメントアウトしておく)
  • コードブロックコメント(コードの塊の処理内容を説明するコメント。"でコメントアウトしておく)
  • 呼び出すプロシージャ

具体的には↓のような内容がアウトプットされます。

参照設定
・VBA : Visual Basic For Applications
・Excel : Microsoft Excel 16.0 Object Library
・stdole : OLE Automation
・Office : Microsoft Office 16.0 Object Library
・Scripting : Microsoft Scripting Runtime
・MSForms : Microsoft Forms 2.0 Object Library
・Outlook : Microsoft Outlook 16.0 Object Library
・VBIDE : Microsoft Visual Basic for Applications Extensibility 5.3

vbaDoc.VbaDoc
アノテーション
・vbaドキュメントを自動生成する(クリップボードへ貼付け)
・再帰呼び出しは対応不可
・参照設定:Microsoft Visual Basic for Applications Extensibility
コードブロックコメント
・インプット用の文字列を作成
・プロシージャクラスを作成
・プロシージャ名リスト(カンマ区切り)の作成
・クラスのstrCallProcNameプロパティを入力
・アウトプット処理
・後処理
呼び出すプロシージャ
・LetCallProc
・MakeProcCls
・SetCB

ただ実用レベルまでは作り上げたのですが、いかんせんJavadocなどのツールと比べると見劣りするなぁと思っています。

そのため、こういった機能が欲しいや、プログラムを書き換えた方が良い点があればコメントをいただきたいと考えております。

プログラムを引用して、こうした方が良いといった内容をコメントしてもらえると嬉しいです。

※参照設定が取得できるようにコードを追加しました。

VBAが難しいと感じたら
自分で作るのが難しいと感じたらご相談ください。
ココナラにて、VBAの作成を受け付けています。
ご相談はこちらから

事前準備

プログラムの実行前に下記2つの設定をしてください。

どちらもVBE内のコードを読み取るために、必要な設定になります。

参照設定

「Microsoft Visual Basic for Applications Extensibility」の参照設定の追加が必要です。

参照設定とは?という方は下記の記事を参考に設定してみてください。

VBAプロジェクトオブジェクトモデルへのアクセスを信頼

トラストセンター

Excelのオプション⇒トラストセンター⇒マクロの設定とクリックしてください。

上記画像が表示されるので、「VBAプロジェクトオブジェクトモデルへのアクセスを信頼する」にチェックを入れてください。

ドキュメント自動生成ツール

このツールは標準モジュール1つとクラスモジュール1つで構成されます。

クラスモジュールのオブジェクト名はclsProcとしてください。

Option Explicit
Option Base 1
Private clsProc() As clsProc
Private clsNum As Long

Sub VbaDoc()
    '''vbaドキュメントを自動生成する(クリップボードへ貼付け)
    '''再帰呼び出しは対応不可
    '''参照設定:Microsoft Visual Basic for Applications Extensibility
    
    clsNum = 0
    Erase clsProc
    Dim objVB As VBProject
    Set objVB = ThisWorkbook.VBProject
    
    ''インプット用の文字列を作成
    Dim strMsg As String, comp_i As Long
    For comp_i = 1 To objVB.VBComponents.Count
        strMsg = strMsg & comp_i & ": " & objVB.VBComponents.Item(comp_i).Name & vbLf
    Next
    strMsg = Left(strMsg, Len(strMsg) - 1) '末尾の改行削除
    
    Dim compNum As String
    compNum = InputBox("ドキュメント生成したいモジュールを選んでください" & vbLf & "(複数選択したい場合はカンマ区切り)" & vbLf & strMsg)
    
    ''プロシージャクラスを作成
    Dim strOneNum, lngOneNum As Long
    Dim oneComp As VBComponent, codeMdl As CodeModule
    On Error GoTo ErrorTrap '入力値がおかしければエラー処理
    For Each strOneNum In Split(compNum, ",")
        lngOneNum = CLng(strOneNum)
        Set oneComp = objVB.VBComponents.Item(lngOneNum)
        Set codeMdl = oneComp.CodeModule
        
        Call MakeProcCls(codeMdl) 'プロシージャクラスを作成
    Next
    On Error GoTo 0
    
    ''プロシージャ名リスト(カンマ区切り)の作成
    Dim i As Long, strProcName As String
    For i = 1 To clsNum
        strProcName = strProcName & clsProc(i).procName & ","
    Next
    strProcName = Left(strProcName, Len(strProcName) - 1)
    
    ''クラスのstrCallProcNameプロパティを入力
    Call LetCallProc(strProcName)
    
    ''アウトプット処理
    Dim strOut As String
    For i = 1 To clsNum
        With clsProc(i)
            strOut = strOut & .mdlName & "." & .procName & vbLf
            If .strAnnt <> "" Then
                strOut = strOut & "アノテーション" & vbLf & .strAnnt
                strOut = strOut & vbLf
            Else
                strOut = strOut & "アノテーション" & vbLf & "・無し" & vbLf
            End If
            
            If .strBlockComment <> "" Then
                strOut = strOut & "コードブロックコメント" & vbLf & .strBlockComment & vbLf
            Else
                strOut = strOut & "コードブロックコメント" & vbLf & "・無し" & vbLf
            End If
            
            If .strCallProcName <> "" Then
                strOut = strOut & "呼び出すプロシージャ" & vbLf & .strCallProcName & vbLf & vbLf
            Else
                strOut = strOut & "呼び出すプロシージャ" & vbLf & "・無し" & vbLf & vbLf
            End If
        End With
    Next
    SetCB strOut
    
    ''後処理
    For i = 1 To clsNum
        Set clsProc(i) = Nothing
    Next
    
    Exit Sub
    
ErrorTrap: MsgBox "数字以外を入力しないでください"
End Sub

Private Sub LetCallProc(ByVal strProcName As String)
    '''プロシージャで呼び出している他のプロシージャ名⇒strCallProcNameプロパティへ入力
    
    Dim arrProcName
    arrProcName = Split(strProcName, ",")
    Dim i As Long, line_i As Long, oneProcName
    For i = 1 To clsNum
        With clsProc(i)
            For Each oneProcName In arrProcName
                
                'プロシージャ名が自身⇒次のプロシージャ名へ
                If .procName = oneProcName Then GoTo NextProcName
                
                For line_i = .sRow To .eRow
                    Dim tmpLine As String
                    tmpLine = .codeMdl.Lines(line_i, 1)
                    
                    ''コード内に他のプロシージャ名があるか判定
                    'プロシージャ名の直前のスペースも確認
                    'コメントの場合を排除
                    If (tmpLine Like "* " & oneProcName & "*") And Not (tmpLine Like "*'*" & oneProcName & "*") Then
                        '2つ目以降は改行後にプロシージャ名を追加
                        If .strCallProcName <> "" Then .strCallProcName = .strCallProcName & vbLf
                        .strCallProcName = .strCallProcName & "・" & oneProcName
                        GoTo NextProcName 'マッチしたら次のプロシージャ名へ
                    End If
                Next
NextProcName:
            Next
        End With
    Next
End Sub

Private Sub MakeProcCls(codeMdl As CodeModule)
    '''プロシージャクラスを作成
    
    Dim strLine As String
    Dim procName As String, strAnnt As String
    Dim i As Long, sRow As Long
    For i = 1 To codeMdl.CountOfLines
        strLine = codeMdl.Lines(i, 1)
        
        ''インスタンス生成
        If JudgeProc(strLine) Then
            
            '要素数の変更
            clsNum = clsNum + 1
            If clsNum = 1 Then
                ReDim clsProc(clsNum)
            Else
                ReDim Preserve clsProc(clsNum)
            End If
            
            Set clsProc(clsNum) = New clsProc
            Set clsProc(clsNum).codeMdl = codeMdl
            clsProc(clsNum).mdlName = codeMdl.Parent.Name
            clsProc(clsNum).procName = GetProcName(strLine)
            clsProc(clsNum).sRow = i
        End If
        
        ''アノテーションとコードブロックコメントの取得
        If Left(Replace(strLine, " ", ""), 3) = "'''" Then
            '2つ目以降は改行後にアノテーションを追加
            If clsProc(clsNum).strAnnt <> "" Then clsProc(clsNum).strAnnt = clsProc(clsNum).strAnnt & vbLf
            clsProc(clsNum).strAnnt = clsProc(clsNum).strAnnt & "・" & Right(strLine, Len(strLine) - InStr(strLine, "'''") - 2)
        
        ElseIf Left(Replace(strLine, " ", ""), 2) = "''" Then
            '2つ目以降は改行後にコードブロックコメントを追加
            If clsProc(clsNum).strBlockComment <> "" Then clsProc(clsNum).strBlockComment = clsProc(clsNum).strBlockComment & vbLf
            clsProc(clsNum).strBlockComment = clsProc(clsNum).strBlockComment & "・" & Right(strLine, Len(strLine) - InStr(strLine, "''") - 1)
        End If
        
        '終了行の取得
        If (strLine Like "*End Sub*") Or (strLine Like "*End Function*") Then
            clsProc(clsNum).eRow = i
        End If
    Next
End Sub

Private Function GetProcName(strLine As String) As String
    '''プロシージャ名を返す
    
    ''SubとFunctionのどちらか判定
    Dim procState As String
    If (strLine Like "*Sub *") Then
        procState = "Sub "
    Else
        procState = "Function "
    End If
    
    Dim nameStart As Long, nameEnd As Long
    nameStart = InStr(strLine, procState)
    nameEnd = InStr(strLine, "(")
    GetProcName = Mid(strLine, nameStart + Len(procState), nameEnd - nameStart - Len(procState))
    
End Function

Private Function JudgeProc(strLine As String) As Boolean
    '''引数strLineがプロシージャの開始行か判定
    
    ''コメント⇒Falseを返す
    If Left(Replace(strLine, " ", ""), 1) = "'" Then Exit Function
    
    JudgeProc = (strLine Like "Sub *") Or (strLine Like "Function *") Or _
              (strLine Like "Private Sub *") Or (strLine Like "Private Function *") Or _
              (strLine Like "Public Sub *") Or (strLine Like "Public Function *")
End Function

Private Function GetRef() As String
    '''参照設定の一覧を返す
    
    GetRef = "参照設定" & vbLf
    
    Dim Ref
    For Each Ref In ThisWorkbook.VBProject.References
        GetRef = GetRef & "・" & Ref.Name & " : " & Ref.Description & vbLf
    Next
End Function

Public Sub SetCB(ByVal strSet As String)
'クリップボードにstrSetを貼り付ける
    '文字化け対策のためTextBoxを使用
    With CreateObject("Forms.TextBox.1")
        .MultiLine = True '複数行入力可
        .Text = strSet
        .SelStart = 0
        .SelLength = .TextLength
        .Copy
    End With
End Sub
Option Explicit
Public mdlName As String 'モジュール名
Public procName As String  'プロシージャ名
Public codeMdl As CodeModule 'コードモジュール
Public sRow As Long 'プロシージャの開始行
Public eRow As Long 'プロシージャの終了行
Public strAnnt As String 'アノテーション
Public strBlockComment As String 'コードブロックコメント
Public strCallProcName As String '呼び出すプロシージャ名

実行イメージ

実行イメージ

プログラムを実行すると画像のインプットボックスが表示され、ブック内のモジュールが一覧表示されます。

ドキュメント生成したモジュールの番号を入力してOKします。

すると、冒頭で紹介したドキュメントがクリップボードに貼り付けられています。

メモ帳などに貼り付けてして確認してみてください。

プログラム解説

後日追記予定。

VBAが難しいと感じたら
自分で作るのが難しいと感じたらご相談ください。
ココナラにて、VBAの作成を受け付けています。
ご相談はこちらから

VBA

Posted by やろまい