【テクニカル・上級編】【カスタムプロパティ操作】CustomPropertyManager.GetNamesとEnumCustomPropertiesを用いたメタデータの完全同期・一括クリーンアップ – SolidWorks VBA解析バイブル

スポンサーリンク

【SolidWorks VBA極限解説】CustomPropertyManagerによるメタデータの完全同期と一括クリーンアップ

レガシーパーツの海に沈む、無秩序なカスタムプロパティ。設計者ごとに異なるキー名、全角・半角の揺れ、消し忘れたゴミデータ――これらはPDM(Product Data Management)やERP連携、自動図面生成スクリプトの最大の障壁となる。

一般的なチュートリアルでは `GetNames` や `Get` といった基本メソッドの解説で終わるが、実務の現場で求められるのは、「何万ものレガシー資産から無駄なゴミを完全に削ぎ落とし、社内標準の最新スキーマへミリ秒単位で強制同期させる高信頼エンジン」である。

今回は、SolidWorks APIの `CustomPropertyManager` を極限まで使い倒し、メモリ効率と堅牢性を担保したメタデータ一括クリーンアップ&同期の実装コードを公開する。

1. カスタムプロパティ操作のアーキテクチャ上の罠

SolidWorksのパーツ、アセンブリ、コンフィギュレーションには、それぞれ独立した `CustomPropertyManager` が存在する。これを誤認すると、「パーツ全体のプロパティを書き換えたつもりが、アクティブなコンフィギュレーションにしか反映されていなかった」という致命的なバグを生む。

さらに、VBA環境におけるCOMオブジェクトの参照解放(オブジェクトチェーンの切断)を怠ると、メモリリークだけでなく、SolidWorksプロセス自体が不安定化する要因となる。伝説的エンジニアであれば、インスタンスの生存期間(ライフサイクル)を完全にコードで支配しなければならない。

メタデータ同期の3大原則

1. コンフィギュレーション固有とモデル全体の完全分離:用途に応じた適切な `CustomPropertyManager` の取得。
2. 安全なスキャンと一括削除の分離:列挙中のコレクションを直接変更(イテレーション中の削除)する御法度を避ける。
3. 明示的なメモリ解放:`Set oPropMgr = Nothing` によるCOM参照カウンタの確実なデクリメント。

2. 実務仕様:メタデータ一括クリーンアップ&同期エンジン

以下のVBAコードは、アクティブなドキュメントから既存のカスタムプロパティ名をすべてスキャンし、ブラックリスト(不要なレガシープロパティ)を一掃した上で、社内標準スキーマ(ホワイトリスト・規定値)を強制適用する堅牢なスクリプトである。

Option Explicit

‘ =========================================================================
‘ ódulo: 模範的カスタムプロパティ同期エンジン
‘ 概要: レガシーメタデータを完全スキャンし、スキーマの強制同期とゴミ掃除を行う
‘ =========================================================================

Public Sub SyncAndCleanCustomProperties()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swCustPropMgr As SldWorks.CustomPropertyManager

Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc

If swModel Is Nothing Then
MsgBox “アクティブなドキュメントが存在しません。”, vbCritical, “致命的エラー”
Exit Sub
End If

‘ モデル全体(ConfigSpecific = False)のマネージャーを取得
‘ ※コンフィギュレーション固有を対象にする場合は swModel.Extension.CustomPropertyManager(“”) を使用
Set swCustPropMgr = swModel.Extension.CustomPropertyManager(“”)

If swCustPropMgr Is Nothing Then
MsgBox “CustomPropertyManagerの取得に失敗しました。”, vbCritical
Exit Sub
End If

On Error GoTo ErrorHandler

‘ ———————————————————————
‘ STEP 1: 既存プロパティの列挙と不要データの特定(クリーンアップ)
‘ ———————————————————————
Dim vPropNames As Variant
Dim lRetVal As Long

‘ GetNamesはVariant配列を返す(プロパティが存在しない場合はEmpty)
lRetVal = swCustPropMgr.GetNames(vPropNames)

If Not IsEmpty(vPropNames) Then
Dim i As Long
Dim propName As String

For i = LBound(vPropNames) To UBound(vPropNames)
propName = CStr(vPropNames(i))

‘ 【判定】社内標準スキーマに含まれない、または削除対象のレガシーキーか?
If IsLegacyOrObsolete(propName) Then
‘ 削除実行
swCustPropMgr.Delete (propName)
Debug.Print “[-] 削除完了: ” & propName
End If
Next i
End If

‘ ———————————————————————
‘ STEP 2: 社内標準スキーマの強制適用(アップサート: 更新または追加)
‘ ———————————————————————
‘ 例: 品名、材料、設計者、承認者、社内規格コード
Call UpsertProperty(swCustPropMgr, “SW-MATERIAL”, “JIS-SUS304”)
Call UpsertProperty(swCustPropMgr, “SW-AUTHOR”, “Automation_Engine”)
Call UpsertProperty(swCustPropMgr, “SW-SCHEMA_VER”, “2.0.0”)

‘ ———————————————————————
‘ STEP 3: 変更の強制リビルドとビュー更新
‘ ———————————————————————
swModel.ForceRebuild3 False

MsgBox “カスタムプロパティの同期およびクリーンアップが正常に完了しました。”, vbInformation, “完了”

CleanUp:
‘ COMオブジェクトの明示的解放(メモリ最適化の極意)
Set swCustPropMgr = Nothing
Set swModel = Nothing
Set swApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “異常終了”
Resume CleanUp
End Sub

‘ =========================================================================
‘ ヘルパー関数: レガシー・不要プロパティ判定ロジック
‘ =========================================================================
Private Function IsLegacyOrObsolete(ByVal propName As String) As Boolean
‘ ここに社内のブラックリスト、あるいは命名規則から外れた不正キーの判定を記述
Select Case UCase(Trim(propName))
Case “OLD_NAME”, “TEMP_PROP”, “MEMBER_ID”, “DRAWN_BY_OLD”
IsLegacyOrObsolete = True
Case Else
‘ アンダースコアから始まる一時的なプロパティも一掃対象とする場合
If Left(propName, 1) = “_” Then
IsLegacyOrObsolete = True
Else
IsLegacyOrObsolete = False
End If
End Select
End Function

‘ =========================================================================
‘ ヘルパー関数: プロパティの安全な追加・更新(Add3 / Set2 のラッパー)
‘ =========================================================================
Private Sub UpsertProperty(ByRef propMgr As SldWorks.CustomPropertyManager, ByVal propName As String, ByVal propValue As String)
Dim valOut As String
Dim resolvedOut As String
Dim status As Long
Dim linkStatus As Boolean

‘ 既存存在確認(Get6を使用: swCustomInfoType_Text = 30)
status = propMgr.Get6(propName, False, valOut, resolvedOut, linkStatus, 0)

If status = swCustomInfoGetResult_NotPresent Then
‘ 新規追加 (SwConstantValueType_Text = 1)
propMgr.Add3 propName, swCustomInfoText, propValue, swCustomPropertyReplaceValue
Debug.Print “[+] 追加完了: ” & propName & ” = ” & propValue
Else
‘ 既存上書き
propMgr.Set2 propName, propValue
Debug.Print “[] 更新完了: ” & propName & ” = ” & propValue
End If
End Sub

3. チーフアーキテクトが解説するコードの急所

`GetNames` が返すVariant配列の罠

`CustomPropertyManager.GetNames` は、プロパティが一件も登録されていないモデルに対して実行すると、`Empty` を返す。これを考慮せずに `LBound` や `UBound` を直接叩くと、VBAランタイムエラー(インデックスが有効範囲にありません)を引き起こす。
必ず `IsEmpty(vPropNames)` による事前ガードを実装すること。

`Add3` と `Set2` の使い分けによるトランザクション的安定性

プロパティの操作において、「あれば更新、なければ追加」というロジック(UPSERT)は不可欠である。

  • `Add3`: 存在しないキーに対してのみ新規作成を試みる。第4引数に `swCustomPropertyReplaceValue` を指定することで、万が一の競合時にも安全に動作する。
  • `Set2`: 既存のキーに対して高速に値を書き換える。

この2つをラッパー関数 (`UpsertProperty`) 内で綺麗に分岐させることで、スクリプトの可読性と保守性を飛躍的に高めている。

メモリリークを根絶する `Set = Nothing` の哲学

VBAからSolidWorksのCOM APIを呼び出す際、背後ではC++のCOMインターフェースが動いている。参照カウントが適切にデクリメントされないままスクリプトが終了すると、バックグラウンドで `SLDWORKS.exe` のゾンビプロセスが残り続け、次回のバッチ処理やPDM連携のフックを完全破壊する。
スクリプトの出口(`CleanUp:` ラベル)を必ず用意し、生成したすべてのオブジェクト変数を `Nothing` で明示的に解放する――これがプロフェッショナルの鉄則である。

4. システム間連携(PDM / ERP)への応用

このモジュールをベースに、外部データベース(SQL ServerやOracle等)やPLM/PDMシステムのREST APIから取得した最新のマスターデータを、ファイルオープン時にバックグラウンドで自動同期する「ヘッドレス実行(Documentなし / 外部起動)」へ拡張することも容易だ。

レガシーデータの呪縛から脱却し、常にクリーンで構造化されたメタデータを維持すること。それが、堅牢な自動化エコシステムを構築するための唯一にして最大の近道である。

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