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

スポンサーリンク

共有フォルダでの悲劇を回避!Project VBAで実現する「ロックファイル」による安全な同時編集

皆さん、こんにちは!Project VBAの世界へようこそ。今回は、皆さんが普段「共有フォルダにあるExcelファイル、誰かが開いてて編集できない!」なんて経験をしたことがあるかもしれません。そんな「あるある」を解消し、複数人でファイルを安全に編集するための、ちょっと高度だけど超実践的なテクニック、「ロックファイル」を使った排他制御について、先輩エンジニアの目線でじっくり解説していきますね。

「ロックファイル?何それ、難しそう…」なんて思わないでください。この技術をマスターすれば、皆さんのVBAスキルは一段階も二段階もステップアップします。まるで、秘密の扉を開ける鍵を手に入れるような感覚ですよ!

なぜ「ロックファイル」が必要なのか?~排他制御の重要性~

まずは、なぜこのような仕組みが必要なのか、その根本から理解しましょう。

1. 複数ユーザーによる同時編集の落とし穴

皆さんが共有フォルダにあるExcelファイルを同時に開いたと想像してみてください。

  • Aさん がファイルを開き、データを更新しました。
  • Bさん もほぼ同時にファイルを開き、Aさんとは別のデータを更新しました。
  • Aさん がファイルを保存しました。
  • その後、Bさん もファイルを保存しました。

この場合、いったいどちらのデータが最終的に保存されるでしょうか? 多くのファイル共有システムでは、後から保存した方のデータが、先に保存した方のデータを上書きしてしまう可能性があります。つまり、Aさんの行った編集が失われてしまう、なんて悲劇が起こりうるのです!

2. Project VBAが提供する「排他制御」の基本

Excel VBAには、ファイルを開く際に「排他モード」を設定する機能があります。これは、ファイルを開くときに「読み取り専用で開く」とか、「書き込み可能で開く」といった指定をするのに似ています。

しかし、これはあくまで「Excel VBA自身」がファイルを開く際の制御であり、「誰かがファイルを開いているかどうか」を外部から検知するものではありません。 ユーザーが手動で「読み取り専用」で開けば、他の人も開けてしまいますし、書き込みもできてしまいます。

3. 独自の「ロックファイル」で「誰かが使ってるよ!」を可視化する

そこで登場するのが、今回ご紹介する「ロックファイル」という考え方です。これは、Excel VBAの標準機能ではなく、私たちが「こういうルールで運用しましょう」と決めて実装する仕組みです。

簡単に言うと、

1. 誰かがプロジェクトファイルを開きたい時 に、
2. そのプロジェクトファイルと同じフォルダに、「このプロジェクトファイルは今、〇〇さんが使っています」という情報を持った「ロックファイル」 を作ります。
3. 他の誰かがファイルを開こうとした時 に、
4. まず、その「ロックファイル」があるかどうかをチェックします。
5. もしロックファイルがあれば、「あ、今誰かが使ってるんだな。後で開こう」と判断します。
6. プロジェクトファイルの編集が終わったら、作ったロックファイルを削除します。

この一連の流れを自動化することで、複数ユーザーが同時にファイルを編集しようとした場合に、「今、このファイルは使用中です」ということを明確に伝え、競合を防ぐことができます。

ロックファイルによる排他制御の仕組みをVBAで実装しよう!

それでは、この「ロックファイル」の仕組みをVBAでどのように実装するか、具体的なコードを見ていきましょう。

1. ロックファイルとは?~シンプルが一番~

ロックファイルに特別な形式はありません。一般的には、プロジェクトファイルと同じ名前で、拡張子を `.lock` や `.lck` としたり、あるいは `ファイル名_lock.txt` のように、一目で「ロックファイルだ!」とわかるような名前にすることが多いです。

ロックファイルの中身も、シンプルに「誰が」「いつ」ロックしたかの情報をテキストで記録しておくと、トラブルシューティングの際に役立ちます。

2. コード例:ロックファイルの作成とチェック

まずは、ファイルを開く前にロックファイルを作成・チェックするVBAコードを見てみましょう。

Option Explicit ‘ 変数の宣言を強制し、タイプミスによるエラーを防ぎます

‘— 定数定義 —
Const PROJECT_FILE_NAME As String = “共有プロジェクトファイル.xlsm” ‘ 対象のプロジェクトファイル名
Const LOCK_FILE_EXTENSION As String = “.lock” ‘ ロックファイルの拡張子
Const LOG_FILE_NAME As String = “編集ログ.txt” ‘ 編集ログファイル名 (オプション)

‘— グローバル変数 —
‘ 必要に応じて、ユーザー名などを保持する変数を宣言

‘——————————————————————————–
‘ ワークブックを開く前にロックファイルを作成・チェックする処理
‘——————————————————————————–
Sub OpenProjectFileSafely()

Dim ws As Worksheet
Dim projectFilePath As String
Dim lockFilePath As String
Dim logFilePath As String
Dim userName As String
Dim fileLockExists As Boolean
Dim isFileOpened As Boolean

‘ — 1. 対象ファイルのパスとロックファイルのパスを設定 —
‘ 現在アクティブなワークブックのパスを取得し、そこからプロジェクトファイルとロックファイルのパスを生成
‘ 実際には、共有フォルダのパスを直接指定する方が堅牢です
projectFilePath = ThisWorkbook.Path & “\” & PROJECT_FILE_NAME
lockFilePath = projectFilePath & LOCK_FILE_EXTENSION
logFilePath = ThisWorkbook.Path & “\” & LOG_FILE_NAME ‘ ログファイルのパス

‘ — 2. 現在のユーザー名を取得 —
‘ 環境によって取得方法が異なります。これは一般的な例です。
On Error Resume Next ‘ エラーが発生しても処理を続行
userName = Environ(“USERNAME”) ‘ Windowsのユーザー名を取得
If userName = “” Then userName = “UnknownUser” ‘ 取得できなかった場合
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ — 3. ロックファイルの存在チェック —
fileLockExists = (Dir(lockFilePath) <> “”) ‘ Dir関数でファイルが存在するかチェック

‘ — 4. ロックファイルが存在する場合の処理 —
If fileLockExists Then
MsgBox “このプロジェクトファイルは現在、他のユーザーによって編集中です。” & vbCrLf & _
“しばらくしてから再度お試しください。”, vbExclamation, “編集中”

‘ — (オプション) ロックファイルの内容を読み取り、誰がロックしているか表示 —
Dim lockFileContent As String
Dim fileNum As Integer
fileNum = FreeFile ‘ 空いているファイル番号を取得

On Error Resume Next
‘ ロックファイルを開いて内容を読み込む
Open lockFilePath For Input As #fileNum
If Err.Number = 0 Then ‘ エラーなく開けた場合
Input #fileNum, lockFileContent
Close #fileNum ‘ ファイルを閉じる

‘ ファイル内容が空でなければ、ロック情報を表示
If lockFileContent <> “” Then
MsgBox “ロック情報: ” & lockFileContent, vbInformation, “ロック情報”
End If
Else
‘ ロックファイルが開けない場合 (権限がない、破損しているなど)
MsgBox “ロックファイルを開けませんでした。手動での確認が必要な場合があります。”, vbExclamation
Close #fileNum ‘ エラーでも一応閉じる
End If
On Error GoTo 0

Exit Sub ‘ ここで処理を終了し、ファイルを開かない
End If

‘ — 5. ロックファイルが存在しない場合、ロックファイルを作成 —
On Error Resume Next ‘ ファイル作成時のエラーを捕捉
fileNum = FreeFile ‘ 空いているファイル番号を取得

‘ ロックファイルを開き、ロック情報 (ユーザー名と日時) を書き込む
Open lockFilePath For Output As #fileNum
If Err.Number = 0 Then ‘ エラーなく開けた場合
Print #fileNum, userName & ” さんが ” & Now() & ” にロックしました。”
Close #fileNum ‘ ファイルを閉じる

‘ — (オプション) 編集ログファイルに記録 —
Open logFilePath For Append As #fileNum ‘ Appendモードで追記
If Err.Number = 0 Then
Print #fileNum, “— ロック開始 —”
Print #fileNum, “ファイル: ” & PROJECT_FILE_NAME
Print #fileNum, “ユーザー: ” & userName
Print #fileNum, “日時: ” & Now()
Close #fileNum
Else
MsgBox “編集ログファイルへの書き込みに失敗しました。”, vbExclamation
Close #fileNum ‘ エラーでも一応閉じる
End If
On Error GoTo 0

MsgBox “プロジェクトファイルを開きます。”, vbInformation, “ロック成功”

‘ — 6. プロジェクトファイルを開く —
‘ ここで、対象のプロジェクトファイルを開く処理を実行します。
‘ 注意: このコードは「このマクロが含まれているブック」を開くわけではなく、
‘ 別の「共有プロジェクトファイル.xlsm」を開くことを想定しています。
‘ もし、このマクロが含まれているブック自体をロックしたい場合は、
‘ 「Workbooks.Open」の代わりに「ThisWorkbook.Activate」や
‘ 「ThisWorkbook.Save」などの処理を検討してください。

‘ 例: 別のワークブックを開く場合
‘ Workbooks.Open (projectFilePath)

‘ 例: このマクロが含まれるブックの編集を継続する場合
‘ (特に何もしなくても、このSubプロシージャが実行されれば、
‘ このブックの編集が続行されます。ロックファイル作成・削除を
‘ 適切に行えば、排他制御は機能します。)

‘ 実際には、このSubプロシージャを呼び出す前に、
‘ ユーザーに「このマクロを実行してファイルを開くか?」といった確認を
‘ 入れると、より親切です。

Else
‘ ロックファイルが作成できなかった場合 (権限がない、ディスク容量不足など)
MsgBox “ロックファイルの作成に失敗しました。ファイルへのアクセス権限を確認してください。”, vbCritical
On Error GoTo 0
Exit Sub ‘ 処理を終了
End If

End Sub

‘——————————————————————————–
‘ プロジェクトファイルを閉じるときにロックファイルを削除する処理
‘——————————————————————————–
Sub ReleaseProjectFileLock()

Dim projectFilePath As String
Dim lockFilePath As String
Dim logFilePath As String
Dim fileNum As Integer

‘ — 1. 対象ファイルのパスとロックファイルのパスを設定 —
‘ ここでも、共有フォルダのパスを直接指定するのが理想です。
projectFilePath = ThisWorkbook.Path & “\” & PROJECT_FILE_NAME
lockFilePath = projectFilePath & LOCK_FILE_EXTENSION
logFilePath = ThisWorkbook.Path & “\” & LOG_FILE_NAME ‘ ログファイルのパス

‘ — 2. ロックファイルが存在するか確認 —
If Dir(lockFilePath) <> “” Then
‘ — 3. ロックファイルを削除 —
On Error Resume Next ‘ 削除時のエラーを捕捉
Kill lockFilePath ‘ ロックファイルを削除
If Err.Number <> 0 Then
MsgBox “ロックファイルの削除に失敗しました。手動での削除が必要な場合があります。”, vbExclamation
‘ エラーが発生しても、処理は続行します(ファイルは開いたままなので)
Else
‘ — (オプション) 編集ログファイルに記録 —
fileNum = FreeFile
Open logFilePath For Append As #fileNum
If Err.Number = 0 Then
Print #fileNum, “— ロック解除 —”
Print #fileNum, “ファイル: ” & PROJECT_FILE_NAME
Print #fileNum, “日時: ” & Now()
Close #fileNum
Else
MsgBox “編集ログファイルへの書き込みに失敗しました。”, vbExclamation
Close #fileNum ‘ エラーでも一応閉じる
End If
End If
On Error GoTo 0
Else
‘ ロックファイルが存在しない場合 (既に削除されている、または元々なかった)
‘ 特にエラーメッセージは出さず、静かに処理を終了します。
End If

End Sub

‘——————————————————————————–
‘ ワークブックが閉じられるときに自動的にロックを解除するイベントプロシージャ
‘ (ThisWorkbookモジュールに記述します)
‘——————————————————————————–
‘ Private Sub Workbook_BeforeClose(Cancel As Integer)
‘ ‘ ワークブックが閉じられる直前に、ロックファイルを削除します。
‘ ‘ ただし、ファイルが正常に保存されることが前提です。
‘ ‘ 保存前に閉じられた場合は、ロックが残る可能性があります。

‘ ‘ ここでReleaseProjectFileLockを実行すると、
‘ ‘ ワークブックを閉じる処理自体がキャンセルされてしまう場合があります。
‘ ‘ より確実なのは、Application.Quit イベントや、
‘ ‘ ユーザーが「保存して閉じる」ボタンを押した際に
‘ ‘ ReleaseProjectFileLockを呼び出すようにすることです。

‘ ‘ 今回は、シンプルに、ユーザーが手動で「閉じる」ボタンを押した際に
‘ ‘ 実行されるようなイメージで、ReleaseProjectFileLockを呼び出す
‘ ‘ ボタンを配置するなどの運用を想定します。
‘ End Sub

‘——————————————————————————–
‘ ユーザーが手動で「保存して閉じる」ボタンなどを押した際に、
‘ ReleaseProjectFileLockを呼び出すための例
‘ (標準モジュールに記述します)
‘——————————————————————————–
Sub SaveAndCloseProject()

Dim projectFilePath As String
Dim lockFilePath As String

projectFilePath = ThisWorkbook.Path & “\” & PROJECT_FILE_NAME
lockFilePath = projectFilePath & LOCK_FILE_EXTENSION

‘ — 1. ファイルを保存 —
On Error Resume Next ‘ 保存時のエラーを捕捉
ThisWorkbook.Save
If Err.Number <> 0 Then
MsgBox “ファイルの保存に失敗しました。ロック解除は行いません。”, vbCritical
On Error GoTo 0
Exit Sub
End If
On Error GoTo 0

‘ — 2. ロックファイルを削除 —
‘ ReleaseProjectFileLockプロシージャに処理を任せます
ReleaseProjectFileLock

‘ — 3. ワークブックを閉じる —
ThisWorkbook.Close

End Sub

コードの解説とポイント

  • `Option Explicit`: これはお約束!変数宣言を強制してくれるので、タイプミスによる思わぬエラーを防いでくれます。VBA開発の基本中の基本です。
  • 定数定義: `PROJECT_FILE_NAME` や `LOCK_FILE_EXTENSION` のように、ファイル名や拡張子を定数にしておくと、後で変更する際に一箇所を修正するだけで済み、管理が楽になります。
  • `ThisWorkbook.Path`: マクロが記述されているブックのフォルダパスを取得します。ただし、共有フォルダ上でこのマクロを含むブックが直接共有されている場合、`ThisWorkbook.Path` はその共有フォルダを指します。 もし、別のブックを操作するマクロの場合は、`Workbooks(“別のブック名.xlsm”).Path` のように、対象ブックのパスを明示的に指定する必要があります。
  • `Dir(lockFilePath) <> “”`: `Dir` 関数は、指定したファイルが存在すればファイル名を、存在しなければ空文字列を返します。これを使ってロックファイルの有無を判定しています。
  • `Environ(“USERNAME”)`: Windowsの環境変数からユーザー名を取得します。環境によっては取得できない場合もあるので、エラーハンドリング(`On Error Resume Next`)は重要です。
  • `FreeFile`: ファイル操作で使うファイル番号を自動で取得してくれます。
  • `Open lockFilePath For Output As #fileNum`: ロックファイルを「出力モード」で開きます。ファイルが存在しない場合は新規作成され、存在する場合は上書きされます。
  • `Print #fileNum, …`: ファイルに文字列を書き込みます。
  • `Close #fileNum`: ファイルを閉じます。これを忘れると、ファイルが正常に書き込まれなかったり、他のプログラムからロックされたままになってしまうので、絶対に忘れないでください。
  • `Kill lockFilePath`: ロックファイルを削除します。
  • `Workbook_BeforeClose` イベント: このイベントプロシージャは、ブックが閉じられる直前に自動的に実行されます。ここに `ReleaseProjectFileLock` を記述すれば、ブックを閉じるときに自動でロックが解除されます。
  • 注意点: `Workbook_BeforeClose` は、ユーザーが「保存せずに閉じる」を選択した場合でも実行されます。そのため、ファイルが正常に保存されていることを確認してからロックを解除する、というロジックをより厳密に組む必要があります。今回は、シンプルにするために `SaveAndCloseProject` という手動実行用のプロシージャを用意しました。
  • `SaveAndCloseProject` Sub: このプロシージャは、ユーザーに「保存して閉じる」ボタンなどを押してもらい、それをクリックすることで実行されることを想定しています。まずファイルを保存し、その後ロックを解除してからブックを閉じます。

陥りやすいエラーとその対策

  • ロックファイルが削除されないまま残ってしまう:
  • 原因: Excelのクラッシュ、ネットワーク切断、ユーザーのPCシャットダウン、VBAコードのエラー、`Close` ステートメントの実行漏れなど。
  • 対策:
  • VBAコードのエラーハンドリングをしっかり行う。
  • `Workbook_BeforeClose` イベントや、`SaveAndCloseProject` のような専用プロシージャでロック解除を確実に行う。
  • 定期的にロックファイルを確認し、古すぎるロックファイル(例: 1日以上前のロック)は手動または自動で削除する仕組みを設ける。(これは少し高度な運用になります)
  • ユーザーに「もしロックファイルが残ってしまったら、手動で削除してください」と周知する。
  • ロックファイルを作成・削除する権限がない:
  • 原因: 共有フォルダへの書き込み権限がない。
  • 対策: 共有フォルダへの適切なアクセス権限を設定する。
  • `ThisWorkbook.Path` の誤用:
  • 原因: マクロが格納されているブックと、操作対象のプロジェクトファイルが別の場合に、`ThisWorkbook.Path` を使ってしまう。
  • 対策: 操作対象のプロジェクトファイルのパスを、設定ファイルや定数で明確に定義・管理する。

より高度な排他制御へ~VB.NETとの連携~

今回紹介したVBAによるロックファイルは、シンプルで実装しやすいのが魅力です。しかし、Excel VBAだけでは、以下のような課題も考えられます。

  • Excelが起動していない状態でのロックチェック: VBAはExcelが起動していないと実行できません。
  • より堅牢なロック管理: 誰がいつロックしたかの詳細なログ、ロックのタイムアウト(一定時間経過したら自動解除)などの高度な機能。

これらの課題を解決するために、VB.NETなどの外部アプリケーションと連携することも検討できます。

VB.NETであれば、Excelが起動していなくてもファイルシステムにアクセスしてロックファイルを作成・削除できます。さらに、データベースと連携してロック情報を管理するなど、より高度で堅牢な排他制御システムを構築することも可能です。

もし、皆さんがVB.NETにも興味があれば、また別の機会に、その連携方法についても深掘りしていきましょう!

まとめ:安全な共有ファイル編集のために

今回は、共有フォルダ上のプロジェクトファイルにおける、複数ユーザー同時編集の危険性と、それを回避するための「ロックファイル」による排他制御について、VBAでの実装方法を交えて解説しました。

  • ロックファイルは「誰かが使っていますよ」という目印
  • ファイルを開く前にロックファイルを作成し、閉じるときに削除する
  • VBAの `Dir` 関数、`Open`、`Print`、`Kill` などを活用する
  • エラーハンドリングと `Close` ステートメントの実行漏れに注意する

このロックファイルの仕組みを理解し、適切に実装することで、皆さんのチームでのファイル共有作業は格段に安全でスムーズになるはずです。

「ここをクリアすれば、Project VBAの基本はバッチリですよ」と、自信を持って言えます!ぜひ、皆さんのプロジェクトでこのテクニックを試してみてくださいね。

次回の記事でも、皆さんのVBAスキルアップに役立つ、実践的な知識をお届けしますので、お楽しみに!

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