【実務・中級編】プロジェクトの「リソースプール」を複数人で同時編集する際の競合回避VBA – Project VBA解析バイブル

スポンサーリンク

Projectリソースプールの同時編集、その危険とVBAで築く堅牢な競合回避策

諸君、業務自動化の最前線でProjectと格闘する開発者たちよ。
私はこれまで数多のプロジェクトを見てきたが、常に聞かされる悲鳴の一つに「リソースプールの共有」がある。特に複数人でリソースの登録・割り当て・コスト管理を行おうとした際、「ファイルがロックされている」「変更が上書きされた」「データが破損した」といった事態に遭遇した経験を持つ者は少なくないだろう。

Project標準の共有機能は確かに存在する。しかし、それは「同時に書き込む」というユースケースを想定したものではない。せいぜい「共有元からの更新を取り込む」という一方通行の連携に過ぎず、真の同時編集には耐えられない。

今日は、その甘い幻想を打ち破り、Projectの「リソースプール」というデリケートな存在を、複数人でいかに堅牢に管理し、競合を回避するか、その極限の知見を伝授する。一般的なリファレンスの引き写しではない、現場で血と汗とデバッグの涙を流してきた者にしか語り得ない、魂のこもった設計思想と実践的VBAコードをここに開示しよう。

1. Projectリソースプールの同時編集が抱える根本的な問題

まず、我々が直面している問題の本質を理解しなければならない。
Microsoft Projectのファイル形式(.mpp)は、本質的にシングルユーザー編集を前提とした構造だ。これはデータベースではない。
複数のユーザーが同時に同じ`.mpp`ファイルを開き、それぞれが書き込みを試みた場合、ファイルシステムレベルでの競合が発生し、結果として最も遅く保存したユーザーの変更が他のユーザーの変更を上書きするか、最悪の場合、ファイル自体が破損する。これが現実だ。

「Projectの標準機能でリソースプール共有があるじゃないか」と反論する者もいるだろう。しかし、その実態を見てみよう。

1.1. Project標準共有機能の限界

Projectには「リソースプールを共有する」機能が存在する。あるProjectファイルを「リソースプール」として設定し、他のProjectファイル(共有元)がそのリソースプールを参照する形式だ。

しかし、この機能は以下の点で「同時編集」には全く対応していない。

  • 排他制御の欠如: 共有元のProjectファイルを開いている間、リソースプールファイルは通常「読み取り専用」で開かれる。これは競合を避けるための最低限の防御策だが、同時に「複数人での書き込み」を完全に排除する。
  • 非同期性: リソースプールに変更があった場合、共有元のProjectファイルがその変更を取り込むには、明示的な操作(「リソースの更新」)が必要だ。リアルタイムな同期は存在しない。
  • データの一貫性: 複数人が異なる共有元からリソースプールを参照し、それぞれが変更を試みた場合、どの変更が最終的にリソースプールに反映されるのか、そのロジックは極めて曖昧で予測不能だ。

つまり、Projectファイル自体をデータベースのように扱うのは、構造上無理があるのだ。
では、VBAでこの限界を突破し、堅牢な同時編集環境を構築するにはどうすればいいのか?

2. 真に堅牢な共有リソースプールの設計思想

Projectファイルがデータベースではないという事実を受け入れることから始める。その上で、VBAでできる最善の策を講じる。それは、「共有」ではなく「連携」と捉えるパラダイムシフトだ。

我々が目指すのは、以下の原則に基づいたシステムだ。

1. 一貫性の確保: データが常に整合性を保っていること。
2. 可用性の維持: 必要な時にリソース情報にアクセスできること。
3. 排他制御の徹底: 複数ユーザーによる同時書き込みを許さないこと。
4. リカバリ可能性: データ破損や予期せぬエラーから復旧できること。

2.1. 排他制御の基本原則

Projectファイルという単一の資源に対して、複数の書き込み要求が発生する場合、最もシンプルかつ確実な排他制御は「ロック」だ。

  • ロックの粒度: Projectファイル全体をロックする。リソース個別のロックは、VBAとファイルシステムレベルでは現実的ではない。
  • ロックの生成: 誰が、いつ、何をロックしたかを記録する「ロックファイル」を作成する。
  • ロックの解除: 編集が完了したら、速やかにロックを解除する。エラー終了時にも解除を試みる。
  • デッドロック回避: ロックが解除されないまま放置された場合に備え、一定時間経過後に強制解除できるメカニズム、またはユーザーへの選択肢提示が必要だ。

2.2. バックアップ戦略の重要性

どんなに堅牢な排他制御を実装しても、ネットワーク障害、PCのフリーズ、人為的ミスなど、予期せぬ事態は必ず発生する。その際にデータを失わないための最後の砦が「バックアップ」だ。

  • 書き込み前のバックアップ: リソースプールファイルを開き、書き込みを開始する直前に必ずバックアップを作成する。これにより、変更中に問題が発生しても直前の状態に復元できる。
  • 世代管理: 複数のバックアップを保持し、過去の任意の時点に戻れるようにする。

最終的には、Projectファイルをデータストアとして直接共有するのではなく、リソース情報のみをデータベース(例えば、SQL ServerやAccess DB)で管理し、Projectファイルからはそのデータベースと連携するというアプローチが最も堅牢である。しかし、今回はVBAで完結させるファイルベースの競合回避に焦点を当てる。

3. VBAで実現する堅牢なファイル操作術

それでは、具体的なVBAコードを通じて、上記で述べた設計思想を実装する方法を見ていこう。
ここでは、以下の機能を備えたVBAモジュールを構築する。

1. リソースプールを開く際の排他制御:

  • ロックファイルの存在を確認し、競合を検知する。
  • ロックファイルが存在しない場合、ロックファイルを作成してからリソースプールファイルを開く。
  • ロックファイルが存在する場合、誰がロックしているかを通知し、ユーザーに選択肢(待機、読み取り専用で開く、キャンセル)を提供する。

2. リソースプールを閉じる際のロック解除:

  • Projectファイルを閉じると同時にロックファイルを削除する。

3. 書き込み前の自動バックアップ:

  • リソースプールを開く前に、必ずバックアップを作成する。

3.1. プロダクションコード例

以下のコードは、`ThisProject`モジュールや標準モジュールに配置し、適宜呼び出すことで利用できる。

‘————————————————————————————————————————
‘ Module: modResourcePoolManager
‘ Purpose: Projectリソースプールの排他制御、バックアップ、堅牢なファイル操作を行うためのモジュール
‘ Author: [Your Name/Company Name] (伝説のチーフアーキテクトより継承)
‘ Date: 2023-10-27
‘————————————————————————————————————————

Option Explicit

‘=== 定数定義 =========================================================================================================
Private Const C_LOCK_FILE_EXTENSION As String = “.lock” ‘ ロックファイルの拡張子
Private Const C_BACKUP_FOLDER_NAME As String = “ResourcePool_Backup” ‘ バックアップフォルダ名
Private Const C_BACKUP_FILE_PREFIX As String = “Backup_” ‘ バックアップファイル名のプレフィックス
Private Const C_LOCK_TIMEOUT_MINUTES As Long = 30 ‘ ロックファイルのタイムアウト時間(分)。これを超えたロックは強制解除の候補。

‘=== ユーザー定義型 ===================================================================================================
‘ ロックファイルに書き込む情報
Private Type LockInfo
UserName As String
MachineName As String
LockDateTime As Date
ProjectFilePath As String
End Type

‘=== Public 関数 ======================================================================================================

”’

”’ 指定されたリソースプールファイルを開き、排他制御とバックアップ処理を実行します。
”’

”’ 開くリソースプールファイルのフルパス。 ”’ Trueの場合、読み取り専用で開きます。排他制御は行われません。 ”’ 開いたProjectオブジェクト。エラーが発生した場合はNothing。
Public Function OpenResourcePoolWithLock(ByVal resourcePoolPath As String, Optional ByVal isReadOnly As Boolean = False) As Project
Dim fso As Object ‘ FileSystemObject
Dim lockFilePath As String
Dim proj As Project
Dim lockInfo As LockInfo
Dim currentLockInfo As LockInfo

Set fso = CreateObject(“Scripting.FileSystemObject”)
lockFilePath = resourcePoolPath & C_LOCK_FILE_EXTENSION

‘ リソースプールファイルが存在しない場合はエラー
If Not fso.FileExists(resourcePoolPath) Then
MsgBox “指定されたリソースプールファイルが見つかりません。” & vbCrLf & resourcePoolPath, vbExclamation, “エラー”
Set OpenResourcePoolWithLock = Nothing
Exit Function
End If

On Error GoTo ErrorHandler

‘ 読み取り専用で開く場合、排他制御はスキップ
If isReadOnly Then
Set proj = Application.FileOpenEx(Name:=resourcePoolPath, ReadOnly:=True, AddToMRU:=False)
Set OpenResourcePoolWithLock = proj
Exit Function
End If

‘ — ロックファイルの確認と処理 —
If fso.FileExists(lockFilePath) Then
‘ ロックファイルが存在する場合
Call ReadLockInfo(lockFilePath, currentLockInfo) ‘ 既存のロック情報を読み込む

‘ タイムアウトチェック
If DateDiff(“n”, currentLockInfo.LockDateTime, Now) > C_LOCK_TIMEOUT_MINUTES Then
Dim timeoutResponse As VbMsgBoxResult
timeoutResponse = MsgBox(“リソースプールは ” & vbCrLf & _
“ユーザー: ” & currentLockInfo.UserName & vbCrLf & _
“マシン: ” & currentLockInfo.MachineName & vbCrLf & _
“日時: ” & Format(currentLockInfo.LockDateTime, “yyyy/mm/dd hh:nn:ss”) & vbCrLf & _
“によってロックされています。” & vbCrLf & _
“ロックが ” & C_LOCK_TIMEOUT_MINUTES & “分以上経過しています。強制的にロックを解除しますか?” & vbCrLf & _
“はい: ロックを解除し、開く” & vbCrLf & _
“いいえ: 読み取り専用で開く” & vbCrLf & _
“キャンセル: 処理を中止”, _
vbYesNoCancel + vbExclamation, “リソースプールロック競合 (タイムアウト)”)
Select Case timeoutResponse
Case vbYes
‘ 強制解除して開く
fso.DeleteFile lockFilePath, True ‘ ロックファイルを削除
‘ ロックを再生成し、ファイルを開く処理へ進む
Case vbNo
‘ 読み取り専用で開く
Set proj = Application.FileOpenEx(Name:=resourcePoolPath, ReadOnly:=True, AddToMRU:=False)
Set OpenResourcePoolWithLock = proj
Exit Function
Case vbCancel
‘ 処理をキャンセル
MsgBox “リソースプールのオープンをキャンセルしました。”, vbInformation, “キャンセル”
Set OpenResourcePoolWithLock = Nothing
Exit Function
End Select
Else
‘ ロックが有効な場合、ユーザーに選択を促す
Dim response As VbMsgBoxResult
response = MsgBox(“リソースプールは現在、” & vbCrLf & _
“ユーザー: ” & currentLockInfo.UserName & vbCrLf & _
“マシン: ” & currentLockInfo.MachineName & vbCrLf & _
“日時: ” & Format(currentLockInfo.LockDateTime, “yyyy/mm/dd hh:nn:ss”) & vbCrLf & _
“によってロックされています。” & vbCrLf & _
“読み取り専用で開きますか?” & vbCrLf & _
“はい: 読み取り専用で開く” & vbCrLf & _
“いいえ: 処理を中止”, _
vbYesNo + vbExclamation, “リソースプールロック競合”)

If response = vbYes Then
Set proj = Application.FileOpenEx(Name:=resourcePoolPath, ReadOnly:=True, AddToMRU:=False)
Set OpenResourcePoolWithLock = proj
Exit Function
Else
MsgBox “リソースプールのオープンをキャンセルしました。”, vbInformation, “キャンセル”
Set OpenResourcePoolWithLock = Nothing
Exit Function
End If
End If
End If

‘ — バックアップの作成 —
Call CreateResourcePoolBackup(resourcePoolPath)

‘ — ロックファイルの作成 —
With lockInfo
.UserName = Application.UserName
.MachineName = Environ(“COMPUTERNAME”)
.LockDateTime = Now
.ProjectFilePath = resourcePoolPath
End With
Call WriteLockInfo(lockFilePath, lockInfo)

‘ — リソースプールファイルを開く —
Set proj = Application.FileOpenEx(Name:=resourcePoolPath, ReadOnly:=False, AddToMRU:=False)

Set OpenResourcePoolWithLock = proj
Exit Function

ErrorHandler:
MsgBox “リソースプールのオープン中にエラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “説明: ” & Err.Description, vbCritical, “エラー”
If Not proj Is Nothing Then
If Not proj.Saved Then proj.Close SaveChanges:=False ‘ 未保存の変更があれば閉じる
End If
‘ エラー発生時はロックファイルが残らないように削除を試みる
If fso.FileExists(lockFilePath) Then
On Error Resume Next ‘ 削除失敗は致命的ではないため
fso.DeleteFile lockFilePath, True
On Error GoTo 0
End If
Set OpenResourcePoolWithLock = Nothing
Set fso = Nothing
End Function

”’

”’ 指定されたProjectオブジェクトを閉じ、ロックファイルを削除します。
”’

”’ 閉じるProjectオブジェクト。 ”’ 変更を保存するかどうか。 Public Sub CloseResourcePoolAndReleaseLock(ByVal proj As Project, Optional ByVal saveChanges As Boolean = True)
Dim fso As Object ‘ FileSystemObject
Dim lockFilePath As String

If proj Is Nothing Then Exit Sub

Set fso = CreateObject(“Scripting.FileSystemObject”)
lockFilePath = proj.FullName & C_LOCK_FILE_EXTENSION

On Error GoTo ErrorHandler

‘ Projectを閉じる前に変更を保存
If saveChanges Then
proj.Save
End If
proj.Close SaveChanges:=False ‘ Closeメソッドでファイルロックが解除される

‘ ロックファイルを削除
If fso.FileExists(lockFilePath) Then
fso.DeleteFile lockFilePath, True
End If

Set fso = Nothing
Exit Sub

ErrorHandler:
MsgBox “リソースプールのクローズ中にエラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “説明: ” & Err.Description, vbCritical, “エラー”
‘ エラー発生時もロックファイルの削除は試みる
If fso.FileExists(lockFilePath) Then
On Error Resume Next ‘ 削除失敗は致命的ではないため
fso.DeleteFile lockFilePath, True
On Error GoTo 0
End If
Set fso = Nothing
End Sub

”’

”’ リソースプールファイルのバックアップを作成します。
”’

”’ バックアップするリソースプールファイルのフルパス。 Public Sub CreateResourcePoolBackup(ByVal resourcePoolPath As String)
Dim fso As Object ‘ FileSystemObject
Dim parentFolder As String
Dim backupFolderPath As String
Dim fileName As String
Dim backupFileName As String

Set fso = CreateObject(“Scripting.FileSystemObject”)
parentFolder = fso.GetParentFolderName(resourcePoolPath)
fileName = fso.GetFileName(resourcePoolPath)
backupFolderPath = parentFolder & “\” & C_BACKUP_FOLDER_NAME

On Error GoTo ErrorHandler

‘ バックアップフォルダが存在しない場合は作成
If Not fso.FolderExists(backupFolderPath) Then
fso.CreateFolder backupFolderPath
End If

‘ バックアップファイル名を生成 (例: Backup_YYYYMMDD_HHMMSS_ResourcePool.mpp)
backupFileName = C_BACKUP_FILE_PREFIX & Format(Now, “YYYYMMDD_HHMMSS”) & “_” & fileName

‘ 元のファイルをバックアップフォルダにコピー
fso.CopyFile resourcePoolPath, backupFolderPath & “\” & backupFileName, True

‘ Optional: 古いバックアップファイルを削除するロジックを追加することも可能(例: 7日以上前のファイルを削除)

Set fso = Nothing
Exit Sub

ErrorHandler:
MsgBox “リソースプールのバックアップ作成中にエラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “説明: ” & Err.Description, vbCritical, “エラー”
Set fso = Nothing
End Sub

‘=== Private ヘルパー関数 =============================================================================================

”’

”’ ロックファイルからロック情報を読み込みます。
”’

”’ ロックファイルのフルパス。 ”’ 読み込んだロック情報を格納するLockInfo型変数。 Private Sub ReadLockInfo(ByVal lockFilePath As String, ByRef info As LockInfo)
Dim fileNum As Integer
Dim line As String
Dim parts() As String

fileNum = FreeFile
On Error GoTo ErrorHandler
Open lockFilePath For Input As #fileNum
Line Input #fileNum, line ‘ ユーザー名
info.UserName = line
Line Input #fileNum, line ‘ マシン名
info.MachineName = line
Line Input #fileNum, line ‘ ロック日時
info.LockDateTime = CDate(line)
Line Input #fileNum, line ‘ プロジェクトファイルパス
info.ProjectFilePath = line
Close #fileNum
Exit Sub

ErrorHandler:
MsgBox “ロックファイルの読み込み中にエラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “説明: ” & Err.Description & vbCrLf & _
“ロックファイルが破損している可能性があります。手動での確認を推奨します。”, vbCritical, “エラー”
If fileNum > 0 Then Close #fileNum
‘ エラーが発生した場合は、初期値のままとする
info.UserName = “UNKNOWN”
info.MachineName = “UNKNOWN”
info.LockDateTime = CDate(“1900/01/01”)
info.ProjectFilePath = “UNKNOWN”
End Sub

”’

”’ ロックファイルにロック情報を書き込みます。
”’

”’ ロックファイルのフルパス。 ”’ 書き込むLockInfo型変数。 Private Sub WriteLockInfo(ByVal lockFilePath As String, ByRef info As LockInfo)
Dim fileNum As Integer
fileNum = FreeFile
On Error GoTo ErrorHandler
Open lockFilePath For Output As #fileNum
Print #fileNum, info.UserName
Print #fileNum, info.MachineName
Print #fileNum, Format(info.LockDateTime, “yyyy/mm/dd hh:nn:ss”)
Print #fileNum, info.ProjectFilePath
Close #fileNum
Exit Sub

ErrorHandler:
MsgBox “ロックファイルの書き込み中にエラーが発生しました。” & vbCrLf & “エラー番号: ” & Err.Number & vbCrLf & “説明: ” & Err.Description, vbCritical, “エラー”
If fileNum > 0 Then Close #fileNum
End Sub

3.2. 利用例

このモジュールをProjectに組み込んだら、以下のように利用できる。

‘————————————————————————————————————————
‘ Module: SampleUsage
‘ Purpose: modResourcePoolManagerモジュールの使用例
‘————————————————————————————————————————

Option Explicit

Sub OpenAndEditResourcePool()
Dim resourcePoolPath As String
Dim projResourcePool As Project
Dim res As Resource

‘ ここにリソースプールファイルのパスを指定
resourcePoolPath = “C:\Project\SharedResourcePool.mpp”
‘ resourcePoolPath = “\\Server\SharedProjects\MasterResourcePool.mpp” ‘ ネットワークパスの例

‘ リソースプールを開く
Set projResourcePool = modResourcePoolManager.OpenResourcePoolWithLock(resourcePoolPath)

If Not projResourcePool Is Nothing Then
‘ 正常にリソースプールが開かれた場合
MsgBox projResourcePool.Name & ” が排他モードで開かれました。リソースの編集が可能です。”, vbInformation, “成功”

‘ — ここにリソースプールに対する編集処理を記述 —
‘ 例: 新しいリソースを追加する
On Error Resume Next ‘ リソース名が重複してもエラーにならないように
Set res = projResourcePool.Resources.Add(“新しいリソース名”)
If Err.Number <> 0 Then
Debug.Print “リソース ‘新しいリソース名’ は既に存在するか、追加に失敗しました: ” & Err.Description
Err.Clear
Else
res.MaxUnits = 1
res.StandardRate = 1000
res.AccrueAt = pjAccrueStart
Debug.Print “リソース ‘新しいリソース名’ を追加しました。”
End If
On Error GoTo 0

‘ 例: 既存のリソースの情報を更新する
Set res = projResourcePool.Resources(“既存リソース名”)
If Not res Is Nothing Then
res.Notes = “VBAで更新されたメモです。”
Debug.Print “リソース ‘既存リソース名’ を更新しました。”
End If
‘ ————————————————–

‘ 編集が完了したら、変更を保存してロックを解除
modResourcePoolManager.CloseResourcePoolAndReleaseLock projResourcePool, saveChanges:=True
MsgBox “リソースプール ” & projResourcePool.Name & ” の編集が完了し、ロックが解除されました。”, vbInformation, “完了”
Else
MsgBox “リソースプールを開けませんでした。”, vbExclamation, “失敗”
End If

Set projResourcePool = Nothing
Set res = Nothing
End Sub

Sub OpenResourcePoolReadOnly()
Dim resourcePoolPath As String
Dim projResourcePool As Project

resourcePoolPath = “C:\Project\SharedResourcePool.mpp”

‘ 読み取り専用で開く
Set projResourcePool = modResourcePoolManager.OpenResourcePoolWithLock(resourcePoolPath, isReadOnly:=True)

If Not projResourcePool Is Nothing Then
MsgBox projResourcePool.Name & ” が読み取り専用で開かれました。”, vbInformation, “成功”

‘ 読み取り専用なので、変更はできません。
‘ 例: リソース情報を表示する
Dim res As Resource
For Each res In projResourcePool.Resources
Debug.Print “リソース名: ” & res.Name & “, 標準単価: ” & res.StandardRate
Next res

‘ 読み取り専用で開いた場合も、CloseResourcePoolAndReleaseLock で閉じます。
‘ この場合、ロックファイルは元々作成されていないため、単にProjを閉じるだけになります。
modResourcePoolManager.CloseResourcePoolAndReleaseLock projResourcePool, saveChanges:=False
Else
MsgBox “リソースプールを開けませんでした。”, vbExclamation, “失敗”
End If

Set projResourcePool = Nothing
End Sub

3.3. コード解説と設計の肝

1. `FileSystemObject`の活用: ファイルやフォルダの存在確認、作成、削除には`FileSystemObject`が必須だ。これはVBAでの堅牢なファイル操作の基本中の基本。
2. ロックファイルの構造: ロックファイルには、単に存在するかどうかだけでなく、「誰が」「いつ」「どのマシンから」ロックしたかという情報を格納している。これにより、競合発生時の情報提供と、不正なロックの強制解除判断を可能にする。
3. タイムアウト処理: ロックファイルが残されたまま放置される「デッドロック」状態は避けたい。`C_LOCK_TIMEOUT_MINUTES`を設定し、一定時間経過したロックに対しては強制解除の選択肢を提示することで、システムの可用性を高めている。ただし、これはユーザーの判断に委ねるべきデリケートな部分だ。
4. バックアップの自動化: `CreateResourcePoolBackup`サブルーチンは、リソースプールを開く前に必ず実行される。これにより、もし編集中に予期せぬエラーでファイルが破損しても、直前の状態に復元できる。バックアップには日時を付与し、世代管理を可能にしている。
5. エラーハンドリング: `On Error GoTo ErrorHandler`を各関数・サブルーチンに適用し、予期せぬエラー発生時にもプログラムが異常終了せず、適切なメッセージを表示し、可能な限りクリーンアップ(ロックファイルの削除など)を試みる。
6. `Application.FileOpenEx`の利用: Projectファイルを開く際は、単なる`Application.FileOpen`ではなく、より詳細なオプションを指定できる`FileOpenEx`を利用する。特に`AddToMRU:=False`とすることで、最近使ったファイルリストに不必要にエントリが追加されるのを防ぐ。
7. オブジェクトのライフサイクル: Projectオブジェクト(`projResourcePool`)は、不要になったら速やかに`Set projResourcePool = Nothing`で解放する。これにより、メモリリークを防ぎ、リソースを適切に管理する。特にファイルを開く処理では、エラー終了時のオブジェクト解放とロックファイル削除のクリーンアップが重要だ。

4. 運用上の注意点とさらなる高みへ

このVBAソリューションは、ファイルベースの限界の中で最大限の堅牢性を提供するものだ。しかし、以下の注意点と、より高度なシステムへの展望も忘れてはならない。

4.1. ネットワーク環境の安定性

共有リソースプールファイルが置かれるネットワーク共有フォルダの安定性は極めて重要だ。ネットワーク遅延や切断は、ロックファイルの操作やProjectファイルの保存に失敗し、データ破損やデッドロックのリスクを高める。信頼性の高いファイルサーバーを利用し、ネットワーク環境を常に監視すること。

4.2. ユーザー教育の重要性

どんなに優れたシステムも、利用する人間の理解と協力なしには成り立たない。ユーザーに対して、この排他制御の仕組み、なぜ読み取り専用で開く必要があるのか、強制解除の判断基準などを十分に教育する必要がある。

4.3. データベース連携への移行(究極の解決策)

このVBAによるファイルロック方式は、Projectファイルが持つ「シングルユーザー編集」という制約の中で、競合を回避する最善策の一つだ。しかし、真の同時編集とスケーラビリティを求めるのであれば、Projectリソースの情報をデータベースで一元管理するアーキテクチャへの移行を強く推奨する。

  • 集中型データストア: SQL Server, Access DBなどをリソースマスタとして利用する。
  • VBAからの連携: Project VBAからADO/DAOを用いてデータベースに接続し、リソースデータのCRUD操作(Create, Read, Update, Delete)を行う。
  • Projectファイルの軽量化: 各ユーザーはProjectファイルのコピーを持ち、リソース情報はデータベースからリアルタイムで同期する形とする。Projectファイル自体はスケジュール情報のみを保持し、リソースのマスターデータはDBが持つ。
  • DBのトランザクション管理: データベースのトランザクション機能により、複数ユーザーからの同時書き込み要求を安全に処理できる。

これはVBAの範疇を一部超えるが、プロジェクトの規模が拡大し、リソース管理の複雑性が増した際には、必ず検討すべき道だ。

5. まとめ

Projectのリソースプールを複数人で安全に編集するという課題は、単なるファイルの開閉を超えた、システム設計の深い理解を要求する。今日の解説で、Projectファイルが抱える本質的な問題、そしてVBAでその限界に挑むための堅牢な設計思想と実践的なコードを提示した。

提供したVBAコードは、ファイルロック、バックアップ、そして丁寧なエラーハンドリングを通じて、データ破損のリスクを最小限に抑え、ユーザー間の競合を適切に管理するための強力な武器となるだろう。しかし、これが最終形ではない。常にシステムの限界を見据え、より堅牢でスケーラブルなソリューションを追求する姿勢こそが、真のアーキテクトに求められる。

この知識が、諸君が日々の業務で遭遇する複雑な課題を解決し、より効率的で安全なプロジェクト管理を実現するための一助となることを願う。システムの奥深くを理解し、その魂に触れること。それこそが、我々エンジニアの使命である。

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