【入門編】【中級者向け】プロジェクト全体のカスタムフィールド使用状況を一覧出力する監査ツール – Project VBA解析バイブル

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

MS Projectの「ブラックボックス」を解体する:カスタムフィールド監査ツールの開発

こんにちは。プロジェクト管理の現場で、複雑に絡み合ったスケジュールやリソース管理に頭を抱えていませんか?

MS Projectを使い込んでいると、必ずと言っていいほど直面する問題があります。それは「カスタムフィールドの肥大化とブラックボックス化」です。

「この数式、誰がいつ作ったんだっけ?」「このフィールド、実はもう誰も使っていないのに、計算負荷だけを上げているんじゃないか?」……。

マクロの記録ボタンを押して、操作をなぞるだけでは決して辿り着けない、「オブジェクトの構造を俯瞰する」というエンジニアリングの視点。今日はその視点を持って、プロジェクトの健全性を保つための「カスタムフィールド監査ツール」を一緒に作っていきましょう。

ここをクリアすれば、Project VBAの基本はバッチリですよ。

1. なぜ「監査」が必要なのか?

MS Projectにおけるカスタムフィールドは、非常に強力な武器です。しかし、その強力さゆえに、以下のような「管理不全」を引き起こします。

  • 数式の迷宮化: 複雑なネスト(入れ子)になった数式が、メンテナンス不能になる。
  • リソースの浪費: 使われていないフィールドに重い数式が入っていると、プロジェクトを開くたびに計算負荷がかかり、動作が重くなる。
  • データの不整合: 似たような名前のフィールドが複数存在し、どのデータが正しいのか分からなくなる。

これらを防ぐには、「今、何が、どのように使われているのか」を一覧化して可視化することが不可欠なのです。

2. 構造を知る:TaskとResourceの二重構造

VBAコードを書く前に、MS Projectの「設計図」を頭に叩き込みましょう。カスタムフィールドは、大きく分けて2つの領域に存在します。

領域 対象オブジェクト 特徴
Task Custom Fields タスク(作業) 作業期間、コスト、進捗率など「作業そのもの」に関する属性
Resource Custom Fields リソース(資源) 氏名、スキル、単価、役割など「人や設備」に関する属性

ここでの重要なポイントは、「Taskのカスタムフィールドを調べても、Resourceのカスタムフィールドは見つからない」ということです。監査ツールを作る際は、この両方のコレクションを個別にスキャンする必要があります。

3. 実装:カスタムフィールド監査ツール

それでは、プロジェクト内のすべてのカスタムフィールドの名前、型、数式、そして「実際に使用されているか」を抽出し、Excelへ書き出すコードを実装しましょう。

「マクロの記録」では、Excelへの書き出し操作は記録できても、オブジェクトのプロパティをループで回る論理構造は作れません。ここからが真のプログラミングの始まりです。

Option Explicit

''' <summary>
''' プロジェクト内のカスタムフィールド使用状況をExcelに書き出す監査ツール
''' </summary>
Sub AuditCustomFields()
    Dim proj As Project
    Set proj = ActiveProject
    
    Dim xlApp As Object
    Dim xlWB As Object
    Dim xlSheet As Object
    Dim row As Long
    
    ' --- 1. Excelの準備 ---
    ' 監査結果をExcelに出力することで、分析しやすくします
    On Error Resume Next
    Set xlApp = GetObject(, "Excel.Application")
    If xlApp Is Nothing Then
        Set xlApp = CreateObject("Excel.Application")
    End If
    On Error GoTo 0
    
    xlApp.Visible = True
    Set xlWB = xlApp.Workbooks.Add
    Set xlSheet = xlWB.Sheets(1)
    
    ' ヘッダーの作成
    With xlSheet
        .Cells(1, 1).Value = "Type (Task/Resource)"
        .Cells(1, 2).Value = "Field Name"
        .Cells(1, 3).Value = "Data Type"
        .Cells(1, 4).Value = "Formula"
        .Cells(1, 5).Value = "Is In Use?"
        .Range("A1:E1").Font.Bold = True
        .Range("A1:E1").Interior.Color = RGB(200, 200, 200)
    End With
    
    row = 2
    
    ' --- 2. Task Custom Fields のスキャン ---
    Debug.Print "Scanning Task Custom Fields..."
    Call ScanFields(proj.TaskCustomFields, "Task", xlSheet, row)
    
    ' --- 3. Resource Custom Fields のスキャン ---
    Debug.Print "Scanning Resource Custom Fields..."
    Call ScanFields(proj.ResourceCustomFields, "Resource", xlSheet, row)
    
    ' --- 4. 後処理 ---
    xlSheet.Columns("A:E").AutoFit
    MsgBox "監査が完了しました!Excelを確認してください。", vbInformation
End Sub

''' <summary>
''' 各フィールドをループしてExcelに書き込む共通処理
''' </summary>
Private Sub ScanFields(ByRef fieldColl As Object, ByVal category As String, _
                       ByRef xlSheet As Object, ByRef row As Long)
    Dim i As Integer
    Dim fld As Object
    
    ' fieldColl は TaskCustomFields または ResourceCustomFields
    For i = 1 To fieldColl.Count
        Set fld = fieldColl(i)
        
        ' Excelへの書き出し
        xlSheet.Cells(row, 1).Value = category
        xlSheet.Cells(row, 2).Value = fld.Name
        xlSheet.Cells(row, 3).Value = GetFieldTypeString(fld.Type)
        xlSheet.Cells(row, 4).Value = fld.Formula
        
        ' UseFieldプロパティがTrueなら、プロジェクト内のどこかで使われている
        If fld.UseField Then
            xlSheet.Cells(row, 5).Value = "YES"
            xlSheet.Cells(row, 5).Font.Color = RGB(0, 128, 0) ' 緑色
        Else
            xlSheet.Cells(row, 5).Value = "NO"
            xlSheet.Cells(row, 5).Font.Color = RGB(255, 0, 0) ' 赤色
        End If
        
        row = row + 1
    Next i
End Sub

''' <summary>
''' 数値(0)などの定数を読みやすい文字列に変換するヘルパー関数
''' </summary>
Private Function GetFieldTypeString(ByVal fieldType As Integer) As String
    Select Case fieldType
        Case 0: GetFieldTypeString = "Text"
        Case 1: GetFieldTypeString = "Flag"
        Case 2: GetFieldTypeString = "Number"
        Case 3: GetFieldTypeString = "Date"
        Case 4: GetFieldTypeString = "Duration"
        Case 5: GetFieldTypeString = "Cost"
        Case Else: GetFieldTypeString = "Unknown"
    End Select
End Function

4. 伝説のエンジニアによる「ここが肝心」解説

コードを書き終えたら、その背後にある「なぜこう書いたのか」というロジックを理解しましょう。これが、ただのプログラマーとエンジニアを分ける境界線です。

① `UseField` プロパティの威力

このコードの心臓部は `fld.UseField` です。
カスタムフィールドは、「定義されているだけ」の状態と、「実際にタスクやリソースの行に割り当てられている」状態があります。
`UseField` をチェックすることで、「定義はされているが、誰も使っていない幽霊フィールド」を瞬時にあぶり出すことができます。これは、プロジェクトの軽量化において極めて重要な指標です。

② 共通処理への集約(DRY原則)

`ScanFields` というサブプロシージャを作りました。これはプログラミングの鉄則 DRY (Don’t Repeat Yourself: 同じことを繰り返すな) に基づいています。
Task用とResource用で似たようなコードを2回書くのではなく、引数(`fieldColl`)を渡すことで、一つのロジックで両方を処理しています。これにより、将来「新しいフィールドタイプが増えた」としても、修正箇所は一箇所で済みます。

③ 型の変換(Enumの解釈)

MS ProjectのAPIは、データの型を「0, 1, 2…」といった整数(Enum)で返してきます。しかし、人間が「Type 2」と言われてもピンときませんよね? `GetFieldTypeString` 関数を用意することで、人間にとって意味のある「Number」という言葉に翻訳しています。「コンピュータが理解できる言葉を、人間が理解できる言葉に翻訳する」。これは優れたツール開発の基本です。

5. 陥りやすいエラーと対策

このツールを運用する際、以下の点に注意してください。

  • 「数式が空っぽ」問題:

`fld.Formula` が空の場合、エラーにはなりませんが、出力結果が空白になります。これは「手入力用のフィールド」であることを意味します。

  • Excelの参照設定:

上記のコードでは `CreateObject(“Excel.Application”)` を使った「実行時バインディング(Late Binding)」を採用しています。これにより、Excelのバージョンが違ってもエラーが出にくい、移植性の高いコードにしています。

  • 権限エラー:

プロジェクトファイルが「読み取り専用」であったり、他のユーザーが排他的に開いている場合、操作に失敗することがあります。

最後に

カスタムフィールドの監査ができるようになると、プロジェクト管理の「見えないコスト」をコントロールできるようになります。

「動けばいい」というマクロの段階から、「管理しやすく、メンテナンスしやすい仕組みを作る」というエンジニアリングの段階へ。あなたは今、その大きな一歩を踏み出しました。

ここをクリアすれば、Project VBAの基本はバッチリですよ。次は、この監査結果をもとに「使われていないフィールドを自動削除する」といった、より高度な自動化に挑戦してみましょう!

応援しています。

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