【入門編】【上級プロ向け】大規模マルチシート図面のメモリリークとCOMオブジェクト解放の完全最適化設計 – SolidWorks VBA解析バイブル

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

【上級プロ向け】大規模マルチシート図面のメモリリークとCOMオブジェクト解放の完全最適化設計

「マクロの記録」から一歩踏み出し、自分でコードを書き始めた皆さん。素晴らしい挑戦です。
しかし、ある日突然、こんな壁にぶつかったことはありませんか?

「1枚の図面を処理する時は完璧なのに、100枚連続で回すとSolidWorksが固まる(フリーズする)、あるいは途中で落ちる……」

これは、あなたが書いたロジックのミスではありません。SolidWorks VBA、ひいてはWindowsの基盤技術である「COM(Component Object Model)」の扱いにおける、『オブジェクトのライフサイクル管理』という、プロフェッショナルへの登竜門とも言える課題に直面しているのです。

今日は、単なる「動くコード」ではなく、大規模な実務に耐えうる「堅牢なアーキテクチャ」を設計するための極意を伝授します。

1. なぜ、図面を回すとメモリが「食いつぶされる」のか?

まず、敵の正体を知りましょう。犯人は「メモリリーク」です。

SolidWorks VBAで操作を行う際、私たちは常に「オブジェクト」を呼び出しています。
`DrawingDoc`(図面)、`Sheet`(シート)、`View`(図面ビュー)……。これらは単なる変数ではなく、SolidWorksの巨大なメモリ空間の中に存在する「実体」への参照です。

「貸し出し」と「返却」のメタファー

COMオブジェクトの仕組みは、「図書館で本を借りる」ことに似ています。

1. Set swView = swSheet.GetFirstView()
→ 図書館員に「ビュー」という本を借りる行為です。
2. 処理を行う
→ 本を読んで、図面を書き換える行為です。
3. (ここでミス!)
→ 本を読み終わったのに、机に置きっぱなしにして帰ってしまう。

これを数百回繰り返すとどうなるでしょうか? 図書館(メモリ)の机は本で埋め尽くされ、新しい本(次の図面)を置くスペースがなくなります。これが、SolidWorksが重くなり、最終的にクラッシュするメカニズムです。

2. プロの鉄則:オブジェクトの「逆順解放」

初心者は、コードの最後に `Set swApp = Nothing` とだけ書けば済むと考えがちです。しかし、これは不十分です。

オブジェクトには「階層構造(親子関係)」があります。

  • Application(親)
  • ModelDoc2 / DrawingDoc(子)
  • Sheet(孫)
  • View(ひ孫)

この階層構造において、最も重要なルールがあります。
それは、「末端(子)から順に、逆順で解放せよ」ということです。

もし、親である `DrawingDoc` を先に `Nothing` にしようとしても、その下に `View` という「参照」が残っている限り、コンピュータは「まだこの図面は使われているな」と判断し、メモリを解放してくれません。これが、メモリリークの正体です。

3. 実践:堅牢なマルチシート処理テンプレート

それでは、実際に数百枚の図面を安全に、かつ高速に処理するためのプロフェッショナルなコード構成を見ていきましょう。

以下のコードは、複数の図面ファイルをループで開き、各シートのビュー情報を取得して、適切にメモリを解放する設計思想に基づいています。

' ==============================================================================
' プロジェクト: 大規模図面一括処理エンジン
' 目的: メモリリークを最小限に抑えたマルチシート・ビュー制御
' 設計思想: Bottom-Up Release (末端からの逆順解放)
' ==============================================================================

Option Explicit

Sub ProfessionalDrawingProcessor()
    Dim swApp As SldWorks.SldWorks
    Dim fileList As Variant
    Dim i As Long
    
    ' 1. SolidWorksアプリケーションへの接続
    Set swApp = Application.SldWorks
    
    ' 本来はフォルダ内のファイルリストを取得する処理が入ります
    fileList = Array("C:\Temp\Drawing1.drw", "C:\Temp\Drawing2.drw") 

    On Error GoTo ErrorHandler

    ' --- メインループ ---
    For i = LBound(fileList) To UBound(fileList)
        Debug.Print "Processing: " & fileList(i)
        
        ' 個別の図面処理をサブルーチンに切り出す(重要!)
        ' これにより、変数スコープが限定され、メモリ管理が容易になります
        ProcessSingleDrawing swApp, CStr(fileList(i))
        
        ' OSに制御を一度戻し、描画更新やメモリ整理の猶予を与える
        DoEvents 
    Next i

    MsgBox "全工程が正常に完了しました。", vbInformation
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical
End Sub

''' <summary>
''' 個別の図面ファイルを処理するサブプロシージャ
''' </summary>
Private Sub ProcessSingleDrawing(ByRef swApp As SldWorks.SldWorks, ByVal filePath As String)
    ' 変数宣言(スコープをこのプロシージャ内に限定)
    Dim swModel As SldWorks.ModelDoc2
    Dim swDraw As SldWorks.DrawingDoc
    Dim swSheet As SldWorks.Sheet
    Dim swView As SldWorks.View
    Dim vSheetNames As Variant
    Dim sSheetName As Variant

    ' 2. 図面を開く
    Set swModel = swApp.OpenDoc6(filePath, swDocDRAWING, swOpenDocOptions_Silent, "", 0, 0)
    
    If swModel Is Nothing Then
        Debug.Print "Failed to open: " & filePath
        Exit Sub
    End If
    
    Set swDraw = swModel

    ' 3. シートのループ処理
    vSheetNames = swDraw.GetSheetNames
    
    For Each sSheetName In vSheetNames
        ' シートの取得
        Set swSheet = swDraw.ActivateSheet(CStr(sSheetName))
        
        ' 4. ビューのループ処理(ここが最深部)
        ' GetFirstViewはシートそのものを指すため、その次のビューから取得
        Set swView = swDraw.GetFirstView ' Sheet itself
        Set swView = swView.GetNextView  ' Actual first view
        
        Do While Not swView Is Nothing
            ' --- ここにビューに対する具体的な処理(配置、尺度変更など)を記述 ---
            ' 例: Debug.Print "View Name: " & swView.Name
            
            ' 次のビューへ
            Set swView = swView.GetNextView
            
            ' 【重要】ループ内での一時的な参照は、必要に応じて Nothing にする
            ' ただし、GetNextViewで上書きする場合は自動的に参照カウントが減ります
        Loop
        
        ' --- シートの解放(末端から順に) ---
        Set swView = Nothing
        Set swSheet = Nothing
    Next sSheetName

    ' 5. 図面を閉じる
    swApp.CloseDoc swModel.GetTitle
    
    ' 6. 【極めて重要】オブジェクトの明示的な解放 (Bottom-Up)
    ' 階層の深い順に Nothing を代入していく
    Set swDraw = Nothing
    Set swModel = Nothing
    
    Debug.Print "Successfully released: " & filePath
End Sub

4. ここがプロの「こだわり」ポイント

このコードには、初学者が陥る罠を回避するための3つの高度なテクニックが組み込まれています。

① サブルーチン化による「スコープの局所化」

`ProcessSingleDrawing` という別のプロシージャに処理を切り出しています。
なぜでしょうか? VBAの変数は、そのプロシージャが終わった瞬間に「役目を終えた」とみなされます。巨大なループの中に全ての変数を詰め込むのではなく、「一仕事終えたら変数をまとめて掃除する」という設計にすることで、メモリ管理のミスを構造的に防いでいます。

② `DoEvents` の魔法

大規模処理では、SolidWorksが「応答なし」になりがちです。ループの節目に `DoEvents` を挟むことで、Windowsに対して「ちょっと一息ついて、画面更新やマウス操作の受付をしていいよ」と許可を与えます。これにより、フリーズを防ぎ、ユーザー体験を向上させます。

③ 徹底した「逆順解放」の実装

コードの終盤を見てください。
`swView` $\rightarrow$ `swSheet` $\rightarrow$ `swDraw` $\rightarrow$ `swModel` の順で、階層を遡るように `Nothing` を代入しています。これが、メモリという名の図書館に本を正しく返却する、最も美しい作法です。

最後に:技術は「規律」から生まれる

「マクロの記録」で作ったコードは、いわば「動けばいい」という短期的な解決策です。
しかし、あなたがこれから向き合うのは、何百枚もの図面、何千もの部品、そしてそれらを扱うプロフェッショナルとしての時間です。

今回学んだ「オブジェクトのライフサイクルを制御する」という考え方は、VBAだけでなく、C#やPython、あるいはあらゆるプログラミング言語に通じる普遍的な真理です。

この規律をコードに刻み込むことができれば、あなたの自動化ツールは、単なる「便利な道具」から、「絶対に信頼できるパートナー」へと進化するはずです。

さあ、自信を持って、次の自動化プロジェクトへ挑戦してください。応援していますよ!

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