【入門編】【中級者向け】カスタムフィールドの値をExcelから読み込み、VBAでProjectに反映させる – Project VBA解析バイブル

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

Excelを「真のマスター」に変える。Project VBAによるカスタムフィールド同期の極意

こんにちは。現場の自動化を支えるアーキテクトとして、日々コードと向き合っているエンジニアです。

皆さんは、MS Projectのプロジェクト管理をしていて、こんな壁にぶつかったことはありませんか?

「Excelで計算した詳細なコスト管理表や進捗分析結果を、Projectのカスタムフィールドに手作業で打ち込むのが苦痛すぎる……」
「マクロを組んでみたけれど、Excelの行がズレているだけでProject側のデータがめちゃくちゃになってしまった……」

マクロの記録機能で「セルをコピーして、Projectのフィールドに貼り付ける」という動作を記録するのは簡単です。しかし、それでは「データの整合性」という、プロフェッショナルが最も大切にする領域を制御できません。

今日は、一歩上のステップへ進みたいあなたに向けて、「Excelをマスター(正解)とし、Projectへ安全かつ高速にデータを流し込む」ための、実践的なアーキテクチャを伝授します。ここをクリアすれば、Project VBAの基本はバッチリですよ。

1. 概念設計:なぜ「単純なループ」では失敗するのか?

多くの初学者が陥る罠は、「Excelの1行目から順番に、Projectのタスクの1番目から順番に適用していく」という設計です。

[失敗するモデル]
Excel Row 1  --->  Project Task 1
Excel Row 2  --->  Project Task 2
...もしExcelでタスクが削除されていたら? データの紐付けが全てズレて崩壊します。

プロのエンジニアは、データに「ユニークな鍵(Key)」を持たせます。
ExcelのデータとProjectのタスクを紐付けるための「共通のID(Task IDなど)」をキーにして、「探して、一致したら、書き込む」というプロセスを設計しなければなりません。

データの整合性を守る3つの守護神

1. Key Matching (キー照合): IDに基づき、確実に「そのタスク」を特定する。
2. Data Validation (型検証): Excelの「文字列」を、Projectの「数値型」フィールドに入れようとしてエラーにならないよう防ぐ。
3. Error Handling (例外処理): 1箇所エラーが出ても、処理全体を止めずに「どこがダメだったか」を報告する。

2. 実装編:堅牢なデータ同期コード

それでは、実際に使えるコードを見ていきましょう。
今回は、「Excelのシートにある『Task ID』と『更新したい値(Text1)』を読み取り、Projectの該当タスクに反映させる」というシナリオで記述します。

事前準備

Excelファイルを開いた状態で、Project側のVBAエディタ(Alt + F11)から、`Tools` -> `References` で 「Microsoft Excel 16.0 Object Library」(バージョンは環境によります)にチェックを入れておいてください。

' ======================================================================
' Project Custom Field Updater
' 概要: Excelの管理表からProjectのカスタムフィールドへ値を同期する
' ターゲット: 中級者向け(エラーハンドリング・型チェック実装済み)
' ======================================================================

Option Explicit

Sub SyncExcelToProject()
    Dim xlApp As Excel.Application
    Dim xlWB As Excel.Workbook
    Dim xlSheet As Excel.Worksheet
    Dim targetProject As Project
    Dim t As Task
    Dim rowIdx As Long
    Dim taskID As Long
    Dim newValue As String
    Dim errorCount As Long
    Dim successCount As Long
    
    ' --- 設定エリア ---
    Const EXCEL_FILE_PATH As String = "C:\Temp\ProjectMaster.xlsx" ' Excelのパス
    Const START_ROW As Long = 2 ' Excelのデータ開始行
    ' ------------------

    Set targetProject = ActiveProject
    errorCount = 0
    successCount = 0

    ' 1. Excelへの接続
    On Error Resume Next
    Set xlApp = GetObject(, "Excel.Application")
    If xlApp Is Nothing Then
        Set xlApp = New Excel.Application
    End If
    On Error GoTo 0

    On Error GoTo ErrorHandler
    Set xlWB = xlApp.Workbooks.Open(EXCEL_FILE_PATH, ReadOnly:=True)
    Set xlSheet = xlWB.Sheets(1) ' 1番目のシートを使用

    Debug.Print "--- 同期処理開始 ---"

    ' 2. Excelの行をループ処理
    rowIdx = START_ROW
    Do While xlSheet.Cells(rowIdx, 1).Value <> "" ' A列が空になるまでループ
        
        ' Excelから値を取得
        taskID = xlSheet.Cells(rowIdx, 1).Value   ' A列: Task ID
        newValue = CStr(xlSheet.Cells(rowIdx, 2).Value) ' B列: 反映したい値 (Text1用)

        ' 3. Project内から該当するタスクを「Key」で探索
        Set t = FindTaskByID(targetProject, taskID)

        If Not t Is Nothing Then
            ' 4. データ整合性チェック & 書き込み
            ' ここでは Text1 フィールドへ書き込む例
            ' ※数値型フィールド(Number1等)に入れる場合は、IsNumeric()でのチェックが必須
            t.Text1 = newValue
            
            successCount = successCount + 1
        Else
            ' タスクが見つからない場合
            Debug.Print "Error: Task ID " & taskID & " がProject内に見つかりません。"
            errorCount = errorCount + 1
        End If

        rowIdx = rowIdx + 1
    Loop

    ' 5. 後処理
    xlWB.Close SaveChanges:=False
    MsgBox "同期完了!" & vbCrLf & _
           "成功: " & successCount & " 件" & vbCrLf & _
           "失敗: " & errorCount & " 件", vbInformation

    Exit Sub

ErrorHandler:
    MsgBox "予期せぬエラーが発生しました。" & vbCrLf & _
           "エラー内容: " & Err.Description, vbCritical
    If Not xlWB Is Nothing Then xlWB.Close SaveChanges:=False
End Sub

' ======================================================================
' 関数: FindTaskByID
' 目的: Task IDを元に、Project内のTaskオブジェクトを高速に検索する
' ======================================================================
Function FindTaskByID(proj As Project, id As Long) As Task
    Dim t As Task
    ' ProjectのタスクコレクションをループしてIDを照合
    ' ※大規模プロジェクトの場合、Dictionaryオブジェクトを使うとさらに高速化可能
    For Each t In proj.Tasks
        If Not t Is Nothing Then
            If t.ID = id Then
                Set FindTaskByID = t
                Exit Function
            End If
        End If
    Next t
    ' 見つからなかった場合はNothingを返す
    Set FindTaskByID = Nothing
End Function

3. アーキテクトの視点:コードの解説と「重み」

このコードが、単なる「マクロの記録」と何が違うのか。その設計思想を解説します。

① `FindTaskByID` による「疎結合」な設計

コードの中核は `FindTaskByID` 関数です。Excelの行番号とProjectの行番号を紐付けるのではなく、「Task ID」という不変の属性を使ってオブジェクトを特定しています。これにより、Project側でタスクが並べ替えられていても、Excel側でタスクが削除されていても、誤ったデータが書き込まれるリスクを最小限に抑えています。

② `On Error GoTo ErrorHandler` による「全壊」の防止

業務自動化において最も恐ろしいのは、「エラーが起きたときに、どこまで処理が進んだか分からなくなること」です。
このコードでは、エラーハンドリングを実装することで、ファイルが開けなかった場合や、予期せぬ型エラーが起きた場合に、Excelを適切に閉じてからユーザーに通知するよう設計しています。

③ パフォーマンスの勘所

`For Each t In proj.Tasks` は、プロジェクトが数千タスク規模になると少し重くなります。もし、あなたが数万行のデータを扱うモンスター級のプロジェクトを管理しているなら、最初にProjectの全タスクを `Scripting.Dictionary` に格納し、`ID` をキー、`Taskオブジェクト` を値としてメモリ上に保持してください。そうすれば、検索スピードは「線形探索」から「定数時間」へと劇的に進化します。

4. 陥りやすい罠(チェックリスト)

実装する際は、以下の3点を必ず確認してください。

  • [ ] データ型の不一致:

Projectの `Number1` フィールドに、Excelから `”N/A”` という文字列を入れようとしていませんか? これは実行時エラー(Type Mismatch)の典型例です。`IsNumeric()` 関数で必ずガードしましょう。

  • [ ] カスタムフィールドのインデックス:

VBAでは `Text1`, `Text2` … と指定しますが、Projectの画面上で「テキスト1」と表示されていても、それが本当にどのプロパティに対応しているか、一度マクロの記録で確認することをお勧めします。

  • [ ] 読み取り専用の考慮:

Excelファイルを他の誰かが開いている場合、`Workbooks.Open` でエラーになります。実運用では、ファイルがロックされていないかチェックするロジックを加えると、より「プロ」らしくなります。

最後に

Project VBAの世界は、単なる「作業の自動化」ではありません。それは、「データの流れを設計し、管理の精度を極限まで高めること」です。

今回紹介した「キーによる照合」と「エラーハンドリング」の考え方は、Projectに限らず、あらゆる業務システム開発の基礎となります。ここをマスターすれば、あなたはもう「マクロを記録する人」ではなく、「業務プロセスを設計するエンジニア」です。

一歩ずつ、確実に。応援していますよ!

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