【実務・中級編】【上級者向け】複数ユーザーの同時編集を想定した、排他制御用ロックファイルの自動生成と削除 – Project VBA解析バイブル

スポンサーリンク

共有フォルダの悲劇を断て!Project VBAにおける排他制御の「正しい」実装法

共有フォルダに置かれたProject VBAファイル。複数の担当者が同時に開く可能性がある。この状況、あなたのチームではどう対処していますか?「先着順」「誰か一人が編集したら他は待つ」…そんな原始的な運用で、編集漏れや上書きによるデータ損失の恐怖に日々怯えているとしたら、それはもはや「開発」ではなく「祈り」です。

本記事は、そんな非効率な運用から脱却し、堅牢で自動化された排他制御メカニズムを、Project VBAで実現するための「真の」ノウハウを伝授します。単なるリファレンスの羅 sintで終わるのではなく、オブジェクトのライフサイクル、パフォーマンスへの影響、そして何より「バグの起きにくい」プロダクションコードの思想を、開発プロジェクトリーダーの視点から、ロジカルかつシャープに解説していきます。

1. なぜ、標準機能だけでは不十分なのか? 〜排他制御の落とし穴〜

ExcelやAccessの標準機能には、「共有ブック」や「データベースのロック」といった機能が存在します。しかし、これらは多くの落とし穴を抱えています。

  • 共有ブックの限界:
  • パフォーマンスの著しい低下: 複数ユーザーが同時に編集すると、Excelの処理が重くなり、フリーズやクラッシュのリスクを高めます。
  • 変更履歴の管理の煩雑さ: 誰がいつ、どのような変更を加えたのかを追跡するのが困難になる場合があります。
  • VBAコードの制限: 共有ブック内で実行されるVBAコードは、予期せぬ動作を引き起こすことがあります。特に、外部ファイルへのアクセスや、他のアプリケーションとの連携は不安定になりがちです。
  • データベースのロック:
  • Accessのデータベースロックは、テーブルレベルやレコードレベルで動作しますが、Project VBAファイルそのものの編集を直接制御するものではありません。
  • ネットワーク環境によっては、ロックの解除に時間がかかったり、意図しないロックが発生したりする可能性があります。

これらの標準機能に依存するだけでは、現代のビジネスシーンで求められる、リアルタイム性、堅牢性、そして効率性を満たすことはできません。我々が目指すべきは、より高度で、より確実な、独自の排他制御メカニズムなのです。

2. 「ロックファイル」という名の鉄壁 〜排他制御の原理原則〜

そこで登場するのが、「ロックファイル」を用いた排他制御です。これは、共有フォルダ上のリソース(ここではProject VBAファイル)にアクセスする際に、「誰かが現在利用中である」という情報を、別の小さなファイル(ロックファイル)に記録することで、他のユーザーの同時編集を物理的にブロックする仕組みです。

このメカニズムの核心は、以下の2点にあります。

  • 不可分な操作: ロックファイルの作成と、その後のリソースへのアクセスは、「不可分」でなければなりません。つまり、ロックファイルが正常に作成されたことだけを確認してリソースを開くのではなく、ロックファイルの作成が成功した「直後」にリソースを開き、処理が完了したらロックファイルを削除する、という一連の流れを、誰にも邪魔されずに実行できる必要があります。
  • タイムアウトとクリーンアップ: ロックファイルが作成されたまま、担当者が意図せずアプリケーションを終了させてしまったり、ネットワークが切断されたりした場合、ロックファイルが残り続けてしまいます。これを「デッドロック」と呼び、リソースへのアクセスを永久にブロックしてしまう原因となります。そのため、一定時間経過してもロックファイルが削除されない場合に、それを検知して削除する仕組み(タイムアウトとクリーンアップ)が不可欠です。

3. Project VBAにおける「ロックファイル」実装の勘所 〜魂を込めた設計思想〜

ここからが、Project VBAにおける具体的な実装の深淵です。単にコードをコピペするだけでなく、その背後にある「なぜ」を理解し、自身のプロジェクトに適用できるレベルまで落とし込むことが重要です。

3.1. ロックファイルの命名規則と配置場所

  • 命名規則: 元となるProject VBAファイル名に、例えば`.lock`や`.locking`といった拡張子を付加するか、あるいは`~`のような特殊文字を prefix として付与するなど、一目で「ロックファイルである」と認識できる命名規則を採用しましょう。例: `MyProject.xlsm` に対し `MyProject.xlsm.lock`
  • 配置場所: 必ず、対象のProject VBAファイルと同じ共有フォルダ内に配置します。これにより、ファイルパスの管理が容易になり、ネットワークドライブの再接続などによるパスの不整合を防ぐことができます。

3.2. ロックファイルの「作成」 〜原子性の担保〜

ロックファイルを「作成する」という操作は、一見単純に見えます。しかし、ここには「原子性(Atomicity)」、つまり「成功するか、全く行われなかったかのどちらか」という性質が求められます。

もし、あるユーザーがロックファイルを作成しようとした際に、別のユーザーも同時に作成しようとしたらどうなるでしょうか?

1. ユーザーAがロックファイルを作成しようと試みる。
2. ユーザーBもロックファイルを作成しようと試みる。
3. どちらかが既にファイルが存在するにも関わらず上書きしようとしてエラーになる、あるいは、ロックファイルが存在しないことを確認してから作成しようとした結果、両方とも作成できてしまう、といった競合が発生する可能性があります。

これを防ぐために、VBAでは`FileSystemObject`の`CreateFolder`や`CreateTextFile`メソッドを巧みに利用します。これらのメソッドは、ファイルが存在しない場合にのみ新規作成を試み、存在する場合はエラーを発生させる性質を持っています。このエラーハンドリングを駆使することで、ロックファイルの「排他的作成」を実現します。

‘ ロックファイルを作成する関数
‘ 成功すれば True、失敗すれば False を返す
Function CreateLockFile(filePath As String) As Boolean
Dim fso As Object
Dim lockFilePath As String
Dim startTime As Double
Dim timeoutSeconds As Double

‘ Project VBAファイル名からロックファイル名を生成
lockFilePath = filePath & “.lock”

On Error Resume Next ‘ エラー発生時は無視して続行

Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ ファイルが存在しない場合のみ作成を試みる
If Not fso.FileExists(lockFilePath) Then
fso.CreateTextFile lockFilePath ‘ ロックファイルを作成
If Err.Number = 0 Then
CreateLockFile = True ‘ 作成成功
Else
CreateLockFile = False ‘ 作成失敗 (他の要因)
Err.Clear
End If
Else
‘ ファイルが既に存在する場合 (他のユーザーがロック中)
CreateLockFile = False
End If

Set fso = Nothing
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

End Function

3.3. ロックファイルの「削除」 〜クリーンアップの徹底〜

ロックファイルの削除は、処理が完了した際や、エラーが発生した場合など、「リソースの利用を終えた」というタイミングで必ず実行されなければなりません。

`On Error GoTo` ステートメントと `Exit Sub` を組み合わせることで、処理の途中でエラーが発生した場合でも、必ずロックファイルの削除処理が実行されるように設計します。

‘ ロックファイルを削除する関数
Sub DeleteLockFile(filePath As String)
Dim fso As Object
Dim lockFilePath As String

lockFilePath = filePath & “.lock”

On Error Resume Next ‘ エラー発生時は無視して続行

Set fso = CreateObject(“Scripting.FileSystemObject”)

If fso.FileExists(lockFilePath) Then
fso.DeleteFile lockFilePath, True ‘ ファイルを削除 (Trueは強制削除)
If Err.Number <> 0 Then
‘ ここでエラーログを記録するなど、何らかの対処を行う
Debug.Print “ロックファイルの削除に失敗しました: ” & lockFilePath
Err.Clear
End If
End If

Set fso = Nothing
On Error GoTo 0 ‘ エラーハンドリングを元に戻す
End Sub

3.4. タイムアウトとデッドロック対策 〜健全な運用を支える仕組み〜

前述の通り、ロックファイルが残ったままになる「デッドロック」は、共有フォルダ運用における最大の敵です。これを回避するために、「一定時間経過してもロックファイルが削除されない場合に、それを検知して削除する」仕組みを導入します。

このタイムアウト処理は、VBA単体では実現が難しい部分もありますが、VB.NETなどの外部プロセスを利用したり、あるいは、ロックファイル内に「ロックを開始した日時」を記録しておき、定期的にそれをチェックする仕組みをVBA内で実装したりする方法が考えられます。

ここでは、VBA内で簡易的なタイムアウトチェックを行う例を示します。

‘ Project VBAファイルを開く際に、ロックファイルが存在する場合の処理
Sub OpenProjectWithLock(filePath As String)
Dim fso As Object
Dim lockFilePath As String
Dim lockFile As Object
Dim lockCreationTime As Date
Dim currentTime As Date
Dim lockDurationMinutes As Double
Dim maxLockMinutes As Double

lockFilePath = filePath & “.lock”
maxLockMinutes = 30 ‘ 最大ロック維持時間 (分)

Set fso = CreateObject(“Scripting.FileSystemObject”)

If fso.FileExists(lockFilePath) Then
‘ ロックファイルが存在する場合
Set lockFile = fso.GetFile(lockFilePath)
lockCreationTime = lockFile.DateCreated ‘ ロックファイル作成日時を取得
currentTime = Now

‘ ロック維持時間が最大時間を超えているかチェック
If DateDiff(“n”, lockCreationTime, currentTime) > maxLockMinutes Then
‘ — デッドロックの可能性あり —
MsgBox “ロックファイルが長時間残存しています。自動削除を試みます。”, vbExclamation
DeleteLockFile filePath ‘ 既存のロックファイルを削除

‘ 再度ロックファイル作成を試みる
If CreateLockFile(filePath) Then
MsgBox “ロックファイルの自動削除と再作成に成功しました。”, vbInformation
‘ ここでProject VBAファイルを開く処理を続ける
‘ 例: Application.Workbooks.Open filePath
Else
MsgBox “ロックファイルの再作成に失敗しました。手動での確認が必要です。”, vbCritical
Exit Sub
End If
Else
‘ まだロック時間内
MsgBox “このファイルは現在、他のユーザーが編集中です。”, vbInformation
Exit Sub
End If
Else
‘ ロックファイルが存在しない場合、新規作成を試みる
If CreateLockFile(filePath) Then
‘ ロックファイル作成成功、ここでProject VBAファイルを開く処理を続ける
‘ 例: Application.Workbooks.Open filePath
Else
MsgBox “ロックファイルの作成に失敗しました。”, vbCritical
Exit Sub
End If
End If

Set lockFile = Nothing
Set fso = Nothing

End Sub

‘ Project VBAファイル保存時や終了時の処理
Sub CloseProjectWithLock(filePath As String)
‘ ここでProject VBAファイル保存処理などを行う

‘ 最後にロックファイルを削除
DeleteLockFile filePath
End Sub

3.5. パフォーマンスへの影響 〜「重い」処理との共存〜

ロックファイルの作成・削除は、ネットワークI/Oを伴うため、VBAコードの実行速度にわずかな影響を与えます。しかし、これは共有ブックのパフォーマンス低下に比べれば、比較にならないほど軽微です。

重要なのは、ロックファイルの操作を、VBAコードの「核となる処理」の直前・直後に配置することです。例えば、大量のデータを処理する関数を呼び出す前にロックファイルを作成し、処理完了後に削除する、という流れです。これにより、処理の「最中」に他のユーザーが割り込むことを防ぎつつ、VBAコード自体の実行速度への影響を最小限に抑えることができます。

3.6. エラーハンドリングとトランザクション 〜堅牢性の追求〜

「バグの起きない堅牢な設計」とは、「あらゆる状況で、意図した通りに動作すること」を意味します。

  • ネットワーク切断: ネットワークが一時的に切断された場合、ロックファイルの作成・削除が失敗する可能性があります。VBAの`On Error`ステートメントを適切に設定し、エラー発生時にはユーザーに通知したり、リトライ処理を実装したりすることが重要です。
  • ディスク容量不足: 共有フォルダのディスク容量が不足している場合、ロックファイルが作成できない可能性があります。
  • 権限不足: 共有フォルダへの書き込み権限がない場合も、ロックファイルは作成できません。

これらの例外的な状況を考慮し、エラー発生時のメッセージ表示、ログ記録、そして必要に応じた手動介入を促すような設計が、プロダクションコードには不可欠です。

4. 生産性を劇的に向上させる「プロダクションコード」例

ここまでの解説を踏まえ、実際の業務でそのままコピペして使える、保守性の高いプロダクションコード例を示します。

4.1. Excel VBAでの実装例

Excel VBAで、ブックを開く前と閉じる際にロックファイルを作成・削除する例です。

‘===============================================================================
‘ Module: clsLockManager
‘ Purpose: Project VBAファイルに対する排他制御(ロックファイル管理)
‘ Author: [あなたの名前/チーム名]
‘ Version: 1.0
‘ Date: [今日の日付]
‘===============================================================================

Option Explicit

‘ — 定数 —
Const LOCK_FILE_EXTENSION As String = “.lock” ‘ ロックファイル拡張子
Const MAX_LOCK_MINUTES As Double = 30 ‘ デッドロックとみなす最大ロック時間 (分)

‘ — グローバル変数 —
Private m_strProjectFilePath As String ‘ 現在開いているProject VBAファイルパス
Private m_blnIsLocked As Boolean ‘ 現在、ファイルがロックされているか

‘===============================================================================
‘ Workbookイベントハンドラ (ThisWorkbookモジュールに記述)
‘===============================================================================

Private Sub Workbook_Open()
Dim strFilePath As String
strFilePath = ThisWorkbook.FullName

‘ ロックファイル管理を開始
If InitializeLock(strFilePath) Then
m_strProjectFilePath = strFilePath
m_blnIsLocked = True
Debug.Print “ロックファイル管理開始: ” & strFilePath
Else
‘ ロック取得失敗時の処理
MsgBox “このファイルは現在、他のユーザーが編集中です。またはロック取得に失敗しました。”, vbCritical
‘ ファイルを開いたまま終了させる
Application.Quit
End If
End Sub

Private Sub Workbook_BeforeClose(Cancel As Integer)
‘ ロックファイルを解放
If m_blnIsLocked Then
If ReleaseLock(m_strProjectFilePath) Then
Debug.Print “ロックファイル解放成功: ” & m_strProjectFilePath
m_blnIsLocked = False
Else
‘ ロック解放失敗時の処理
MsgBox “ロックファイルの解放に失敗しました。手動での確認が必要な場合があります。”, vbExclamation
‘ 続行を許可するかどうかは、運用ポリシーによる
End If
End If
End Sub

‘===============================================================================
‘ ロックファイル管理関数 (Module: clsLockManager に記述)
‘===============================================================================

‘ — ロックの初期化 (ファイルを開く前に行う) —
‘ 成功: True, 失敗: False
Public Function InitializeLock(ByVal targetFilePath As String) As Boolean
Dim fso As Object
Dim lockFilePath As String
Dim lockFile As Object
Dim lockCreationTime As Date
Dim currentTime As Date

On Error GoTo ErrorHandler

Set fso = CreateObject(“Scripting.FileSystemObject”)
lockFilePath = targetFilePath & LOCK_FILE_EXTENSION

‘ 1. ロックファイルが存在するかチェック
If fso.FileExists(lockFilePath) Then
‘ 2. ロックファイルが存在する場合、デッドロックをチェック
Set lockFile = fso.GetFile(lockFilePath)
lockCreationTime = lockFile.DateCreated
currentTime = Now

If DateDiff(“n”, lockCreationTime, currentTime) > MAX_LOCK_MINUTES Then
‘ — デッドロックの可能性あり —
If MsgBox(“ロックファイルが長時間残存しています。自動削除を試みますか?”, vbQuestion + vbYesNo) = vbYes Then
If DeleteLockFile(lockFilePath) Then
‘ 削除成功後、新規ロック作成を試みる
If CreateLockFile(lockFilePath) Then
InitializeLock = True ‘ ロック取得成功
Else
InitializeLock = False ‘ ロック作成失敗
End If
Else
InitializeLock = False ‘ ロック削除失敗
End If
Else
InitializeLock = False ‘ ユーザーが削除を拒否
End If
Else
‘ まだロック時間内
InitializeLock = False ‘ ロック取得失敗 (他のユーザーが使用中)
End If
Set lockFile = Nothing
Else
‘ 3. ロックファイルが存在しない場合、新規作成を試みる
If CreateLockFile(lockFilePath) Then
InitializeLock = True ‘ ロック取得成功
Else
InitializeLock = False ‘ ロック作成失敗
End If
End If

Set fso = Nothing
Exit Function

ErrorHandler:
MsgBox “ロック処理中に予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
InitializeLock = False ‘ エラー発生時は失敗とする
Set fso = Nothing
Set lockFile = Nothing
Err.Clear
End Function

‘ — ロックファイルの作成 (排他制御) —
‘ 成功: True, 失敗: False
Private Function CreateLockFile(ByVal lockFilePath As String) As Boolean
Dim fso As Object
Dim tempFile As Object

On Error Resume Next ‘ エラー発生時は無視して続行

Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ ファイルが存在しない場合のみ作成を試みる
If Not fso.FileExists(lockFilePath) Then
‘ CreateTextFileは、ファイルが存在しない場合にのみ成功する
Set tempFile = fso.CreateTextFile(lockFilePath, True) ‘ TrueはUnicode形式 (必要に応じてFalseに変更)

If Err.Number = 0 Then
‘ ファイル作成成功
CreateLockFile = True
Else
‘ ファイル作成失敗 (既に存在、権限不足など)
CreateLockFile = False
Err.Clear
End If
If Not tempFile Is Nothing Then
tempFile.Close
Set tempFile = Nothing
End If
Else
‘ ファイルが既に存在する場合
CreateLockFile = False
End If

Set fso = Nothing
On Error GoTo 0 ‘ エラーハンドリングを元に戻す
End Function

‘ — ロックファイルの削除 —
‘ 成功: True, 失敗: False
Private Function DeleteLockFile(ByVal lockFilePath As String) As Boolean
Dim fso As Object

On Error GoTo ErrorHandler

Set fso = CreateObject(“Scripting.FileSystemObject”)

If fso.FileExists(lockFilePath) Then
fso.DeleteFile lockFilePath, True ‘ Trueは強制削除
If Err.Number = 0 Then
DeleteLockFile = True
Else
DeleteLockFile = False
Err.Clear ‘ エラーをクリア
End If
Else
‘ ファイルが存在しない場合は成功とみなす (既に削除されている場合など)
DeleteLockFile = True
End If

Set fso = Nothing
Exit Function

ErrorHandler:
MsgBox “ロックファイルの削除中にエラーが発生しました。” & vbCrLf & _
“ファイル: ” & lockFilePath & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
DeleteLockFile = False
Set fso = Nothing
Err.Clear
End Function

‘ — ロックの解放 (ファイル保存・終了時などに行う) —
‘ 成功: True, 失敗: False
Public Function ReleaseLock(ByVal targetFilePath As String) As Boolean
Dim lockFilePath As String
lockFilePath = targetFilePath & LOCK_FILE_EXTENSION

‘ ロックファイルの削除を実行
ReleaseLock = DeleteLockFile(lockFilePath)
End Function

‘===============================================================================
‘ 外部から呼び出すためのラッパー関数 (必要に応じて)
‘===============================================================================

‘ 外部からファイルを開く際に呼び出す
‘ 戻り値: True (開けた), False (開けなかった)
Public Function TryOpenProject(ByVal filePath As String) As Boolean
Dim fso As Object
Dim lockFilePath As String
Dim wb As Workbook

On Error GoTo ErrorHandler

Set fso = CreateObject(“Scripting.FileSystemObject”)
lockFilePath = filePath & LOCK_FILE_EXTENSION

‘ 1. ロックファイルが存在するかチェック
If fso.FileExists(lockFilePath) Then
‘ ロックファイルが存在する場合、デッドロックチェックと削除を試みる
If Not InitializeLock(filePath) Then
MsgBox “ロックの取得に失敗しました。ファイルは他のユーザーによって編集されているか、デッドロックが発生しています。”, vbExclamation
TryOpenProject = False
Exit Function
End If
‘ InitializeLockがTrueを返した場合、ロックは正常に取得されたとみなす
End If

‘ 2. ロックファイルが存在しない、またはデッドロック解除後に新規ロック取得成功
‘ Project VBAファイルを開く
Set wb = Application.Workbooks.Open(filePath)

‘ Workbook_Openイベントでロックが取得されているはずだが、念のため確認
If m_blnIsLocked Then
TryOpenProject = True
Else
‘ 予期せぬ状況: ファイルは開いたがロックが取得できていない
MsgBox “ファイルは開かれましたが、ロックの取得に失敗しました。即座に保存せずに閉じることを推奨します。”, vbExclamation
wb.Close SaveChanges:=False ‘ 保存せずに閉じる
TryOpenProject = False
End If

Set fso = Nothing
Set wb = Nothing
Exit Function

ErrorHandler:
MsgBox “ファイルを開く際に予期せぬエラーが発生しました。” & vbCrLf & _
“ファイル: ” & filePath & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
TryOpenProject = False
Set fso = Nothing
Set wb = Nothing
Err.Clear
End Function

‘ 外部からファイルを閉じる際に呼び出す
Public Sub TryCloseProject(ByVal filePath As String, Optional ByVal SaveChanges As Boolean = True)
Dim wb As Workbook

On Error GoTo ErrorHandler

‘ 現在開いているブックから対象のブックを探す
On Error Resume Next ‘ Workbookが見つからない場合のエラーを無視
Set wb = Application.Workbooks(Dir(filePath)) ‘ Dir関数でファイル名部分を取得
On Error GoTo ErrorHandler ‘ エラーハンドリングを元に戻す

If wb Is Nothing Then
MsgBox “指定されたブックが見つかりません: ” & filePath, vbExclamation
Exit Sub
End If

‘ 保存処理
If SaveChanges Then
wb.Save
End If

‘ ロック解放処理
If ReleaseLock(filePath) Then
Debug.Print “ロック解放成功 (閉じる処理): ” & filePath
Else
MsgBox “ロックファイルの解放に失敗しました (閉じる処理)。手動での確認が必要な場合があります。”, vbExclamation
End If

‘ ブックを閉じる
wb.Close SaveChanges:=False ‘ SaveChangesは既に実行済み、またはFalseにする

Set wb = Nothing
Exit Sub

ErrorHandler:
MsgBox “ファイルを閉じる際に予期せぬエラーが発生しました。” & vbCrLf & _
“ファイル: ” & filePath & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical
If Not wb Is Nothing Then wb.Close SaveChanges:=False
Set wb = Nothing
Err.Clear
End Sub

【使い方】

1. `ThisWorkbook`モジュールに、`Workbook_Open`と`Workbook_BeforeClose`イベントプロシージャをコピー&ペーストします。
2. 新しい標準モジュール(例: `clsLockManager`)を作成し、上記コードの残りをコピー&ペーストします。
3. `Workbook_Open`内で`InitializeLock`が`False`を返した場合、`Application.Quit`でExcelを終了させるようにしています。必要に応じて、この挙動を変更してください(例: ユーザーにファイルを開くことを諦めさせるメッセージを出すなど)。
4. ファイルを開く際は、`Application.Workbooks.Open`の代わりに、`clsLockManager.TryOpenProject(“ファイルのフルパス”)`を呼び出します。
5. ファイルを閉じる際は、`Workbook.Close`の代わりに、`clsLockManager.TryCloseProject(“ファイルのフルパス”, True)`を呼び出します。

【コードのポイント】

  • モジュール化: ロック管理のロジックを独立したモジュール(`clsLockManager`)に分離することで、コードの再利用性、保守性、可読性を向上させています。
  • 定数定義: ロックファイル拡張子や最大ロック時間などを定数として定義することで、将来的な変更が容易になります。
  • `Workbook_Open`と`Workbook_BeforeClose`の活用: Excelのライフサイクルイベントを最大限に活用し、ファイルを開くときと閉じるときに自動的にロック処理が実行されるようにしています。
  • エラーハンドリング: `On Error GoTo` を用いて、予期せぬエラーが発生した場合でも、クリーンアップ処理(ロックファイルの削除など)が実行されるように努めています。
  • デッドロック検出: `DateCreated` プロパティを利用して、ロックファイルが作成されてからの経過時間をチェックし、デッドロックの可能性を検出します。
  • ユーザーへの通知: ロック取得の成否やデッドロックの可能性について、ユーザーに分かりやすく通知します。

4.2. VB.NETでの実装例(より堅牢な選択肢として)

VBAの実行環境に制約がある場合や、より高度な処理が必要な場合は、VB.NETでロック管理用のexeを作成し、VBAからCOM連携で呼び出す、というアプローチも強力です。

.net
‘ VB.NET (ロック管理用EXEの例)
Imports System.IO
Imports System.Threading

Public Class LockManager

‘ ロックファイル拡張子
Private Const LOCK_FILE_EXTENSION As String = “.lock”
‘ デッドロックとみなす最大ロック時間 (分)
Private Const MAX_LOCK_MINUTES As Integer = 30

‘ ロックファイルを作成し、ロックを取得する
‘ 成功: True, 失敗: False
Public Function AcquireLock(ByVal targetFilePath As String) As Boolean
Dim lockFilePath As String = targetFilePath & LOCK_FILE_EXTENSION

Try
‘ ロックファイルが存在するかチェック
If File.Exists(lockFilePath) Then
Dim lockFile As FileInfo = New FileInfo(lockFilePath)
Dim lockCreationTime As DateTime = lockFile.CreationTime
Dim currentTime As DateTime = DateTime.Now

‘ デッドロックチェック
If currentTime.Subtract(lockCreationTime).TotalMinutes > MAX_LOCK_MINUTES Then
‘ デッドロックとみなす、削除を試みる
Try
File.Delete(lockFilePath)
‘ 削除成功後、新規ロック作成を試みる
File.Create(lockFilePath).Close() ‘ 空のファイルを作成
Return True ‘ ロック取得成功
Catch ex As Exception
‘ 削除または作成失敗
Return False
End Try
Else
‘ まだロック時間内
Return False ‘ ロック取得失敗 (他のユーザーが使用中)
End If
Else
‘ ロックファイルが存在しない場合、新規作成
File.Create(lockFilePath).Close() ‘ 空のファイルを作成
Return True ‘ ロック取得成功
End If
Catch ex As Exception
‘ その他のエラー (権限不足など)
Console.WriteLine($”ロック取得エラー: {ex.Message}”)
Return False
End Try
End Function

‘ ロックファイルを削除し、ロックを解放する
‘ 成功: True, 失敗: False
Public Function ReleaseLock(ByVal targetFilePath As String) As Boolean
Dim lockFilePath As String = targetFilePath & LOCK_FILE_EXTENSION

Try
If File.Exists(lockFilePath) Then
File.Delete(lockFilePath)
End If
Return True ‘ 存在しない場合でも成功とみなす
Catch ex As Exception
Console.WriteLine($”ロック解放エラー: {ex.Message}”)
Return False
End Try
End Function

‘ ロックファイルが存在するかどうかをチェックする (vb.netからvbaへ返す場合など)
Public Function IsLocked(ByVal targetFilePath As String) As Boolean
Dim lockFilePath As String = targetFilePath & LOCK_FILE_EXTENSION
Return File.Exists(lockFilePath)
End Function

End Class

このVB.NETコードをコンパイルしてexe化し、VBAからは`CreateObject`や`Shell`コマンドで連携します。VB.NETの方がファイル操作のAPIが豊富で、より細やかな制御が可能です。

5. まとめ:「自動化」こそが、チームの生産性を解き放つ

Project VBAにおける排他制御は、単なる「競合防止」以上の意味を持ちます。それは、チームメンバーが安心して作業に集中できる環境を提供し、データ損失のリスクを排除し、結果としてチーム全体の生産性を劇的に向上させるための、不可欠な「基盤」なのです。

今回ご紹介した「ロックファイル」による排他制御メカニズムは、そのための強力な手段です。標準機能の限界を理解し、独自の仕組みを構築することで、あなたはチームを「祈り」の運用から「確実な自動化」へと導くことができるはずです。

この知識を、あなたのプロジェクトで、そしてチームで、ぜひ活かしてください。そして、もしこの解説に疑問や、さらに深掘りしたい点があれば、遠慮なく問いかけてください。開発プロジェクトのリーダーとして、私は常に最善の道筋を示す準備ができています。

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