頑張らないために頑張る

ゆるく頑張ります

Accessレポートの調査も相変わらず苦行なので分析用コードを作った

Posted at — Jun 22, 2025

背景と目的

pic

Microsoft Accessで長期間運用されているデータベースシステムでは、レポート機能が業務上重要な役割を担っていることが多くあります。とくに紙ベースで運用されてきた現場などでは顕著でしょう。しかし、システムの改修やWebアプリケーションへの移行などを検討する際、これらのレポートの正確な仕様を把握することは大きな課題となります。ドキュメントが古くなっていたり、そもそも存在しなかったりする場合、手作業で各レポートのデザインやVBAコードを調査するには膨大な時間と労力が必要です。

つまり、これも大抵の場合はメチャクチャ大変ってことです_( _´ω`)_ペショ

このVBAスクリプトは、このような背景から既存のAccessデータベース内に存在するすべてのレポートオブジェクトの技術的な仕様を自動的に調査・分析し、その結果をMarkdown形式のレポートとして出力することを目的としています。

このコードは、これらの課題に対応するため、以下のニーズに応えることを目指しています。

実行環境

今回のソースコードは、以下の環境で動作を確認しています。

alt text

ソースコード

Option Compare Database
Option Explicit

' 統合MDB分析ツール (全フォームのVBAコード分析対応)
' データベース構造の包括的な把握と、新システム移行のための機能分析を支援

Sub AnalyzeAllReportSpecificationsToMarkdown()
    ' プロジェクト内の全レポートの仕様を調査し、個別のMarkdownレポートを生成する
    Dim rptObj As AccessObject
    Dim reportName As String
    Dim strFilePath As String
    Dim intFileNum As Integer
    Dim vbProj As Object ' As VBIDE.VBProject
    Dim vbCrntProj As Object ' As Application.CurrentProject
    Dim analysisCounter As Long: analysisCounter = 0
    Dim errorCounter As Long: errorCounter = 0
    Dim overallStatus As String: overallStatus = ""

'    On Error GoTo ErrorHandler_Overall ' デバッグ中はコメントアウト推奨

    ' VBEプロジェクトの取得 (最初に一度だけ)
    Set vbCrntProj = Application.CurrentProject
    If vbCrntProj Is Nothing Then
        MsgBox "VBAプロジェクトにアクセスできませんでした。参照設定やAccessのオプションを確認してください。", vbCritical, "初期化エラー"
        Exit Sub
    End If

    If vbCrntProj.AllReports.count = 0 Then
        MsgBox "分析対象のレポートが見つかりません。", vbInformation, "情報"
        Exit Sub
    End If

    Set vbProj = Application.VBE.ActiveVBProject ' Application.CodeProject の方が堅牢な場合がある

    ' 全レポートをループ処理
    For Each rptObj In vbCrntProj.AllReports
        reportName = rptObj.Name
        strFilePath = vbCrntProj.Path & "\" & ReplaceValidFileNameChars(reportName) & "_ReportSpec_Analysis.md"
        
        Debug.Print "レポート仕様分析開始: " & reportName

        intFileNum = FreeFile
        Open strFilePath For Output As #intFileNum

        ' レポートヘッダー
        Print #intFileNum, "# レポート仕様分析: `" & MDEscape(reportName) & "`"
        Print #intFileNum, ""
        Print #intFileNum, "**データベース:** `" & MDEscape(CurrentProject.Name) & "`"
        Print #intFileNum, "**分析日時:** " & Now()
        Print #intFileNum, ""
        Print #intFileNum, "---"
        Print #intFileNum, ""

'        On Error GoTo ErrorHandler_ReportLevel ' レポートごとのエラー処理 (デバッグ中はコメントアウト推奨)

        Dim rpt As Report ' Reportオブジェクト用
        Dim designViewOpened As Boolean: designViewOpened = False

        ' レポートをデザインビューで開く (プロパティ取得のため)
'        On Error Resume Next ' 個別のエラー処理に任せる
        DoCmd.OpenReport reportName, acViewDesign, , , acHidden
        If Err.Number = 0 Then
            Set rpt = Reports(reportName)
            designViewOpened = True
        Else
            Print #intFileNum, "## エラー"
            Print #intFileNum, ""
            Print #intFileNum, "- レポート `" & MDEscape(reportName) & "` をデザインビューで開けませんでした。エラー: " & MDEscape(Err.Description)
            Err.Clear
            errorCounter = errorCounter + 1
            overallStatus = overallStatus & vbCrLf & "- " & reportName & ": デザインビューオープンエラー"
            GoTo NextReportIteration
        End If
'        On Error GoTo ErrorHandler_ReportLevel ' 必要に応じて戻す


        ' --- 1. レポートの目的と利用者 (手動確認項目) ---
        Print #intFileNum, "## 1. レポートの目的と利用者 (手動確認推奨)"
        Print #intFileNum, ""
        Print #intFileNum, "- **そのレポートが何のために作成され、誰が、どのような目的で利用しているか?** (ヒアリング等で確認)"
        Print #intFileNum, "- **レポートからどのような情報を得ようとしているか?** (ヒアリング等で確認)"
        Print #intFileNum, "- **レポートの利用頻度**(毎日、毎週、毎月、特定のイベント時など)。 (ヒアリング等で確認)"
        Print #intFileNum, "- **レポートを参照した後、利用者は次にどのようなアクションを取るか?** (ヒアリング等で確認)"
        Print #intFileNum, ""
        Print #intFileNum, "---"
        Print #intFileNum, ""

        ' --- 2. データソースとデータ取得ロジック ---
        AnalyzeReportDataSourceMD intFileNum, rpt, reportName, vbProj
        Print #intFileNum, "---"
        Print #intFileNum, ""

        ' --- 3. レイアウトと表示項目 ---
        AnalyzeReportLayoutMD intFileNum, rpt
        Print #intFileNum, "---"
        Print #intFileNum, ""

        ' --- 4. VBAコードとマクロ ---
        AnalyzeReportCodeAndMacroMD intFileNum, rpt, reportName, vbProj
        Print #intFileNum, "---"
        Print #intFileNum, ""
        
        ' --- 5. 出力形式と用途 ---
        AnalyzeReportOutputFormatMD intFileNum, rpt
        Print #intFileNum, "---"
        Print #intFileNum, ""

        ' --- 6. パフォーマンスとデータ量 (手動確認項目) ---
        Print #intFileNum, "## 6. パフォーマンスとデータ量 (手動確認推奨)"
        Print #intFileNum, ""
        Print #intFileNum, "- **レポートの表示や印刷にどの程度の時間がかかっているか?** (実測またはヒアリングで確認)"
        Print #intFileNum, "- **対象となるデータ量はどの程度か?**(レコード数、期間など) (データソース調査と併せて確認)"
        Print #intFileNum, "- **パフォーマンスに関するユーザーからの不満はあるか?** (ヒアリング等で確認)"
        Print #intFileNum, ""
        Print #intFileNum, "---"
        Print #intFileNum, ""

        ' --- 7. セキュリティとアクセス制御 (手動確認項目) ---
        Print #intFileNum, "## 7. セキュリティとアクセス制御 (手動確認推奨)"
        Print #intFileNum, ""
        Print #intFileNum, "- **そのレポートを誰が閲覧できるべきか?**(特定の役職、部署、ユーザーなど) (ヒアリング等で確認)"
        Print #intFileNum, "- **レポートの内容に、機密性の高い情報や個人情報は含まれているか?** (内容確認)"
        Print #intFileNum, "- **Access側で、レポートへのアクセスに関して何らかの制御が行われているか?** (オブジェクトプロパティ、VBA、ユーザーレベルセキュリティ等の確認)"
        Print #intFileNum, ""
        Print #intFileNum, "---"
        Print #intFileNum, ""
        
        ' --- 8. 改善要望と将来の拡張性 (手動確認項目) ---
        Print #intFileNum, "## 8. 改善要望と将来の拡張性 (手動確認推奨)"
        Print #intFileNum, ""
        Print #intFileNum, "- **現在のレポートに関して、利用者からの改善要望や不満点はあるか?** (ヒアリング等で確認)"
        Print #intFileNum, "- **将来的に、このレポートに関してどのような機能追加や変更が予想されるか?** (ヒアリング等で確認)"
        Print #intFileNum, ""


        analysisCounter = analysisCounter + 1
        overallStatus = overallStatus & vbCrLf & "- " & reportName & ": 正常終了"

NextReportIteration:
        If designViewOpened Then
            On Error Resume Next ' 既に閉じている場合のエラーを無視
            DoCmd.Close acReport, reportName, acSaveNo
            On Error GoTo 0 ' エラーハンドラを全体に戻す前にリセット
        End If
        Set rpt = Nothing
        If intFileNum > 0 Then Close #intFileNum
        intFileNum = 0 ' ファイルハンドルをリセット
'        On Error GoTo ErrorHandler_Overall ' エラーハンドラを全体に戻す (ループの最後に移動)
    Next rptObj
    
    On Error GoTo 0 ' メッセージ表示前のエラーハンドラ解除

    ' --- 全体処理の完了メッセージ ---
    Dim summaryMsg As String
    summaryMsg = "全レポートの仕様分析が完了しました。" & vbCrLf & vbCrLf & _
                 "処理済みレポート数: " & analysisCounter & vbCrLf & _
                 "エラー発生レポート数: " & errorCounter & vbCrLf & vbCrLf & _
                 "レポートは各レポート名のファイルとして、データベースと同じフォルダに保存されました。"
    
    If errorCounter > 0 Then
        summaryMsg = summaryMsg & vbCrLf & vbCrLf & "エラーが発生したレポートの詳細:" & overallStatus
        MsgBox summaryMsg, vbExclamation, "分析完了 (一部エラーあり)"
    Else
        MsgBox summaryMsg, vbInformation, "分析完了"
    End If

Exit Sub

ErrorHandler_ReportLevel:
    Print #intFileNum, "## レポートレベルのエラー"
    Print #intFileNum, ""
    Print #intFileNum, "レポート `" & MDEscape(reportName) & "` の分析処理中に予期せぬエラーが発生しました。"
    Print #intFileNum, "- **エラー番号:** " & Err.Number
    Print #intFileNum, "- **エラー内容:** `" & MDEscape(Err.Description) & "`"
    
    Debug.Print "レポート `" & MDEscape(reportName) & "` の分析処理中に予期せぬエラーが発生しました。"
    Debug.Print "- **エラー内容:** `" & MDEscape(Err.Description) & "`"
    
    errorCounter = errorCounter + 1
    overallStatus = overallStatus & vbCrLf & "- " & reportName & ": 分析中エラー (" & Err.Number & ")"
    Resume NextReportIteration ' エラーが発生したら次のレポートの処理へ

ErrorHandler_Overall:
    MsgBox "分析処理全体で予期せぬエラーが発生しました。" & vbCrLf & _
           "エラー番号: " & Err.Number & vbCrLf & _
           "エラー内容: " & Err.Description, vbCritical, "重大なエラー"
    If intFileNum > 0 Then Close #intFileNum ' 開いている可能性のあるファイルを閉じる
End Sub

' ファイル名として使用できない文字を置換するヘルパー関数
Function ReplaceValidFileNameChars(originalName As String) As String
    Dim invalidChars As String
    Dim i As Long
    Dim char As String
    Dim resultName As String
    
    invalidChars = "\/:*?""<>|"
    resultName = originalName
    
    For i = 1 To Len(invalidChars)
        char = Mid(invalidChars, i, 1)
        resultName = Replace(resultName, char, "_")
    Next i
    ReplaceValidFileNameChars = resultName
End Function


Sub AnalyzeReportDataSourceMD(ByVal FileNum As Integer, ByVal rpt As Report, ByVal reportName As String, ByVal vbProj As Object)
    ' レポートのデータソース関連情報を分析
    Print #FileNum, "## 2. データソースとデータ取得ロジック"
    Print #FileNum, ""
    
    If rpt Is Nothing Then
        Print #FileNum, "- レポートオブジェクトにアクセスできませんでした。"
        Exit Sub
    End If

    Print #FileNum, "- **レコードソース:**"
    If Trim(rpt.RecordSource) = "" Then
        Print #FileNum, "  - `(設定なし)`"
    Else
        Print #FileNum, "  ```sql"
        Print #FileNum, Trim(rpt.RecordSource)
        Print #FileNum, "  ```"
        ' レコードソースがクエリ名の場合、そのSQLも表示しようと試みる
        Dim qdf As DAO.QueryDef
        On Error Resume Next
        Set qdf = CurrentDb.QueryDefs(rpt.RecordSource)
        If Err.Number = 0 And Not qdf Is Nothing Then
            Print #FileNum, "  - **上記レコードソースがクエリの場合のSQL定義:**"
            Print #FileNum, "    ```sql"
            Print #FileNum, Trim(qdf.SQL)
            Print #FileNum, "    ```"
        Else
            Print #FileNum, "  - (レコードソースはテーブル名、または直接SQL文の可能性があります)"
        End If
        Err.Clear
        On Error GoTo 0 ' 通常のエラー処理に戻す
        Set qdf = Nothing
    End If
    Print #FileNum, ""

    ' パラメータ (レポートのFilterプロパティや、レコードソースのクエリがパラメータクエリかなど)
    ' Filterプロパティ
    If Trim(rpt.Filter) <> "" Then
        Print #FileNum, "- **フィルタ (プロパティ):** `" & MDEscape(rpt.Filter) & "`"
        Print #FileNum, "- **フィルタ適用中 (FilterOnプロパティ):** " & IIf(rpt.FilterOn, "はい", "いいえ")
    Else
        Print #FileNum, "- **フィルタ (プロパティ):** 設定なし"
    End If
    ' パラメータクエリの検出は困難なので、レコードソースのSQLに [パラメータ名] が含まれるかなどで推測
    If InStr(1, rpt.RecordSource, "[") > 0 And InStr(1, rpt.RecordSource, "]") > 0 Then
        Print #FileNum, "- **レコードソースにパラメータが含まれる可能性あり。** (例: `[パラメータ名]`) SQL文を確認してください。"
    End If
    Print #FileNum, ""

    ' VBAやマクロによる動的変更の可能性 (VBAコード分析は別セクション)
    Print #FileNum, "- **VBA/マクロによる動的変更:**"
    If rpt.hasModule Then ' 小文字のh
        Dim compName As String, codeMod As Object
        compName = "Report_" & reportName
        On Error Resume Next
        Set codeMod = vbProj.VBComponents(compName).CodeModule ' Itemは不要な場合あり
        If Err.Number = 0 And Not codeMod Is Nothing Then
             If InStr(1, codeMod.Lines(1, codeMod.CountOfLines), ".RecordSource", vbTextCompare) > 0 Or _
                InStr(1, codeMod.Lines(1, codeMod.CountOfLines), ".Filter", vbTextCompare) > 0 Or _
                InStr(1, codeMod.Lines(1, codeMod.CountOfLines), ".FilterOn", vbTextCompare) > 0 Then
                Print #FileNum, "  - VBAコード内で RecordSource や Filter プロパティを操作している可能性があります。詳細はVBAコード分析セクションを参照。"
             Else
                Print #FileNum, "  - VBAコードは存在しますが、RecordSource/Filter操作は見つかりませんでした(簡易チェック)。"
             End If
        Else
            Print #FileNum, "  - VBAモジュールはありますが、コードにアクセスできませんでした。エラー: " & Err.Description
        End If
        Err.Clear
        On Error GoTo 0 ' 通常のエラー処理に戻す
        Set codeMod = Nothing
    Else
        Print #FileNum, "  - VBAモジュールなし。"
    End If
    If Trim(rpt.OnOpen) <> "" Or Trim(rpt.OnNoData) <> "" Then
         Print #FileNum, "  - OnOpenまたはOnNoDataイベントに処理が設定されています(マクロまたはVBAの可能性)。"
    End If
    Print #FileNum, ""
    
    ' リンクテーブルや外部データソースの参照は、レコードソースのSQLから推測
    Print #FileNum, "- **外部データソース参照の可能性:** レコードソースのSQL文を確認し、リンクテーブルや外部DB名が含まれていないか調査してください。"
    Print #FileNum, ""
End Sub

Sub AnalyzeReportLayoutMD(ByVal FileNum As Integer, ByVal rpt As Report)
    ' レポートのレイアウトと表示項目を分析
    ' グループ化に関するロジックは削除
    Dim ctl As Control
    Dim sec As Section

    Print #FileNum, "## 3. レイアウトと表示項目"
    Print #FileNum, ""
    
    If rpt Is Nothing Then
        Print #FileNum, "- レポートオブジェクトにアクセスできませんでした。"
        Exit Sub
    End If

    Print #FileNum, "### 表示コントロール一覧 (主要プロパティ)"
    Print #FileNum, ""
    If rpt.Controls.count > 0 Then
        For Each ctl In rpt.Controls
            Print #FileNum, "- **コントロール名:** `" & MDEscape(ctl.Name) & "`"
            Print #FileNum, "  - **種類:** " & MDEscape(TypeName(ctl)) & " (ControlType: " & ctl.ControlType & ")"
            If ctl.ControlType = acLabel Then
                 Print #FileNum, "  - **キャプション:** `" & MDEscape(ctl.Caption) & "`"
            ElseIf ctl.ControlType = acTextBox Or ctl.ControlType = acComboBox Or ctl.ControlType = acListBox Then
                If Trim(ctl.ControlSource) <> "" Then Print #FileNum, "  - **コントロールソース:** `" & MDEscape(ctl.ControlSource) & "`"
                If Trim(ctl.Format) <> "" Then Print #FileNum, "  - **書式:** `" & MDEscape(ctl.Format) & "`"
                If Trim(ctl.InputMask) <> "" Then Print #FileNum, "  - **入力マスク:** `" & MDEscape(ctl.InputMask) & "`"
            ElseIf ctl.ControlType = acImage Then
                If Trim(ctl.Picture) <> "" Then Print #FileNum, "  - **ピクチャ:** `" & MDEscape(ctl.Picture) & "`"
            ElseIf ctl.ControlType = acSubform Or ctl.ControlType = acSubReport Then ' サブレポートも考慮
                 Print #FileNum, "  - **ソースオブジェクト:** `" & MDEscape(ctl.SourceObject) & "`"
            End If
            If Trim(ctl.Tag) <> "" Then Print #FileNum, "  - **タグ:** `" & MDEscape(ctl.Tag) & "`"
            Print #FileNum, "  - **セクション:** " & SectionCodeToString(ctl.Section)
            Print #FileNum, ""
        Next ctl
    Else
        Print #FileNum, "*表示コントロールはありません。*" & vbCrLf
    End If

    Print #FileNum, "### グループ化と並べ替え"
    Print #FileNum, ""
    
    Print #FileNum, "- **グループ化:** 手動でレポートデザインを確認してください。" ' グループ化情報を手動確認するよう促す
        
    If Trim(rpt.OrderBy) <> "" Then
        Print #FileNum, "- **並べ替え (OrderByプロパティ):** `" & MDEscape(rpt.OrderBy) & "`"
        Print #FileNum, "- **並べ替え適用中 (OrderByOnプロパティ):** " & IIf(rpt.OrderByOn, "はい", "いいえ")
    Else
        Print #FileNum, "- **並べ替え (OrderByプロパティ):** 設定なし"
    End If
    Print #FileNum, ""

    Print #FileNum, "### セクション情報"
    Print #FileNum, ""
    On Error Resume Next ' Sectionコレクションへのアクセスエラー対策
    Dim i As Integer
    ' レポートセクションのインデックスは acDetail から始まる定数で直接指定する方が確実
    Dim sectionTypes As Variant
    sectionTypes = Array(acDetail, acHeader, acFooter, acPageHeader, acPageFooter, _
                       acGroupLevel1Footer, acGroupLevel1Header, _
                       acGroupLevel2Footer, acGroupLevel2Header) ' 他のグループも必要なら追加

    For Each i In sectionTypes
        On Error Resume Next ' 特定のセクションが存在しない場合のエラーをハンドル
        Set sec = rpt.Section(i)
        If Err.Number = 0 And Not sec Is Nothing Then
            Print #FileNum, "- **セクション:** " & MDEscape(sec.Name) & " (" & SectionCodeToString(sec.SectionType) & ")"
            Print #FileNum, "  - **表示:** " & IIf(sec.Visible, "はい", "いいえ")
            Print #FileNum, "  - **高さ:** " & sec.Height
            If sec.SectionType = acGroupLevel1Header Or sec.SectionType = acGroupLevel1Footer Or _
               sec.SectionType = acGroupLevel2Header Or sec.SectionType = acGroupLevel2Footer Then
                 If Trim(sec.Tag) <> "" Then Print #FileNum, "  - **タグ:** `" & MDEscape(sec.Tag) & "`"
            End If
        ElseIf Err.Number <> 0 And Err.Number <> 2459 Then ' 2459: セクションが存在しない
            Print #FileNum, "- (セクション " & SectionCodeToString(i) & " の情報取得エラー: " & Err.Description & ")"
        End If
        Err.Clear
        Set sec = Nothing
        On Error GoTo 0 ' 通常のエラー処理に戻す
    Next i
    
    If Err.Number <> 0 Then Print #FileNum, "- (セクション情報の一部取得で予期せぬエラー発生)": Err.Clear
    On Error GoTo 0
    Print #FileNum, ""
    
    Print #FileNum, "- **条件付き書式の有無:** 手動でレポートデザインを確認してください。"
    Print #FileNum, "- **画像/OLEオブジェクト/グラフの有無:** 上記コントロール一覧とレポートデザインを手動で確認してください。"
    Print #FileNum, ""
End Sub

Sub AnalyzeReportCodeAndMacroMD(ByVal FileNum As Integer, ByVal rpt As Report, ByVal reportName As String, ByVal vbProj As Object)
    ' レポートのVBAコードとマクロを分析
    Print #FileNum, "## 4. VBAコードとマクロ"
    Print #FileNum, ""
    
    If rpt Is Nothing Then
        Print #FileNum, "- レポートオブジェクトにアクセスできませんでした。"
        Exit Sub
    End If

    Print #FileNum, "- **VBAモジュールの有無:** " & IIf(rpt.hasModule, "**あり**", "なし") ' 小文字のh
    If rpt.hasModule Then
        Dim vbComp As Object, codeMod As Object
        Dim compName As String: compName = "Report_" & reportName
        On Error Resume Next
        Set vbComp = vbProj.VBComponents(compName) ' Itemは不要な場合あり
        If vbComp Is Nothing Then Set vbComp = vbProj.VBComponents(reportName) ' Itemなしも試す
        
        If Err.Number = 0 And Not vbComp Is Nothing Then
            Set codeMod = vbComp.CodeModule
            If Not (codeMod Is Nothing) And codeMod.CountOfLines > 1 Then ' 宣言行のみは除く
                Print #FileNum, "  - **VBAコード総行数:** " & codeMod.CountOfLines
                Print #FileNum, "  - **注:** 詳細なVBAコード分析は、フォーム分析と同様のツール/手法で別途実施することを推奨します。"
                ' イベントプロシージャの存在チェック (簡易)
                Dim eventProcList As String: eventProcList = ""
                Dim procInfo As Variant ' 配列を想定
                Dim allProcs As Collection
                Set allProcs = GetAllProceduresFromCodeModule(codeMod) ' 既存関数を再利用
                For Each procInfo In allProcs
                     If Left(LCase(procInfo(0)), 7) = "report_" Or InStr(1, LCase(procInfo(0)), "_format") > 0 Or InStr(1, LCase(procInfo(0)), "_print") > 0 Or InStr(1, LCase(procInfo(0)), "_retreat") > 0 Or InStr(1, LCase(procInfo(0)), "_nodata") > 0 Then
                        eventProcList = eventProcList & "`" & MDEscape(procInfo(0)) & "`, "
                     End If
                Next procInfo
                If eventProcList <> "" Then
                    eventProcList = Left(eventProcList, Len(eventProcList) - 2)
                    Print #FileNum, "  - **主要イベントプロシージャ候補:** " & eventProcList
                End If
            Else
                Print #FileNum, "  - VBAモジュールはありますが、実質的なコードは少ないか、アクセスできませんでした。"
            End If
        Else
            Print #FileNum, "  - VBAコンポーネントにアクセスできませんでした。エラー: " & Err.Description
        End If
        Err.Clear
        On Error GoTo 0 ' 通常のエラー処理に戻す
        Set codeMod = Nothing: Set vbComp = Nothing
    End If
    Print #FileNum, ""
    
    ' 主要イベントプロパティのマクロ設定確認
    Dim eventProps As Variant
    Dim prop As Variant
    eventProps = Array("OnOpen", "OnClose", "OnActivate", "OnDeactivate", "OnError", "OnNoData", "OnPage")
    Dim macroFound As Boolean: macroFound = False
    For Each prop In eventProps
        On Error Resume Next ' プロパティが存在しない場合もある
        Dim propValue As String
        propValue = rpt.Properties(prop).Value
        If Err.Number = 0 And Trim(propValue) <> "" And Left(Trim(propValue), 1) <> "[" Then ' "["で始まらないのはマクロ名
            Print #FileNum, "- **イベントプロパティ `" & MDEscape(CStr(prop)) & "` にマクロ設定あり:** `" & MDEscape(propValue) & "`"
            macroFound = True
        End If
        Err.Clear
        On Error GoTo 0 ' 通常のエラー処理に戻す
    Next prop
    If Not macroFound Then
        Print #FileNum, "- **主要イベントプロパティにマクロ設定は見つかりませんでした(簡易チェック)。**"
    End If
    Print #FileNum, ""
End Sub

Sub AnalyzeReportOutputFormatMD(ByVal FileNum As Integer, ByVal rpt As Report)
    ' レポートの出力形式関連情報を分析
    Print #FileNum, "## 5. 出力形式と用途"
    Print #FileNum, ""
    
    If rpt Is Nothing Then
        Print #FileNum, "- レポートオブジェクトにアクセスできませんでした。"
        Exit Sub
    End If

    Print #FileNum, "- **主に画面プレビューか印刷か、エクスポート利用か:** (ヒアリング等で確認)"
    Print #FileNum, ""
    Print #FileNum, "### ページ設定 (一部抜粋)"
    Print #FileNum, ""
    On Error Resume Next ' Printerオブジェクトがない場合がある
    If Not rpt.Printer Is Nothing Then
        With rpt.Printer
            Print #FileNum, "- **用紙サイズ:** " & PaperSizeToString(.PaperSize) & " (" & .PaperSize & ")"
            Print #FileNum, "- **向き:** " & IIf(.Orientation = acPRORLandscape, "横", "縦") & " (" & .Orientation & ")"
            Print #FileNum, "- **左余白:** " & Format(.LeftMargin / 1440, "0.00") & "インチ (" & .LeftMargin & " twips)" ' 1 inch = 1440 twips
            Print #FileNum, "- **右余白:** " & Format(.RightMargin / 1440, "0.00") & "インチ (" & .RightMargin & " twips)"
            Print #FileNum, "- **上余白:** " & Format(.TopMargin / 1440, "0.00") & "インチ (" & .TopMargin & " twips)"
            Print #FileNum, "- **下余白:** " & Format(.BottomMargin / 1440, "0.00") & "インチ (" & .BottomMargin & " twips)"
        End With
    Else
        Print #FileNum, "- プリンタオブジェクトにアクセスできませんでした。ページ設定は手動で確認してください。"
    End If
    If Err.Number <> 0 Then Print #FileNum, "- ページ設定情報取得エラー: " & MDEscape(Err.Description): Err.Clear
    On Error GoTo 0 ' 通常のエラー処理に戻す
    Print #FileNum, ""
    Print #FileNum, "- **定期的な自動印刷/エクスポートの有無:** (バッチ処理やタスクスケジューラの確認)"
    Print #FileNum, ""
End Sub


' --- ユーティリティ関数群 (一部新規・変更あり) ---

Function ReplaceValidFileNameChars(originalName As String) As String
    Dim invalidChars As String, i As Long, char As String, resultName As String
    invalidChars = "\/:*?""<>|": resultName = originalName
    For i = 1 To Len(invalidChars)
        char = Mid(invalidChars, i, 1): resultName = Replace(resultName, char, "_")
    Next i
    ReplaceValidFileNameChars = resultName
End Function

Function GetAllProceduresFromCodeModule(ByVal codeMod As Object) As Collection
    Dim procs As New Collection, procName As String, procKind As Long, currentLine As Long
    Dim lastProcName As String: lastProcName = "###INITIAL_VALUE###"
    Dim tempProcInfo(0 To 1) As Variant
    If codeMod Is Nothing Or codeMod.CountOfLines = 0 Then Set GetAllProceduresFromCodeModule = procs: Exit Function
    On Error GoTo ErrorHandler_GetAllProcs
    currentLine = 1
    Do While currentLine <= codeMod.CountOfLines
        Dim procNameAtLine As String, procKindAtLine As Long
        On Error Resume Next
        procNameAtLine = codeMod.ProcOfLine(currentLine, procKindAtLine)
        If Err.Number <> 0 Then procNameAtLine = "": Err.Clear
        On Error GoTo ErrorHandler_GetAllProcs
        If procNameAtLine <> "" Then
            If procNameAtLine <> lastProcName Then
                tempProcInfo(0) = procNameAtLine: tempProcInfo(1) = procKindAtLine
                On Error Resume Next
                procs.Add Item:=tempProcInfo, Key:=CStr(procNameAtLine)
                If Err.Number <> 0 Then Err.Clear
                On Error GoTo ErrorHandler_GetAllProcs
                lastProcName = procNameAtLine
            End If
            Dim procStart As Long, procLinesCount As Long
            procStart = codeMod.ProcStartLine(procNameAtLine, procKindAtLine)
            procLinesCount = codeMod.ProcCountLines(procNameAtLine, procKindAtLine)
            If procLinesCount > 0 Then currentLine = procStart + procLinesCount Else currentLine = currentLine + 1
        Else
            currentLine = currentLine + 1
        End If
    Loop
    Set GetAllProceduresFromCodeModule = procs
    Exit Function
ErrorHandler_GetAllProcs:
    Debug.Print "GetAllProceduresFromCodeModule Error: " & Err.Number & " - " & Err.Description
    Set GetAllProceduresFromCodeModule = procs
End Function

Function GetFormControlNames(objectName As String, Optional isReport As Boolean = True) As Collection
    ' レポートまたはフォーム上のコントロール名一覧を取得する
    Dim ctl As Control, col As New Collection, objOwner As Object, designViewOpened As Boolean
    Dim objectType As AcObjectType
    
    Set col = New Collection ' 毎回新しいコレクションを生成
    
    If isReport Then objectType = acReport Else objectType = acForm
    
    On Error Resume Next
    ' オブジェクトが既に開いているか、どのビューで開いているかを確認
    Dim currentState As Long
    currentState = SysCmd(acSysCmdGetObjectState, objectType, objectName)
    
    designViewOpened = False ' 初期化
    
    If currentState <> 0 Then ' 何らかの形で開いている
        If isReport Then
            Set objOwner = Reports(objectName)
            If objOwner.CurrentView <> acViewDesign Then ' デザインビューでなければ開きなおす
                DoCmd.Close objectType, objectName, acSaveNo ' 一旦閉じる
                DoCmd.OpenReport objectName, acViewDesign, , , acHidden
                Set objOwner = Reports(objectName)
                designViewOpened = True ' 新しく開いたフラグ
            End If
        Else ' フォームの場合
            Set objOwner = Forms(objectName)
            If objOwner.CurrentView <> acDesign Then
                DoCmd.Close objectType, objectName, acSaveNo
                DoCmd.OpenForm objectName, acDesign, , , , acHidden
                Set objOwner = Forms(objectName)
                designViewOpened = True
            End If
        End If
    Else ' 開いていない場合
        If isReport Then
            DoCmd.OpenReport objectName, acViewDesign, , , acHidden
            Set objOwner = Reports(objectName)
        Else
            DoCmd.OpenForm objectName, acDesign, , , , acHidden
            Set objOwner = Forms(objectName)
        End If
        designViewOpened = True
    End If
    
    If Err.Number <> 0 Then
        Debug.Print "GetFormControlNames: オブジェクト '" & objectName & "' のオープン/参照エラー: " & Err.Description
        Err.Clear
        Set GetFormControlNames = col ' 空のコレクションを返す
        Exit Function
    End If
    On Error GoTo 0

    If Not objOwner Is Nothing Then
        On Error Resume Next ' コントロールアクセスエラー対策
        For Each ctl In objOwner.Controls
            col.Add ctl.Name, ctl.Name ' キーも同じ名前で追加(重複回避のため)
        Next ctl
        If Err.Number <> 0 Then
            Debug.Print "GetFormControlNames: コントロール取得エラー in '" & objectName & "': " & Err.Description
            Err.Clear
        End If
        On Error GoTo 0
        
        If designViewOpened And Not objOwner Is Nothing Then ' 新しくデザインビューで開いた場合のみ閉じる
            On Error Resume Next ' 閉じる際のエラーは無視
            DoCmd.Close objectType, objectName, acSaveNo
            On Error GoTo 0
        End If
    Else
        Debug.Print "GetFormControlNames: オブジェクト '" & objectName & "' にアクセスできませんでした。"
    End If
    Set GetFormControlNames = col
End Function

Function GetEventProcedureInfo(procName As String, controlNames As Collection, Optional objectTypeName As String = "Report") As String
    ' プロシージャ名からイベント情報を推測する
    Dim parts() As String, objectName As String, eventName As String, ctlName As Variant
    GetEventProcedureInfo = ""
    If InStr(procName, "_") = 0 Then Exit Function
    parts = Split(procName, "_")
    If UBound(parts) < 1 Then Exit Function
    objectName = parts(0): eventName = parts(1)
    If LCase(objectName) = LCase(objectTypeName) Then
        GetEventProcedureInfo = MDEscape(objectTypeName) & " の " & MDEscape(eventName) & " イベント"
        Exit Function
    End If
    On Error Resume Next
    For Each ctlName In controlNames
        If LCase(CStr(ctlName)) = LCase(objectName) Then
            GetEventProcedureInfo = "コントロール `" & MDEscape(objectName) & "` の " & MDEscape(eventName) & " イベント"
            Exit Function
        End If
    Next ctlName
    On Error GoTo 0
End Function

Function ProcKindToString(Kind As Long) As String
    Select Case Kind
        Case 0: ProcKindToString = "Sub/Function (vbext_pk_Proc)"
        Case 1: ProcKindToString = "Property Let (vbext_pk_Let)"
        Case 2: ProcKindToString = "Property Set (vbext_pk_Set)"
        Case 3: ProcKindToString = "Property Get (vbext_pk_Get)"
        Case Else: ProcKindToString = "不明な種類 (" & Kind & ")"
    End Select
End Function

Function MDEscape(TextToEscape As String) As String
    Dim EscapedText As String, parts() As String, i As Long, inBackticks As Boolean
    EscapedText = "": If IsNull(TextToEscape) Then MDEscape = "(Null)": Exit Function
    parts = Split(TextToEscape, "`"): inBackticks = False
    For i = 0 To UBound(parts)
        If inBackticks Then
            EscapedText = EscapedText & "`" & parts(i)
            If i < UBound(parts) Then EscapedText = EscapedText & "`" ElseIf TextToEscape Like "*`" Then EscapedText = EscapedText & "`"
        Else
            Dim tempPart As String: tempPart = parts(i)
            tempPart = Replace(tempPart, "\", "\\"): tempPart = Replace(tempPart, "*", "\*")
            tempPart = Replace(tempPart, "_", "\_"): tempPart = Replace(tempPart, "{", "\{")
            tempPart = Replace(tempPart, "}", "\}"): tempPart = Replace(tempPart, "[", "\[")
            tempPart = Replace(tempPart, "]", "\]"): tempPart = Replace(tempPart, "(", "\(")
            tempPart = Replace(tempPart, ")", "\)"): tempPart = Replace(tempPart, "#", "\#")
            tempPart = Replace(tempPart, "+", "\+"): tempPart = Replace(tempPart, "-", "\-")
            tempPart = Replace(tempPart, ".", "\."): tempPart = Replace(tempPart, "!", "\!")
            tempPart = Replace(tempPart, "|", "\|")
            EscapedText = EscapedText & tempPart
        End If
        inBackticks = Not inBackticks
    Next i
    MDEscape = EscapedText
End Function

Function SectionCodeToString(sectionCode As Integer) As String
    ' セクションコードを文字列に変換
    Select Case sectionCode
        Case acDetail: SectionCodeToString = "詳細 (acDetail)"
        Case acHeader: SectionCodeToString = "レポートヘッダー (acHeader)"
        Case acFooter: SectionCodeToString = "レポートフッター (acFooter)"
        Case acPageHeader: SectionCodeToString = "ページヘッダー (acPageHeader)"
        Case acPageFooter: SectionCodeToString = "ページフッター (acPageFooter)"
        Case acGroupLevel1Header: SectionCodeToString = "グループヘッダー1 (acGroupLevel1Header)"
        Case acGroupLevel1Footer: SectionCodeToString = "グループフッター1 (acGroupLevel1Footer)"
        Case acGroupLevel2Header: SectionCodeToString = "グループヘッダー2 (acGroupLevel2Header)"
        Case acGroupLevel2Footer: SectionCodeToString = "グループフッター2 (acGroupLevel2Footer)"
        ' 他のグループレベルも必要に応じて追加
        Case Else: SectionCodeToString = "不明なセクション (" & sectionCode & ")"
    End Select
End Function

Function PaperSizeToString(paperSizeCode As Integer) As String
    ' 用紙サイズコードを文字列に変換 (主要なもののみ)
    Select Case paperSizeCode
        Case acPRPSLetter: PaperSizeToString = "レター (Letter)"
        Case acPRPSLegal: PaperSizeToString = "リーガル (Legal)"
        Case acPRPSExecutive: PaperSizeToString = "エグゼクティブ (Executive)"
        Case acPRPSA3: PaperSizeToString = "A3"
        Case acPRPSA4: PaperSizeToString = "A4"
        Case acPRPSA5: PaperSizeToString = "A5"
        Case acPRPSB4: PaperSizeToString = "B4 (JIS)"
        Case acPRPSB5: PaperSizeToString = "B5 (JIS)"
        ' 他の用紙サイズも必要に応じて追加 (詳細はAccessのヘルプ参照)
        Case Else: PaperSizeToString = "その他/ユーザー定義 (" & paperSizeCode & ")"
    End Select
End Function

Function IsUserDefinedProcedureName(procNameToCheck As String) As Boolean
    IsUserDefinedProcedureName = True 
    If procNameToCheck = "" Then IsUserDefinedProcedureName = False: Exit Function
    If Left(LCase(procNameToCheck), 4) = "docmd." Or _
       Left(LCase(procNameToCheck), 7) = "screen." Or _
       Left(LCase(procNameToCheck), 3) = "me." Or _
       Left(LCase(procNameToCheck), 6) = "forms!" Or _
       Left(LCase(procNameToCheck), 8) = "reports!" Or _
       LCase(procNameToCheck) = "currentdb" Or _
       LCase(procNameToCheck) = "currentproject" Then IsUserDefinedProcedureName = False: Exit Function
    If IsVBAKeywordOrBuiltIn(procNameToCheck) Then IsUserDefinedProcedureName = False: Exit Function
End Function

Function IsVBAKeywordOrBuiltIn(word As String) As Boolean
    Dim keywords As Variant, i As Long, lowerWord As String
    keywords = Array("Sub", "Function", "Property", "Get", "Let", "Set", "End", "If", "Then", "Else", "ElseIf", _
                     "Select", "Case", "For", "To", "Next", "Do", "While", "Loop", "Until", "Wend", "Exit", _
                     "Call", "Dim", "ReDim", "Const", "Public", "Private", "Friend", "Static", "With", "As", _
                     "New", "Me", "Nothing", "Empty", "Null", "True", "False", "Not", "And", "Or", "Xor", "Eqv", "Imp", "Is", _
                     "Like", "Option", "Explicit", "Compare", "Database", "Binary", "Text", "GoTo", "On", "Error", "Resume", _
                     "Type", "Enum", "Event", "RaiseEvent", "Implements", "Declare", "Lib", "Alias", "AddressOf", "ByVal", "ByRef", "Optional", "ParamArray", _
                     "Array", "MsgBox", "InputBox", "Chr", "Asc", "Str", "Val", "CStr", "CBool", "CByte", "CCur", "CDate", "CDbl", "CDec", "CInt", "CLng", "CSng", "CVar", _
                     "Format", "Date", "Time", "Now", "Timer", "DateAdd", "DateDiff", "DatePart", "DateSerial", "DateValue", "Day", "Month", "Year", "Hour", "Minute", "Second", "Weekday", _
                     "Left", "Right", "Mid", "Len", "InStr", "InStrRev", "Replace", "Split", "Join", "LCase", "UCase", "Trim", "LTrim", "RTrim", "Space", "String", _
                     "Abs", "Sgn", "Sqr", "Int", "Fix", "Round", "Rnd", _
                     "RGB", "QBColor", _
                     "TypeName", "VarType", _
                     "IsEmpty", "IsNull", "IsDate", "IsNumeric", "IsObject", "IsArray", "IsError", _
                     "CreateObject", "GetObject", _
                     "Err", "Erl", _
                     "Nz", "Choose", "Switch", "IIf", _
                     "Environ", "Command", "Shell", _
                     "Beep", "SendKeys", "AppActivate", _
                     "DoEvents", "EOF", "LOF", "FileLen", "GetAttr", "SetAttr", "Dir", "FreeFile", "Input", "Print", "Write", "Open", "Close", "Seek", "Lock", "Unlock", "Name", "Kill", "FileCopy", "MkDir", "RmDir", "ChDir", "ChDrive")
    IsVBAKeywordOrBuiltIn = False: lowerWord = LCase(Trim(word))
    For i = LBound(keywords) To UBound(keywords)
        If lowerWord = LCase(keywords(i)) Then IsVBAKeywordOrBuiltIn = True: Exit Function
    Next i
    If IsNumeric(word) Then IsVBAKeywordOrBuiltIn = True: Exit Function
    If (Left(word, 1) = """" And Right(word, 1) = """") Or (Left(word, 1) = "'" And Right(word, 1) = "'") Then IsVBAKeywordOrBuiltIn = True: Exit Function
End Function

Sub AnalyzeKeywordsAndSyntaxMD(ByVal FileNum As Integer, ByVal codeMod As Object)
    Dim i As Long, lineText As String, evidences As Collection, k As Long, kw As Variant
    Dim counts() As Long, keywordSets(0 To 9) As Variant, sectionTitles(0 To 9) As String
    Print #FileNum, "## 3. 特定のキーワード・構文の使用状況"
    Print #FileNum, ""
    If codeMod.CountOfLines <= 1 Then Print #FileNum, "*分析可能なコードがほとんどありません。*" & vbCrLf: Exit Sub
    keywordSets(0) = Array("DoCmd.", "Forms!", "Reports!", "Me.", "Screen.ActiveForm", "Screen.ActiveControl", "CurrentDb(", "CurrentProject."): sectionTitles(0) = "Accessオブジェクト/メソッド"
    keywordSets(1) = Array("DAO.Database", "DAO.Recordset", "db.OpenRecordset", "ADODB.Connection", "ADODB.Command", "ADODB.Recordset", "cn.Execute", "rs.Open"): sectionTitles(1) = "DAO/ADOオブジェクト"
    keywordSets(2) = Array("On Error GoTo", "On Error Resume Next", "Resume Next", "Resume", "Err.Raise", "Err.Clear"): sectionTitles(2) = "エラー処理構文"
    keywordSets(3) = Array("Declare Sub", "Declare Function"): sectionTitles(3) = "API関数 (宣言)"
    keywordSets(4) = Array("Open ", "Close #", "Print #", "Input #", "Line Input #", "FreeFile", "Kill ", "FileCopy ", "MkDir ", "Scripting.FileSystemObject"): sectionTitles(4) = "ファイル操作"
    keywordSets(5) = Array("For ", "Next", "Do ", "Loop", "While ", "Wend", "For Each"): sectionTitles(5) = "ループ構文 (出現回数)"
    keywordSets(6) = Array("If ", "Then", "ElseIf ", "Else", "End If", "Select Case", "Case ", "End Select"): sectionTitles(6) = "条件分岐構文 (出現回数)"
    keywordSets(7) = Array("CreateObject(", "GetObject("): sectionTitles(7) = "CreateObject/GetObject (外部オブジェクト連携)"
    keywordSets(8) = Array("GoTo "): sectionTitles(8) = "GoToステートメント"
    keywordSets(9) = Array("SELECT """, "INSERT INTO """, "UPDATE """, "DELETE FROM """): sectionTitles(9) = "SQL文字列埋め込みの可能性"
    ReDim counts(LBound(keywordSets) To UBound(keywordSets))
    For k = LBound(keywordSets) To UBound(keywordSets)
        Print #FileNum, "### " & MDEscape(sectionTitles(k)): Print #FileNum, ""
        Set evidences = New Collection: counts(k) = 0
        For i = 1 To codeMod.CountOfLines
            lineText = codeMod.Lines(i, 1)
            If Trim(lineText) = "" Or Left(Trim(lineText), 1) = "'" Then GoTo NextLine_ReportSpec
            For Each kw In keywordSets(k)
                If InStr(1, lineText, CStr(kw), vbTextCompare) > 0 Then
                    If k = 5 Or k = 6 Then counts(k) = counts(k) + 1 Else On Error Resume Next: evidences.Add "**行 " & i & ":** `" & MDEscape(Trim(lineText)) & "`": On Error GoTo 0: counts(k) = counts(k) + 1: Exit For
                End If
            Next kw
NextLine_ReportSpec:
        Next i
        If k = 5 Or k = 6 Then Print #FileNum, "- **総出現回数 (簡易カウント):** " & counts(k) Else If evidences.count > 0 Then Print #FileNum, "- **検出された箇所 (" & evidences.count & "件):**": For Each kw In evidences: Print #FileNum, "  - " & kw: Next kw Else Print #FileNum, "*該当するキーワード・構文は見つかりませんでした。*"
        Print #FileNum, ""
    Next k
    Dim declaredAPIs As New Collection, apiName As String, apiCallFound As Boolean
    For i = 1 To codeMod.CountOfLines
        lineText = codeMod.Lines(i, 1)
        If InStr(1, lineText, "Declare", vbTextCompare) > 0 Then apiName = ExtractApiName(lineText): If apiName <> "" Then On Error Resume Next: declaredAPIs.Add apiName, apiName: On Error GoTo 0
    Next i
    Print #FileNum, "### API関数 (呼び出し)": Print #FileNum, ""
    If declaredAPIs.count > 0 Then
        Set evidences = New Collection: apiCallFound = False
        For Each kw In declaredAPIs
            For i = 1 To codeMod.CountOfLines
                lineText = codeMod.Lines(i, 1)
                If Trim(lineText) = "" Or Left(Trim(lineText), 1) = "'" Then GoTo NextApiLine_ReportSpec
                If InStr(1, lineText, CStr(kw), vbTextCompare) > 0 And InStr(1, lineText, "Declare", vbTextCompare) = 0 Then On Error Resume Next: evidences.Add "**行 " & i & " (`" & MDEscape(CStr(kw)) & "`):** `" & MDEscape(Trim(lineText)) & "`": apiCallFound = True: On Error GoTo 0
NextApiLine_ReportSpec:
            Next i
        Next kw
        If evidences.count > 0 Then Print #FileNum, "- **呼び出しが検出された箇所 (" & evidences.count & "件):**": For Each kw In evidences: Print #FileNum, "  - " & kw: Next kw Else Print #FileNum, "*宣言されたAPI関数の呼び出しは見つかりませんでした。*"
    Else Print #FileNum, "*呼び出しをチェックする宣言済みAPI関数が見つかりませんでした。*"
    Print #FileNum, ""
End Sub

Function ExtractApiName(declarationLine As String) As String
    Dim namePart As String
    ExtractApiName = ""
    If InStr(1, declarationLine, "Function ", vbTextCompare) > 0 Then namePart = Mid(declarationLine, InStr(1, declarationLine, "Function ", vbTextCompare) + Len("Function ")) Else If InStr(1, declarationLine, "Sub ", vbTextCompare) > 0 Then namePart = Mid(declarationLine, InStr(1, declarationLine, "Sub ", vbTextCompare) + Len("Sub ")) Else Exit Function
    If InStr(1, namePart, "(") > 0 Then ExtractApiName = Trim(Left(namePart, InStr(1, namePart, "(") - 1)) Else ExtractApiName = Trim(namePart)
End Function

概要

このVBAスクリプトは、Microsoft Accessデータベース内に存在するすべてのレポートオブジェクトを対象とし、それぞれの技術的な仕様(データソース、レイアウト、表示項目、VBAコードやマクロの利用状況、ページ設定など)を自動的に調査・分析します。分析結果は、レポートごとに個別のMarkdownファイルとして、データベースファイルと同じフォルダに出力されます。

このツールの主な目的は、既存のAccessレポートの機能を新しいシステム(例: Webアプリケーション)へ移行する際や、大規模な改修を行う際に必要となる初期の仕様情報を効率的に収集し、ドキュメント化することです。レポートの「目的」や「利用者」といった意味的な情報は自動分析が困難なため、これらの項目については手動での確認を促すメッセージがレポートに含まれます。

スクリプトは、VBE (Visual Basic Editor) オブジェクトモデルやDAO (Data Access Objects) を活用して、レポートのプロパティや関連するVBAコードの情報を取得し、Markdown形式で整形して出力します。これにより、開発者は各レポートの技術的な詳細を迅速に把握し、移行計画や改修作業の基礎資料として利用できます。

各プロシージャ・関数の機能解説

Sub AnalyzeAllReportSpecificationsToMarkdown()

Sub AnalyzeReportDataSourceMD(ByVal FileNum As Integer, ByVal rpt As Report, ByVal reportName As String, ByVal vbProj As Object)

Sub AnalyzeReportLayoutMD(ByVal FileNum As Integer, ByVal rpt As Report)

Sub AnalyzeReportCodeAndMacroMD(ByVal FileNum As Integer, ByVal rpt As Report, ByVal reportName As String, ByVal vbProj As Object)

Sub AnalyzeReportOutputFormatMD(ByVal FileNum As Integer, ByVal rpt As Report)

ユーティリティ関数群

当ソースコードの利用手順

  1. Accessデータベースを開く: 分析したいAccessデータベースファイル(.mdb または .accdb)を開きます。
  2. VBAエディタを開く: キーボードの Alt + F11 キーを押すか、Accessのリボンメニューから「データベースツール」タブ → 「Visual Basic」を選択します。
  3. 標準モジュールを挿入: VBAエディタのメニューから「挿入」→「標準モジュール」を選択し、新しい標準モジュールを作成します。
  4. ソースコードの貼り付け: 上記で提示されたVBAソースコード全体(AnalyzeAllReportSpecificationsToMarkdownプロシージャと、それに付随する全てのサブプロシージャおよびユーティリティ関数)を、作成した標準モジュールのコードウィンドウにコピー&ペーストします。
  5. 参照設定の確認:
    • VBAエディタのメニューから「ツール」→「参照設定」を開きます。
    • 以下のライブラリにチェックが入っていることを確認してください。もしチェックされていなければ、リストから探してチェックを入れます。
      • Microsoft Office XX.0 Access database engine Object Library (XX.0はOfficeのバージョンによって変わります。)
      • Microsoft Visual Basic for Applications Extensibility 5.3
    • 「OK」をクリックしてダイアログを閉じます。
  6. VBAプロジェクトへのアクセス許可:
    • Accessのメインウィンドウで、「ファイル」→「オプション」→「トラストセンター」→「トラストセンターの設定」ボタンをクリックします。
    • 「トラストセンター」ダイアログで、「マクロの設定」を選択し、「VBA プロジェクト オブジェクト モデルへのアクセスを信頼する」にチェックを入れます。
    • 「OK」を数回クリックして、すべてのダイアログを閉じます。
  7. コードの実行:
    • VBAエディタに戻り、AnalyzeAllReportSpecificationsToMarkdown プロシージャ内のどこかにカーソルを置きます。
    • キーボードの F5 キーを押すか、ツールバーの実行ボタン(緑色の右向き三角)をクリックしてマクロを実行します。
  8. レポートファイルの確認:
    • 処理が完了すると、メッセージボックスで結果の概要が表示されます。
    • Accessデータベースファイルが保存されているのと同じフォルダ内に、プロジェクト内の各レポートに対応する [レポート名]_ReportSpec_Analysis.md という名前のMarkdownファイルが複数作成されていることを確認します。
    • これらのMarkdownファイルを、Markdown対応のテキストエディタやビューア(例: Visual Studio Code, Typora, 各種オンラインMarkdownエディタなど)で開いて内容を確認します。

当ソースコードを利用する際の注意点

Todo

参考

  1. Access VBA リファレンス
comments powered by Disqus