【テクニカル・上級編】【上級プロ向け】DrawingDoc.FindReferencesを用いた参照モデル欠損の事前チェックと自動修復機構 – SolidWorks VBA解析バイブル

スポンサーリンク
スポンサーリンク

The Ghost in the Drawing: 欠損参照を自動修復する「自己治癒型」図面管理アーキテクチャ

設計現場において、PDM(Product Data Management)の移行やファイルリネーム、あるいは不完全なディレクトリ移動は、エンジニアの生産性を削り取る「静かなる侵略者」である。

図面ファイルを開くたびに発生する「参照モデルが見つかりません」というダイアログ。これを手動で一つずつ、ダイアログを閉じてはファイル選択を繰り返す。そんな作業は、高度な計算資源を、単純なファイルパスの解決という低次なタスクに浪費しているに等しい。

本稿では、SolidWorks APIの深淵に触れ、`DrawingDoc.FindReferences` を起点とした、欠損参照の検知から「ヒューリスティック(類推)アルゴリズム」による自動修復まで、極限の堅牢性を備えたエンジニアリング手法を解説する。

1. 参照欠損のメカニズムと `FindReferences` の真価

通常の自動化スクリプトは、`GetModelDoc2` でモデルを取得しようとし、失敗すればエラーハンドリングで終了する。しかし、それは単なる「エラーの検知」であり、「修復」ではない。

図面(Drawing)における参照の正体は、各ビュー(View)が保持するモデルへのポインタである。`DrawingDoc.FindReferences` メソッドは、図面内で使用されているすべてのモデルへの参照を配列として返すが、参照が切れている場合、そのオブジェクトの `GetPathName` が期待されるパスを指しながらも、物理的なファイルが存在しないという「不整合状態」に陥る。

我々が狙うべきは、この「不整合状態にあるポインタの特定」である。

2. アーキテクチャ設計:検知・類推・結合

堅牢な修復ツールを構築するには、以下の3層構造が必要となる。

1. Detection Layer (検知層): `FindReferences` を走査し、`FileSystemObject` または Windows API を用いて物理的な存在確認を行う。
2. Heuristic Layer (類推層): 欠損したファイル名に基づき、指定されたディレクトリツリー内を再帰探索し、ファイル名の一致度(Levenshtein Distance 等の概念)から「真のモデル」を特定する。
3. Restoration Layer (修復層): 特定されたパスを用いて、`ReplaceReference` を実行し、図面の整合性を再構築する。

3. 実装への深淵:極限のVBAコード

以下に、シニアエンジニアが実戦投入するための、メモリ管理とエラーハンドリングを極限まで突き詰めた実装例を示す。

'**
' Module: DrawingRepairEngine
' Purpose: 自動参照修復エンジン (Advanced Implementation)
' Author: Chief Architect
'**

Option Explicit

' Windows API: ファイルの存在確認を高速化するために使用
Private Declare PtrSafe Function GetFileAttributes Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As String) As Long
Private Const INVALID_FILE_ATTRIBUTES As Long = &HFFFFFFFF

' 修復エンジンのメインエントリポイント
Public Sub ExecuteAdvancedRepair(ByRef swApp As SldWorks.SldWorks, ByRef swDraw As SldWorks.DrawingDoc)
    Dim vRefs As Variant
    Dim i As Long
    Dim swModelRef As SldWorks.ModelDoc2
    Dim expectedPath As String
    Dim actualPath As String
    Dim repairedPath As String
    
    ' 1. 参照リストの取得
    vRefs = swDraw.FindReferences
    
    If IsEmpty(vRefs) Then
        Debug.Print "No references found."
        Exit Sub
    End If

    ' 2. 参照の走査
    For i = 0 To UBound(vRefs)
        Set swModelRef = vRefs(i)
        
        ' モデルが取得できているが、パスが壊れているケースを想定
        expectedPath = swModelRef.GetPathName
        
        ' 物理ファイルの存在確認 (Windows APIによる高速チェック)
        If GetFileAttributes(expectedPath) = INVALID_FILE_ATTRIBUTES Then
            Debug.Print "Broken Reference Detected: " & expectedPath
            
            ' 3. ヒューリスティック修復の実行
            ' ここでは、ファイル名のみを抽出して検索を行う
            repairedPath = HeuristicSearch(swApp, expectedPath)
            
            If repairedPath <> "" Then
                ' 4. 参照の再アタッチ
                ' 注意: ReplaceReference は図面全体の整合性を再計算するため、コストが高い
                Debug.Print "Attempting Repair: " & repairedPath
                
                ' DrawingDocのメソッドを使用して参照を置換
                ' 第2引数以降のオプションは要件に応じて調整
                Dim status As Boolean
                status = swDraw.ReplaceReference(expectedPath, repairedPath)
                
                If status Then
                    Debug.Print "Successfully Repaired: " & expectedPath
                Else
                    Debug.Print "Failed to Replace: " & expectedPath
                End If
            Else
                Debug.Print "Heuristic search failed for: " & expectedPath
            End If
        End If
        
        ' メモリ管理: オブジェクトの明示的解放
        ' 大規模アセンブリでは、これが欠けるとSolidWorksが即座にクラッシュする
        Set swModelRef = Nothing
    Next i

    ' 5. 図面の再構築
    swDraw.ForceRebuild3
    Debug.Print "Repair Process Completed."
End Sub

' ヒューリスティック検索エンジン
' ファイル名に基づき、指定されたルートディレクトリから類似ファイルを探索する
Private Function HeuristicSearch(ByRef swApp As SldWorks.SldWorks, ByVal brokenPath As String) As String
    Dim fso As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    Dim fileName As String
    fileName = fso.GetFileName(brokenPath)
    
    ' 検索対象のルート(本来はPDMのルートやプロジェクトフォルダを指定すべき)
    ' ここでは例として、図面と同じディレクトリを検索対象とする
    Dim searchRoot As String
    searchRoot = fso.GetParentFolderName(brokenPath)
    
    ' 実運用では、ここで再帰的な探索アルゴリズムを実装する
    ' 以下のコードは、簡易的な「同一ディレクトリ内の一致確認」の骨格である
    Dim foundPath As String
    foundPath = RecursiveSearch(fso, searchRoot, fileName)
    
    HeuristicSearch = foundPath
    Set fso = Nothing
End Function

' 再帰的ファイル検索 (パフォーマンスを考慮した実装)
Private Function RecursiveSearch(ByRef fso As Object, ByVal currentDir As String, ByVal targetName As String) As String
    Dim folder As Object
    Dim subFolder As Object
    Dim file As Object
    
    On Error Resume Next ' アクセス権限のないフォルダをスキップ
    Set folder = fso.GetFolder(currentDir)
    If folder Is Nothing Then Exit Function
    
    ' 1. 現在のディレクトリ内でファイルを確認
    For Each file In folder.Files
        ' 完全一致、または拡張子を除いた名前での類推
        If LCase(file.Name) = LCase(targetName) Then
            RecursiveSearch = file.Path
            Exit Function
        End If
    Next file
    
    ' 2. サブディレクトリを再帰探索
    For Each subFolder In folder.SubFolders
        Dim result As String
        result = RecursiveSearch(fso, subFolder.Path, targetName)
        If result <> "" Then
            RecursiveSearch = result
            Exit Function
        End If
    Next subFolder
    On Error GoTo 0
End Function

4. アーキテクトの視点:パフォーマンスと安定性の極限

このコードを単なる「動くスクリプト」で終わらせてはならない。エンタープライズ環境に投入するには、以下の3点を考慮せよ。

I. オブジェクトのライフサイクル管理

VBAはガベージコレクションが極めて脆弱である。`swModelRef = Nothing` による明示的な解放は、単なる作法ではない。`FindReferences` が返すオブジェクト群は、内部的にCOMインターフェースの参照カウンタを増大させる。大規模な図面(数百のビューを持つもの)をループで回す際、これを怠れば、メモリリークによってSolidWorksのプロセスがメモリ不足(Out of Memory)で死に至る。

II. Windows API による I/O 最適化

`FileSystemObject` は便利だが、大量のファイルを走査する際にはオーバーヘッドが大きい。ファイルが存在するかどうかの判定(`GetFileAttributes`)には、可能な限り Windows API を直接呼び出すこと。これにより、I/O待ち時間を劇的に削減できる。

III. ヒューリスティックの精度(Levenshtein Distance の導入)

上記のコードでは単純な完全一致を用いているが、実務では `PartA_v2.sldprt` が `PartA.sldprt` にリネームされているケースが多々ある。
真に「伝説的」なツールを目指すなら、文字列の編集距離(Levenshtein Distance)を計算する関数を実装し、スコアが一定(例:80%以上)のファイルを「正解」と判定するロジックを組み込むべきだ。

結論

図面の自動修復は、単なる自動化の範疇を超えた「データの整合性維持」というインフラストラクチャの仕事である。`FindReferences` を使いこなし、メモリとI/Oの特性を理解したコードを書くこと。それが、レガシーなシステムを支え、次世代の設計環境を構築するシニアエンジニアに課せられた責務である。

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