【テクニカル・上級編】【実務中級】Taskの「Notes」フィールドへの一括書き込みと、RTF形式の制御によるレポート作成 – Project VBA解析バイブル

スポンサーリンク

【実務中級】Taskの「Notes」フィールドへの一括書き込みと、RTF形式の制御によるレポート作成

MS ProjectのVBA開発において、多くの開発者が絶望する壁の一つが「Notes(メモ)フィールドの制御」だ。
GUI上では何気なく入力している改行やフォント装飾も、裏側ではRich Text Format(RTF)のバイナリに近い文字列としてシリアライズされている。これをVBAから無造作に書き込むと、文字化けを起こすか、最悪の場合はプロジェクトファイルそのものが破損する。

本稿では、数千件規模のタスクを持つ巨大プロジェクトにおいて、メモリリークを完全に抑制しつつ、構造化されたレポートテキストをNotesフィールドへ一括流し込むための極限の知見を公開する。

1. Notesフィールドの深層:なぜプレーンテキストではなくRTFなのか?

MS Projectの`Task.Notes`プロパティは、内部的にRTF(Rich Text Format)を保持している。
VBAから単に `task.Notes = “進捗遅延:要確認”` と代入した場合、Projectエンジン側が自動的に最低限のRTFヘッダーを付与してラップする。しかし、これを数千件のループ内で高速実行すると、COMオブジェクトのマーシャリングコストと文字列の暗黙的なエンコード変換により、パフォーマンスが著しく劣化する。

さらに、レポートとして視認性の高い「太字」「色分け」「インデント」を動的に挿入したい場合、開発者は自前で正確なRTF構文を組み立て、それを流し込む必要がある。

2. メモリ最適化とオブジェクトモデルのライフサイクル管理

数万行のタスクを走査する際、最も恐れるべきは COMオブジェクトの解放漏れによるメモリ肥大化 である。
VBAのガベージコレクションは遅延実行されるため、ループ内で `ActiveProject.Tasks` を直接叩き続けると、ExcelやProjectのプロセスがメモリリークを起こしてフリーズする。

以下の鉄則を遵守せよ:
1. ループの前にオブジェクトを変数にキャッシュする。
2. 処理終了後、オブジェクト変数には明示的に `Nothing` を代入する。
3. 画面描画(`ScreenUpdating` や `Calculation` 相当の機能)を一時停止し、描画コストを排除する。

3. 実装コード:構造化RTF生成と一括書き込みエンジン

以下のコードは、実務の現場で即座に耐えうる堅牢性を持ったプロシージャである。
指定されたタスクに対し、進捗状況に応じた色付きの構造化RTFを生成し、Notesフィールドへ書き込む。

Option Explicit

‘ ==============================================================================
‘ módulo名: modTaskNotesExporter
‘ 概要 : MS Project タスクのNotesフィールドへの構造化RTF一括書き込みエンジン
‘ 著作者 : チーフアーキテクト
‘ ==============================================================================

Public Sub ExportStructuredReportToNotes()
Dim prj As MSProject.Project
Dim tsk As MSProject.Task
Dim tasksColl As MSProject.Tasks
Dim lngCounter As Long
Dim lngTotal As Long

‘ パフォーマンス最適化:UI更新の停止(Projectには直接的なScreenUpdatingがないため、
‘ アプリケーションレベルの警告抑制とビューの固定を活用)
On Error GoTo ErrorHandler
Application.Cursor = pjWait

Set prj = ActiveProject
Set tasksColl = prj.Tasks
lngTotal = tasksColl.Count

‘ 巨大プロジェクトにおけるトランザクション的処理の開始
MsgBox “Notesフィールドへのレポート一括書き込みを開始します。対象タスク数: ” & lngTotal, vbInformation

For lngCounter = 1 To lngTotal
Set tsk = tasksColl(lngCounter)

‘ 概要サマリー行や空行、削除済みタスクの除外
If Not tsk Is Nothing Then
If tsk.Summary = False And tsk.Active = True Then

‘ 独自ロジックによる構造化RTF文字列の生成
tsk.Notes = BuildCustomRtfNote(tsk)

End If
End If

‘ ループ内でのメモリ解放の徹底
Set tsk = Nothing
Next lngCounter

Application.Cursor = pjDefault
MsgBox “すべてのタスクノートの更新が正常に完了しました。”, vbInformation
Exit Sub

ErrorHandler:
Application.Cursor = pjDefault
MsgBox “致命的なエラーが発生しました: ” & Err.Description & ” (Line: ” & Erl & “)”, vbCritical

‘ 異常系におけるオブジェクトのクリーンアップ
Set tsk = Nothing
Set tasksColl = Nothing
Set prj = Nothing
End Sub

‘ ==============================================================================
‘ 関数名 : BuildCustomRtfNote
‘ 概要 : プレーンテキストをMS Projectが解釈可能な安全なRTF構文にラップする
‘ ==============================================================================
Private Function BuildCustomRtfNote(ByVal targetTask As MSProject.Task) As String
Dim rtfBuilder As String
Dim statusColor As String
Dim statusText As String

‘ 進捗に応じたカラーコードの割り当て(RTF用カラーテーブル:紅、緑、青)
‘ ※RTFの仕様上、\colortblには初期のエントリが必要
Select Case targetTask.PercentComplete
Case 100
statusColor “\cf2 ” ‘ 緑 (完了)
statusText = “【STATUS: COMPLETED】”
Case Is > 0
statusColor = “\cf3 ” ‘ 青 (進行中)
statusText = “【STATUS: IN PROGRESS】”
Case Else
statusColor = “\cf1 ” ‘ 赤 (未着手・遅延リスク)
statusText = “【STATUS: NOT STARTED】”
End Select

‘ RTFヘッダーの構築
‘ \rtf1\ansi\ansicp932 (日本語Shift-JIS環境対応)
rtfBuilder = “{\rtf1\ansi\ansicp932” & _
“{\colortbl ;\red255\green0\blue0;\red0\green128\blue0;\red0\green0\blue255;}” & _
“\f0\fs20 ”

‘ タイトル行の挿入(太字)
rtfBuilder = rtfBuilder & “\b ” & EscapeRtfText(targetTask.Name) & “\b0\par”

‘ ステータス行の挿入(色付き)
rtfBuilder = rtfBuilder & statusColor & “\b ” & statusText & “\b0\cf0\par”

‘ メタデータの埋め込み
rtfBuilder = rtfBuilder & “\fs18 —————————————-\par”
rtfBuilder = rtfBuilder & “開始予定: ” & Format(targetTask.Start, “yyyy/mm/dd”) & “\par”
rtfBuilder = rtfBuilder & “終了予定: ” & Format(targetTask.Finish, “yyyy/mm/dd”) & “\par”
rtfBuilder = rtfBuilder & “実工数: ” & targetTask.ActualWork / 60 & “H (予定: ” & targetTask.Work / 60 & “H)\par”

‘ フッター(閉じる)
rtfBuilder = rtfBuilder & “}”

BuildCustomRtfNote = rtfBuilder
End Function

‘ ==============================================================================
‘ 関数名 : EscapeRtfText
‘ 概要 : 日本語文字列およびRTF制御文字のエスケープ処理
‘ ==============================================================================
Private Function EscapeRtfText(ByVal rawText As String) As String
Dim i As Long
Dim c As String
Dim result As String

result = “”
For i = 1 To Len(rawText)
c = Mid(rawText, i, 1)
Select Case c
Case “\”
result = result & “\\”
Case “{”
result = result & “\{”
Case “}”
result = result & “\}”
Case Else
‘ 2バイト文字(日本語)や通常のASCII文字の処理
‘ MS ProjectのRTFエンジンはANSIコードページ(932)を前提とするため、
‘ 特殊なUnicode変換を行わなくてもShift-JISとして渡せば解釈される。
result = result & c
End Select
Next i

EscapeRtfText = result
End Function

4. エンジニアリング上の注意点とトラブルシューティング

1. Shift-JIS(CP932)とUnicodeの罠

コード中のRTFヘッダーに `\ansicp932` を指定している点に注目してほしい。これを怠ると、日本語環境のOSであってもマルチバイト文字が文字化けし、メモ欄がゴミデータで埋め尽くされる。モダンな環境であっても、MS ProjectのCOMインターフェースはレガシーな文字コード体系を引きずっている現実を忘れてはならない。

2. トランザクションとパフォーマンスのトレードオフ

数万件のタスクを保持するプロジェクトファイルにおいて、毎回の代入はディスクI/Oとオブジェクトキャッシュのフラッシュを引き起こす。
もしパフォーマンスが許容値を超えて低下する場合は、処理を一定件数(例: 500件ごと)に区切り、`DoEvents` を挟んでUIスレッドの飢餓を防ぐ設計に拡張せよ。ただし、その場合はユーザーによる中断処理(Cancelボタンの監視など)のハンドリングが必須となる。

総括

VBAを用いたMS Projectの自動化において、単なるプロパティの読み書きにとどまらず、内部フォーマット(RTF)やメモリのライフサイクルまで制御下置くことこそが、真にプロフェッショナルなエンジニアの証左である。
本稿で示したアーキテクチャを武器に、貴殿のプロジェクト管理基盤を極限まで最適化せよ。

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