Project VBAを掌握せよ:標準モジュール依存からの脱却と「タスク管理エンジン」の構築
MS ProjectのVBA開発で、多くのエンジニアが陥る罠がある。それは、標準モジュールに「手続き型」のコードを羅列し、`ActiveProject.Tasks(i)` を直書きするような、スパゲッティコードの量産だ。
プロフェッショナルであれば理解しているはずだ。MS Projectのタスクは単なる行データではない。 それは、リソース、依存関係、カレンダー、そしてコストという多次元の属性を持つ「動的なオブジェクト」である。
今回は、標準モジュール依存を排し、クラスモジュールを用いて堅牢かつ再利用可能な「タスク管理エンジン」を設計する手法を伝授する。
—
1. なぜ「手続き型」で開発してはいけないのか
標準モジュールだけで書かれたVBAは、保守の段階で必ず破綻する。理由は明確だ。
- カプセル化の欠如: タスクの属性(ID、名前、開始日)を直接参照すると、仕様変更時に全コードを修正する必要がある。
- エラーハンドリングの脆弱性: MS Projectのオブジェクトモデルは非常にデリケートだ。存在しないTask IDにアクセスした際、手続き型のコードは即座に停止する。
- テストの困難さ: 単体テストが不可能。ビジネスロジックとUI(Projectのグリッド)が癒着しているためだ。
我々は、「タスクをオブジェクトとして抽象化する」ことから始める。
—
2. アーキテクチャの核心:`TaskWrapper` クラスの設計
まず、クラスモジュール `clsTask` を作成し、MS Projectの `Task` オブジェクトをラップする。これにより、データアクセスのインターフェースを統一できる。
クラスモジュール: `clsTask`
‘ 必要な属性をプロパティとして定義
Private pTask As MSProject.Task
Public Property Set TaskObject(t As MSProject.Task)
Set pTask = t
End Property
‘ タスク名を取得・設定(ガード付き)
Public Property Get Name() As String
If Not pTask Is Nothing Then Name = pTask.Name
End Property
‘ 進捗率を安全に更新するロジック
Public Sub UpdateProgress(ByVal percent As Integer)
If pTask Is Nothing Then Err.Raise 91, “clsTask”, “Taskが割り当てられていません。”
pTask.PercentComplete = percent
End Sub
‘ タスクの妥当性チェック(エンジンの心臓部)
Public Function IsValid() As Boolean
If pTask Is Nothing Then IsValid = False: Exit Function
‘ 完了日が開始日より前なら不正とみなす例
If pTask.Finish < pTask.Start Then IsValid = False Else IsValid = True
End Function
---
3. 「エンジン」によるタスク操作の抽象化
次に、これらのオブジェクトを管理する「エンジン」クラスを作成する。これにより、プロジェクトファイルとの疎結合を実現する。
クラスモジュール: `clsProjectEngine`
Private pProject As MSProject.Project
Public Sub Initialize(prj As MSProject.Project)
Set pProject = prj
End Sub
‘ タスクをラッパーに変換して取得する
Public Function GetTask(ByVal taskID As Long) As clsTask
Dim wrapped As New clsTask
On Error Resume Next
wrapped.TaskObject = pProject.Tasks(taskID)
On Error GoTo 0
Set GetTask = wrapped
End Function
—
4. プロダクション環境における「掟」
この設計を採用する上で、必ず守るべきルールが3つある。
① オブジェクトのライフサイクルを意識せよ
`MSProject.Task` はProjectのメモリ空間に強く依存する。長時間ループを回す際、`Set task = Nothing` を明示的に行わないと、MS Project特有の「メモリリークによるパフォーマンス低下」を招く。
② ファイル/DB連携は「DTO」を介せ
外部からデータを読み込む際、直接 `Task.Name = …` と書くのはNGだ。必ず「データ受け渡し用のクラス(DTO)」を経由させ、型変換やバリデーションを通過させること。これにより、外部システム(ExcelやSQL Server)の変更がプロジェクト本体に波及するのを防げる。
③ ユーザー体験を損なわないエラー通知
`On Error Resume Next` を多用するプログラマがいるが、これは悪手だ。必ず独自のエラーハンドリングクラスを作り、発生したエラーをログファイルに書き出す設計にせよ。
—
結論:コードは「資産」である
今回紹介したクラスモジュール設計は、一見するとコード量が増えたように見えるだろう。しかし、考えてみてほしい。
「大規模なスケジュール変更が発生した際、修正箇所が1カ所で済む」という事実は、納期に追われる現場において何物にも代えがたい「安心」をもたらす。
VBAは、単なるマクロの集まりではない。適切に設計されたVBAは、企業のプロジェクト管理を支える「エンタープライズ・エンジン」になり得る。今日から、手続き型のコードを卒業し、オブジェクト指向の設計へ舵を切ろう。
貴方の書くコードが、明日の業務効率を劇的に変えることを期待している。
