【上級プロ】複数ユーザーの同時アクセスを想定したファイルロック回避:SolidWorksアセンブリの読取専用(Read-Only)自動判定と安全な排他制御
エンタープライズ環境において、複数エンジニアが同一の共有サーバーやPDM(Product Data Management)リポジトリ上で設計データにアクセスすることは日常茶飯事である。しかし、SolidWorksのドキュメント管理において、何も考えずにVBAからファイルをオープンすればどうなるか。他者が編集中(排他ロック中)のファイルを無慈悲に上書きし、数日分のエンジニアリング工数を灰に変えるか、あるいはVBランタイムの醜悪な実行時エラー(エラー70: 書き込みできません)でスクリプトが非業の死を遂げることになる。
本稿では、レガシーなVBAの限界を突破し、OSレベルのファイル共有セマンティクスとSolidWorks APIを完全に調停する「ネットワーク対応型例外ハンドリングと安全な排他制御」の極限アーキテクチャを提示する。
—
1. なぜSolidWorks VBAの標準機能だけでは不十分なのか
初心者が陥る最初の罠は、`SldWorks.Application.OpenDoc6` メソッドの戻り値やエラー処理にのみ依存することだ。
SolidWorksのAPIは強力だが、ファイルがネットワーク上で「誰に、どのようにロックされているか」の事前診断能力において、OSのファイルI/Oサブシステムほどのきめ細やかさを持たない。特に、読取専用(Read-Only)で安全にサイレントオープンし、他者の作業を阻害せずにBOM情報を抽出したり、軽量プレビューを生成したりするエンタープライズ要件においては、「開く前の悲観的ロック検知」が絶対に不可欠となる。
ここでWindows API(Kernel32)の出番だ。VBAから直接ファイルストリームの排他制御(`CreateFile` API)を叩くことで、SolidWorksがファイルを掴むコンマ数秒前に、そのファイルの生死とアクセス権限を完全かつ不可逆的に判定する。
—
2. Windows APIによるファイルロックプリエンプティブ検知
以下のコードは、指定されたファイルパスに対して排他制御をかけたテストオープンを試み、他プロセスによる占有状態(Exclusive Lock)をミリ秒単位で検知する堅牢な関数群である。
Option Explicit
‘ — Windows API Declarations for File Locking Check —
Private Declare PtrSafe Function CreateFile Lib “kernel32” Alias “CreateFileW” ( _
ByVal lpFileName As LongPtr, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As LongPtr, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As LongPtr) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib “kernel32” ( _
ByVal hObject As LongPtr) As Long
Private Const GENERIC_READ As Long = &H80000000
Private Const GENERIC_WRITE As Long = &H40000000
Private Const FILE_SHARE_READ As Long = &H1
Private Const FILE_SHARE_WRITE As Long = &H2
Private Const OPEN_EXISTING As Long = 3
Private Const FILE_ATTRIBUTE_NORMAL As Long = &H80
Private Const INVALID_HANDLE_VALUE As LongPtr = -1
/
- @brief 指定されたファイルが他者によって排他ロックされているかを判定する
- @param filePath 検査対象のフルパス
- @return True = ロックされている(書き込み不可), False = アクセス可能
/
Public Function IsFileLockedByOther(ByVal filePath As String) As Boolean
Dim hFile As LongPtr
‘ 書き込み権限を要求しつつ、他のプロセスの読み書きを一切拒否するモードでハンドル取得を試みる
‘ すでに他者が開いている場合、このAPIは INVALID_HANDLE_VALUE を返す
hFile = CreateFile( _
StrPtr(filePath), _
GENERIC_READ Or GENERIC_WRITE, _
0, _
0, _
OPEN_EXISTING, _
FILE_ATTRIBUTE_NORMAL, _
0)
If hFile = INVALID_HANDLE_VALUE Then
‘ ハンドル取得失敗 = 誰かが掴んでいる、またはアクセス権がない
IsFileLockedByOther = True
Else
‘ 取得成功 = ロックフリー。即座にハンドルを解放する
IsFileLockedByOther = False
Call CloseHandle(hFile)
End If
End Function
このプリエンプティブ(先取り的)なチェックにより、SolidWorksの重いドキュメントオープン処理を走らせる前に、スクリプト側で「読取専用モードで開くべきか」「処理をスキップしてアラートを出すべきか」の分岐を完全に行えるようになる。
—
3. SolidWorks アセンブリの安全な排他制御とメモリ最適化
ファイルの状態を把握した上で、SolidWorks APIを叩く。ここでの最大のタブーは、「不必要なドキュメントのメモリ常駐」と「COMオブジェクトの解放漏れ(メモリリーク)」である。
アセンブリファイル(`.sldasm`)は巨大なコンポーネントツリーを持つ。これをバックグラウンドで処理する際、メモリ最適化の極意を適用しなければ、VBAホストプロセス(EXCELやSolidWorks本体)は確実にメモリリークを起こし、やがてOut of Memoryでクラッシュする。
以下の実装パターンを標準とせよ。
Public Enum AssemblyOpenResult
swOpenSuccess_ReadWrite = 0
swOpenSuccess_ReadOnly = 1
swOpenError_LockedByPeer = 2
swOpenError_FileNotFound = 3
swOpenError_Unknown = 99
End Enum
/
- @brief ネットワーク整合性を担保したSolidWorksアセンブリの安全オープン
- @param swApp SldWorksアプリケーションインスタンス
- @param filePath アセンブリのフルパス
- @param outDoc [out] オープンされたModelDoc2オブジェクト
- @return AssemblyOpenResult 列挙体
/
Public Function SafeOpenAssembly( _
ByVal swApp As SldWorks.SldWorks, _
ByVal filePath As String, _
ByRef outDoc As SldWorks.ModelDoc2) As AssemblyOpenResult
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FileExists(filePath) Then
SafeOpenAssembly = swOpenError_FileNotFound
Exit Function
End If
Dim isReadOnly As Boolean
isReadOnly = False
‘ 1. OSレベルでの排他ロック検知
If IsFileLockedByOther(filePath) Then
‘ 他者が書き込み権限を持って開いている場合、読取専用(ReadOnly)でのオープンを強制する
isReadOnly = True
End If
Dim errorCode As Long
Dim warningCode As Long
Dim docSpec As SldWorks.DocumentSpecification
‘ 2. DocumentSpecification 객체の生成(SolidWorks 2012以降のベストプラクティス)
Set docSpec = swApp.GetOpenDocSpec(filePath)
With docSpec
.ReadOnly = isReadOnly
.Silent = True ‘ ダイアログを表示させない(完全なヘッドレス/自動化制御)
.DocumentType = swDocASSEMBLY
.LightWeight = swConfigurationLoad_LightWeight ‘ 大規模アセンブリ対策:軽量モードでロード
End With
‘ 3. ドキュメントのオープン実行
Set outDoc = swApp.OpenDoc7(docSpec)
‘ DocumentSpecificationの明示的破棄(COMラッパーのメモリ解放)
Set docSpec = Nothing
If outDoc Is Nothing Then
SafeOpenAssembly = swOpenError_Unknown
Exit Function
End If
‘ 4. 結果の返却
If isReadOnly Then
SafeOpenAssembly = swOpenError_LockedByPeer ‘ 実際には開けたが、実質的に読取専用
Else
SafeOpenAssembly = swOpenSuccess_ReadWrite
End If
‘ 【重要】呼び出し元で不要になった ModelDoc2 は必ず ReleaseComObject 相当の解放を行うこと。
‘ 例: Set outDoc = Nothing
End Function
—
4. チーフアーキテクトからの実践的提言:例外ハンドリングとリトライ戦略
ネットワークドライブ(NASや共有フォルダ)上でSolidWorksを操作する場合、パケットロスや一時的なネットワークの切断(スリープ復帰後など)により、ファイルI/Oの瞬断が発生する。
頑健な(Resilientな)システムを構築するためには、単にエラーを検知して終了するのではなく、「エクスポネンシャル・バックオフ(指数関数的バックオフ)を伴うリトライ機構」をマクロの根底に組み込まなければならない。
/
- @brief ネットワークの揺らぎを考慮したリトライ付きアセンブリオープン
/
Public Function SafeOpenAssemblyWithRetry( _
ByVal swApp As SldWorks.SldWorks, _
ByVal filePath As String, _
ByRef outDoc As SldWorks.ModelDoc2, _
Optional ByVal maxRetries As Long = 3) As AssemblyOpenResult
Dim attempt As Long
Dim result As AssemblyOpenResult
Dim waitTimeMs As Long
waitTimeMs = 1000 ‘ 初期ウェイト 1秒
For attempt = 1 To maxRetries
On Error Resume Next
result = SafeOpenAssembly(swApp, filePath, outDoc)
On Error GoTo 0
If result <> swOpenError_Unknown Then
‘ 成功、あるいは明確な排他ロックの場合はリトライしない
SafeOpenAssemblyWithRetry = result
Exit Function
End If
‘ 不明なエラー(ネットワーク瞬断の可能性)の場合、待機してリトライ
If attempt < maxRetries Then
VBA.Interaction.DoEvents
' Sleep APIなどを利用してウェイトを入れる(ここでは簡易的にループ)
Dim start As Double
start = Timer
Do While Timer < start + (waitTimeMs / 1000)
DoEvents
Loop
waitTimeMs = waitTimeMs 2 ' バックオフ倍加
End If
Next attempt
SafeOpenAssemblyWithRetry = swOpenError_Unknown
End Function
---
5. 結び:コードの美しさは、システムの生存率に直結する
VBAは「おもちゃの言語」と揶揄されることがある。しかし、APIの裏側にあるOSの挙動(ファイルハンドル、メモリ管理、COMの参照カウント)を完全に理解した上で記述されたVBAコードは、C#やC++で書かれたネイティブアプリケーションと同等、あるいはそれ以上の堅牢性を発揮する。
複数ユーザー環境での「上書き事故」と「実行時エラー」は、プログラマの怠慢が生んだ人災に他ならない。本稿で示したWindows APIによるプリエンプティブ・チェックと、SolidWorksのDocumentSpecificationによる安全なロード手順をあなたのコードベースに統合し、真にプロフェッショナルな自動化基盤を構築してほしい。
