【入門編】【上級プロ】カスタムイベントとWindows APIの非同期タイマーを融合した、アセンブリ自動ビルド中のリアルタイム進捗&中断UIの実装 – SolidWorks VBA解析バイブル

スポンサーリンク

こんにちは!SolidWorks VBAの世界へようこそ。
マクロの記録ボタンを押して生成されるコードを眺める段階はもう卒業しましたか?

「何百点もの部品を読み込み、位置を計算し、合致(Mate)を組んでいく……」
実務でアセンブリの自動ビルドを実装しようとすると、必ずぶ壁にぶつかります。そう、「処理中にSolidWorksがフリーズしたようになり、プログレスバーが動かない。おまけにキャンセルもできない」というあの絶望的な瞬間です。

VBAのシングルスレッドな世界では、重いループを回している間、ExcelやSolidWorksの画面は完全に固まります。ユーザーは「バグったのかな?」と強制終了し、パソコンの前で頭を抱えることになりますよね。

今回は、Windows APIの非同期タイマー(正確にはメッセージポンプの維持)を駆使し、数千行の重いアセンブリ構築処理の最中であっても、滑らかなプログレスバーと「キャンセルボタン」の動作を完全に担保する、極限のUI実装テクニックを伝授します。

ここをクリアすれば、あなたのVBAスキルは「お遊びのマクロ」から「プロフェッショナルなエンジニアリングツール」へと劇的に進化します。一緒に本質をマスターしていきましょう!

—

なぜ、通常のVBAコードでは画面が固まるのか?

私たちが普段書く `For i = 1 To 1000` のようなループ処理。VBAは真面目すぎるあまり、この計算が終わるまでWindowsからの「画面を描画して!」という命令(メッセージ)を一切無視してしまいます。これが「フリーズ(応答なし)」の正体です。

これを解決するためには、処理の裏でこっそりWindowsに対して「ねぇ、溜まっているメッセージ(ユーザーのクリックや画面の再描画)を処理してよ!」と呼びかける必要があります。それが `DoEvents` 関数……と言いたいところですが、アセンブリ構築のような超高負荷な処理では、`DoEvents`だけだとタイマーの精度が狂ったり、イベントを取りこぼしたりします。

そこで登場するのが、Windows APIの力です。

—

実装の全体像

今回は、以下の3つを組み合わせて「絶対に固まらないアセンブリビルダー」を作ります。

1. ユーザーフォーム(View): キャンセルボタンと滑らかなプログレスバーを持つUI。
2. Windows API(DoEventsの強化版): メッセージポンプを強制駆動させる仕組み。
3. SolidWorks API(Modeler / Assembly): パーツを配置し、合致を組むメインエンジン。

—

実装コード:完全版プログレスUI付きアセンブリビルダー

さあ、実際のコードを見ていきましょう。
まずはVBAの標準モジュールと、ユーザーフォーム(`FrmProgress`という名前と仮定)を用意してください。

1. Windows APIの宣言(標準モジュール)

Windowsが持つ「今、画面のイベントある?」を確認するためのAPIを宣言します。

Option Explicit

‘ Windowsのメッセージキューを処理するためのAPI
Public Declare PtrSafe Function GetMessage Lib “user32” Alias “GetMessageA” ( _
lpMsg As MSG, ByVal hwnd As LongPtr, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long) As Long

Public Declare PtrSafe Function TranslateMessage Lib “user32” (lpMsg As MSG) As Long
Public Declare PtrSafe Function DispatchMessage Lib “user32” Alias “DispatchMessageA” (lpMsg As MSG) As Long

Public Type POINTAPI
x As Long
y As Long
End Type

Public Type MSG
hwnd As LongPtr
message As Long
wParam As LongPtr
lParam As LongPtr
time As Long
pt As POINTAPI
End Type

‘ キャンセルボタンが押されたかを判定するフラグ
Public g_IsCancelled As Boolean

2. ユーザーフォームのコード(`FrmProgress`)

フォームには、プログレスバーの役割をするラベル(`lblBar`)と、キャンセルボタン(`btnCancel`)を配置してください。

Option Explicit

Private Sub btnCancel_Click()
‘ ユーザーがキャンセルを押したらグローバル旗を立てる
g_IsCancelled = True
Me.Caption = “キャンセル処理中…”
btnCancel.Enabled = False
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
‘ ✖ボタンで閉じられた場合もキャンセルとみなす
If CloseMode = vbFormControlMenu Then
g_IsCancelled = True
Cancel = True ‘ すぐには閉じず、処理側で安全に終了させる
End Sub
End Sub

3. メインのビルド処理(標準モジュール)

ここが今回の核心です。アセンブリを新規作成し、部品を配置しながら、定期的にWindowsのメッセージを強制処理させます。

Option Explicit

Sub BuildAssemblyWithAsyncUI()
‘ SldWorksアプリケーションのオブジェクト
Dim swApp As SldWorks.SldWorks
Dim swAssy As SldWorks.AssemblyDoc
Dim swModel As SldWorks.ModelDoc2

Set swApp = Application.SldWorks
If swApp Is Nothing Then
MsgBox “SolidWorksが起動していません。”, vbCritical
Exit Sub
End If

‘ キャンセルフラグの初期化
g_IsCancelled = False

‘ 1. 進捗用フォームのロードと表示(モーダレス表示がキモ!)
Dim frmProg As FrmProgress
Set frmProg = New FrmProgress
frmProg.Show vbModeless ‘ 非モーダル表示にすることで、裏で処理を続けられる

‘ 2. 新規アセンブリドキュメントの作成
Dim defaultTemplate As String
defaultTemplate = swApp.GetUserPreferenceStringValue(swUserPreferenceStringValue_e.swDefaultTemplateAssembly)
Set swModel = swApp.NewDocument(defaultTemplate, 0, 0, 0)
Set swAssy = swModel

If swAssy Is Nothing Then
MsgBox “アセンブリの新規作成に失敗しました。”, vbCritical
Unload frmProg
Exit Sub
End If

‘ — 仮想的な重いアセンブリ構築ループ(例:100個の部品を配置する想定) —
Dim totalSteps As Long
totalSteps = 100

Dim i As Long
For i = 1 To totalSteps

‘ — A. キャンセルボタンが押されたか毎ループでチェック —
‘ Windows APIを使ってメッセージポンプを回す
DoEventsAdvanced

If g_IsCancelled Then
MsgBox “ユーザーによって処理がキャンセルされました。”, vbInformation, “中断”
Exit For
End If

‘ — B. ここに本来の重いSolidWorks API処理を書く —
‘ 例: 実際には部品の挿入や合致追加などが入ります
‘ swAssy.AddComponents3(…)
‘ 負荷をシミュレートするためのウェイト
Application.Wait Now + TimeValue(“0:00:01”) / 10

‘ — C. UI(プログレスバー)のリアルタイム更新 —
UpdateProgress frmProg, i, totalSteps

Next i

‘ 終了処理
Unload frmProg

If Not g_IsCancelled Then
MsgBox “アセンブリの自動ビルドが正常に完了しました!”, vbInformation, “完了”
End If

End Sub

‘ —————————————————————-
‘ 秘伝のソース:Windowsメッセージを強制処理する高度なDoEvents
‘ —————————————————————-
Private Sub DoEventsAdvanced()
Dim msg As MSG
‘ メッセージキューに溜まったイベント(ボタンクリックや再描画要求)を消化する
Do While PeekMessageW(msg, 0, 0, 0, 1) ‘ PM_REMOVE = 1
TranslateMessage msg
DispatchMessage msg
Loop
End Sub

‘ 宣言の追加(APIのオーバーロード対策として内部で定義)
If VBA7 Then
Private Declare PtrSafe Function PeekMessageW Lib “user32” (lpMsg As MSG, ByVal hwnd As LongPtr, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
Else
Private Declare Function PeekMessageW Lib “user32” (lpMsg As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
End If

‘ —————————————————————-
‘ プログレスバーの見た目を滑らかに更新するサブルーチン
‘ —————————————————————-
Private Sub UpdateProgress(frm As FrmProgress, current As Long, total As Long)
Dim percent As Double
percent = (CDbl(current) / CDbl(total))

With frm
‘ ラベルの幅を変化させてプログレスバーを表現
.lblBar.Width = (.Frame1.Width – 4) percent
.Caption = “ビルド中… ” & Format(percent 0, “0”) & “% (” & current & “/” & total & “)”
End With

‘ フォーム自体の再描画を強制
frm.Repaint
End Sub

—

コードのここがポイント!プロの知見

1. `Show vbModeless` による非モーダル表示
通常 `frm.Show` と書くと、フォームを閉じるまでコードが停止します(モーダル表示)。しかし、`vbModeless` を指定すると、フォームを表示したまま次の行のコード(SolidWorksの処理)を実行し続けることができます。これが大前提です。

2. `PeekMessageW` APIによるメッセージポンプの乗っ取り
標準の `DoEvents` はたまにイベントを取りこぼしますが、Windows APIの `PeekMessageW` を自前でループさせることで、ユーザーが「キャンセル」ボタンを押した瞬間のクリックイベントを一瞬の遅延もなくキャッチできるようになります。

3. SolidWorksオブジェクトのライフサイクル管理
重い処理の最中にユーザーがキャンセルした際、中途半端に開かれたドキュメントやメモリリークを残さないよう、`g_IsCancelled` フラグでループを安全に抜け、クリーンアップ(`Unload`)を行う設計が極めて重要です。

—

陥りやすいエラーと対策

  • エラー: 「プロシージャの呼び出しまたは引数が不正です」
  • 原因: APIの宣言が 64bit 版のExcel/SolidWorks環境(VBA7)に対応していない可能性があります。必ず `PtrSafe` と `LongPtr` を適切に使い分けてください(上記のコードはすでにVBA7対応済みです)。
  • 現象: キャンセルボタンを押してもすぐ反応しない
  • 原因: ループ内の処理があまりにも重く(例:1つの部品の合致に10秒かかるなど)、`DoEventsAdvanced` が呼ばれる間隔が空きすぎています。処理の粒度を細かくするか、重い処理の直前にもメッセージポンプを挟むようにしてください。

—

まとめ

いかがでしたでしょうか?
今回は、単にSolidWorksのAPIを叩くだけではなく、WindowsのOSレベルの仕組み(メッセージポンプ)にまで踏み込んだ、上級プログラミングの世界をご紹介しました。

ここをクリアすれば、ユーザーから「このマクロ、動いているのか固まっているのか分からない!」と言われるクレームとはおさらばです。洗練されたUIを持つ、プロフェッショナルな自動化ツールを自信を持って構築できるようになります。

あなたのSolidWorks自動化ライフが、より快適で知的スリリングなものになりますように。
それでは、次の極限の知見でお会いしましょう!

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