序文:技術的負債を「資産」へと昇華させる、真のリファクタリング
かつて書かれたProject VBAのコードが、現代の肥大化したプロジェクトデータの前で悲鳴を上げていないか?
「マクロを実行すると画面が固まる」「特定の環境でしか動かない」「誰が書いたか分からないスパゲッティコード」。これらはすべて、MS Projectという特殊なソフトウェアの「オブジェクトモデルのライフサイクル」と「計算エンジンの特性」を理解していないがゆえに引き起こされる悲劇だ。
私はこれまで数多の巨大プロジェクト(数万タスク規模)の自動化を指揮してきたが、そこで得た結論は一つ。「Project VBAをExcel VBAの延長で書いてはならない」ということだ。
本記事では、数年前のレガシーなコードをいかにして現代的な、高速かつ堅牢なプロダクションコードへと転生させるか、その極意を伝授する。
—
1. 速度を殺す「三悪」を排除せよ
古いコードに共通する致命的な欠陥は、ユーザーの「操作」をシミュレートしてしまっている点にある。
① `Select` と `ActiveCell` の乱用
「タスクを選択して、その値を書き換える」という処理は、UIの描画プロセスを強制的に割り込ませる。1,000タスクあれば1,000回の画面更新が発生し、パフォーマンスは指数関数的に低下する。
解決策: オブジェクトを直接変数にセットし、バックグラウンドで処理せよ。
② 計算エンジンの放置
ProjectはExcel以上に計算負荷が高い。一つのタスクの期間を変えるだけで、後続のタスクすべてのスケジュールを再計算しようとする。
解決策: 処理開始前に計算モードを `pjManual` に変更し、最後に一括計算させる。
③ 画面更新の垂れ流し
`ScreenUpdating = False` は基本だが、Project VBAではこれだけでは不十分だ。
—
2. 現代的なアーキテクチャへの転換
保守性の高いコードとは、「関心の分離」がなされたコードだ。
データ取得、ロジック処理、UI更新を一つのプロシージャに詰め込むのは素人の仕事である。
堅牢なエラーハンドリング・パターンの確立
VBAにおいて、実行時エラーでマクロが止まることは「ユーザーの信頼を失う」ことと同義だ。特にProjectは外部ファイルやDBとの連携が多いため、不慮の事態を想定した「ガード句」の実装が必須となる。
—
3. 実践:レガシーからモダンへのリファクタリング例
以下に、古いスタイルのコードと、それを私がリファクタリングした後の「プロダクション級」のコードを示す。
【Before】よくある遅くて壊れやすいレガシーコード
Sub UpdateTasks_Legacy()
‘ 画面のあちこちを選択しながら処理する、典型的な非効率コード
Dim i As Integer
For i = 1 To ActiveProject.Tasks.Count
SelectRow Row:=i
If ActiveSelection.Tasks(1).PercentComplete < 100 Then
ActiveSelection.Tasks(1).Text1 = "未完了"
End If
Next i
MsgBox "完了"
End Sub
【After】高速・堅牢・モダンなリファクタリングコード
Option Explicit
”’
”’
Public Sub UpdateTasks_Modernized()
Dim targetProject As Project
Set targetProject = ActiveProject
‘ 1. パフォーマンス・ブースト開始
TogglePerformanceMode True
On Error GoTo ErrorHandler
‘ 2. オブジェクトへの直接アクセス(Selectは絶対に使用しない)
Dim tsk As Task
For Each tsk In targetProject.Tasks
‘ Project特有の注意点: 空白行(Nothing)のスキップは必須
If Not tsk Is Nothing Then
‘ 3. ロジックの適用
UpdateTaskStatus tsk
End If
Next tsk
MsgBox “正常に更新されました。”, vbInformation
CleanExit:
‘ 4. パフォーマンス・ブースト解除(エラー時も必ず通す)
TogglePerformanceMode False
Exit Sub
ErrorHandler:
Dim errDetail As String
errDetail = “Error: ” & Err.Number & ” / ” & Err.Description
Debug.Print errDetail
MsgBox “予期せぬエラーが発生しました。処理を中断します。” & vbCrLf & errDetail, vbCritical
Resume CleanExit
End Sub
”’
”’
Private Sub UpdateTaskStatus(ByRef tsk As Task)
‘ 外部連携や複雑な条件分岐はここに集約する
If tsk.PercentComplete < 100 Then
If tsk.Text1 <> “未完了” Then
tsk.Text1 = “未完了”
End If
End If
End Sub
”’
”’
Private Sub TogglePerformanceMode(ByVal isBusy As Boolean)
With Application
If isBusy Then
.ScreenUpdating = False
.Calculation = pjManual ‘ 自動計算を停止
Else
.ScreenUpdating = True
.Calculation = pjAutomatic ‘ 自動計算を再開し再計算を実行
End If
End With
End Sub
—
4. プロフェッショナルが教える「運用の急所」
外部データ(CSV/DB)連携の注意点
Project VBAでCSVを読み込む際、`Open`ステートメントで自前でパースするのは時間の無駄であり、バグの温床だ。
モダンな設計では、「FileSystemObject」を使用してストリームとして読み込むか、ADO(ActiveX Data Objects)を使用してDBライクに処理を行うべきである。
また、外部連携時は以下の「3層チェック」を徹底せよ。
1. ファイル存在チェック: 実行前にパスの妥当性を確認。
2. 排他制御チェック: 誰かがファイルを開いていないか?
3. データ整合性チェック: Projectの「タスクID」や「独自のUID」が一致しているか?
ライフサイクルの管理
`Set obj = Nothing` を明示的に書くべきか?という議論があるが、Project VBAにおいては「COMオブジェクトの参照カウント」を意識すべきだ。特にProjectを外部アプリ(Excel等)から操作する場合、参照を解放し忘れるとProjectのプロセスがゾンビのように残り続け、ファイルがロックされる原因となる。
—
結言:コードは「静的な文書」ではなく「動く生命体」である
リファクタリングとは、単に見た目を綺麗にすることではない。
プロジェクトの規模が拡大しても、データが複雑化しても、常に一定のパフォーマンスと正確性を維持し続ける「強靭な骨組み」を作り直す作業だ。
今回紹介した「計算制御」「オブジェクト直接参照」「エラーハンドリングの定型化」を徹底するだけで、あなたのProject VBAは劇的に進化する。レガシーコードという負債を、組織の意思決定を支える強力な武器へと変貌させるのは、他でもないエンジニアであるあなたの責任なのだ。
この知見が、あなたのプロジェクトを成功に導く一助となることを願っている。
