You set up a WithEvents handler in a SolidWorks VBA macro. The macro runs, SolidWorks does something (opens a document, rebuilds, saves), and your handler fires correctly. Then you close the macro editor and try to use SolidWorks normally — the handler no longer fires. The macro is gone, and so is your event subscription.
This confuses VBA developers who are used to Office automation, where event handlers in a document’s VBA project persist as long as the document is open. SolidWorks works differently. Here’s why, and how to keep handlers alive when you need them.
Why the Handler Dies
A SolidWorks VBA macro is a standard executable module. When the entry point (Sub Main) finishes, all variables declared inside it go out of scope and are garbage collected. Any WithEvents object that was created inside the macro is destroyed. The COM event connection goes with it.
The crucial point: VBA macros are not persistent processes. They run to completion and stop. There is no background thread keeping your class instance alive. Unlike an Excel workbook’s VBA, which persists as long as the workbook is open, a SolidWorks macro has no “host object” to live inside once execution ends.
The SolidWorks API event interfaces were designed for COM add-ins — DLLs that are loaded when SolidWorks starts and unloaded when it closes. Events fire into the add-in for the entire SolidWorks session. In a macro, they can only fire during the window when the macro is executing.
The WithEvents Mechanism
SolidWorks COM interfaces expose events through standard COM connection points. To subscribe in VBA:
- Create a class module that declares the event source with
WithEvents - Implement the event methods in that class
- Instantiate the class and keep the instance alive
Class module: AppEventHandler
Option Explicit
Public WithEvents swApp As SldWorks.SldWorks
Private Sub swApp_ActiveDocChangeNotify()
Debug.Print "Active document changed: " & swApp.ActiveDoc.GetTitle
End Sub
Private Sub swApp_DocumentLoadNotify2(ByVal NewDoc As String, ByVal DocType As Long)
Debug.Print "Document loaded: " & NewDoc
End Sub
Private Sub swApp_DocumentCloseNotify(ByVal DocTitle As String)
Debug.Print "Document closed: " & DocTitle
End Sub
Standard module: Main
Option Explicit
Dim handler As AppEventHandler ' Module-level — not local to a Sub
Sub StartEventMonitor()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
Set handler = New AppEventHandler
Set handler.swApp = swApp ' Subscribe to events
Debug.Print "Event monitoring started. Run StopEventMonitor to stop."
' Keep the macro alive — events only fire while execution is running
Do While Not handler Is Nothing
DoEvents
Loop
End Sub
Sub StopEventMonitor()
Set handler = Nothing ' Destroys the handler, loop exits
End Sub
The Do While ... DoEvents loop is not a busy-wait. DoEvents yields control to the Windows message queue, which is where SolidWorks processes its own events. During the yield, SolidWorks can receive user input, trigger its internal events, and call your handler. The loop itself consumes negligible CPU.
To stop the monitor, run StopEventMonitor from a second macro call, a keyboard shortcut, or a menu entry. Setting handler = Nothing destroys the class instance, which drops the COM connection, which causes the loop condition to fail.
Module-Level Variable: the Critical Detail
The Dim handler As AppEventHandler declaration at module level (outside any Sub) is what keeps the instance alive between the start of the loop and its end. A local variable declared inside StartEventMonitor would be destroyed when the Sub’s stack frame is destroyed — which never happens in this pattern since the Sub never returns. In practice, module-level and local-inside-loop behave the same here, but declaring at module level is the correct practice because it:
- Makes
handleraccessible toStopEventMonitor - Survives any future refactoring that might extract inner code into another Sub
- Matches the pattern documentation uses (CodeStack, SolidWorks Developer Portal)
Document-Level Events
Application-level events fire regardless of which document is active. For events scoped to a specific document, subscribe to IModelDoc2:
Class module: DocEventHandler
Option Explicit
Public WithEvents swDoc As SldWorks.ModelDoc2
Public IsListening As Boolean
Private Sub swDoc_FileSaveNotify(ByVal FileName As String)
Debug.Print "Saved: " & FileName
End Sub
Private Sub swDoc_RegenNotify()
Debug.Print "Rebuild completed: " & swDoc.GetTitle
End Sub
Private Sub swDoc_ModifyNotify()
Debug.Print "Document modified"
End Sub
Standard module usage:
Dim docHandler As DocEventHandler
Sub MonitorActiveDocument()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
If swApp.ActiveDoc Is Nothing Then
MsgBox "No active document."
Exit Sub
End If
Set docHandler = New DocEventHandler
Set docHandler.swDoc = swApp.ActiveDoc
docHandler.IsListening = True
Do While docHandler.IsListening
DoEvents
Loop
Set docHandler = Nothing
End Sub
Sub StopDocMonitor()
If Not docHandler Is Nothing Then
docHandler.IsListening = False
End If
End Sub
Here IsListening is a flag property on the handler class. The loop reads it. StopDocMonitor sets it false, the loop exits naturally.
Commonly Used Events
Application-level (subscribe via ISldWorks):
| Event | Fires When |
|---|---|
ActiveDocChangeNotify | User switches active document |
DocumentLoadNotify2(NewDoc, DocType) | A document finishes opening |
DocumentCloseNotify(DocTitle) | A document is closed |
FileNewNotify2(NewDoc) | A new document is created |
CommandCloseNotify(Command, reason) | A SolidWorks command closes |
Document-level (subscribe via IModelDoc2):
| Event | Fires When |
|---|---|
FileSaveNotify(FileName) | Document is saved |
ModifyNotify | Document is modified (nearly anything) |
RegenNotify | Rebuild completes |
DestroyNotify2(DestroyType) | Document is closed/destroyed |
AddItemNotify(EntityType, itemName) | A feature, component, or annotation is added |
DeleteItemNotify(EntityType, itemName) | An item is deleted |
Drawing-specific (subscribe via IDrawingDoc):
| Event | Fires When |
|---|---|
DrawingViewCreateNotify(View) | A new drawing view is added |
DrawingViewActivatedNotify(View) | A drawing view receives focus |
Assembly-specific (subscribe via IAssemblyDoc):
| Event | Fires When |
|---|---|
ComponentConfigurationChangeNotify2(...) | A component’s configuration changes |
AddComponentNotify(componentName) | A component is added to the assembly |
Gotchas
Events fire on the main thread. VBA is single-threaded. Your event handler runs synchronously on the same thread as the DoEvents loop. If the handler takes a long time (e.g., opening Excel, running a complex traversal), SolidWorks will appear to freeze until it returns. Keep handlers fast — queue work, don’t do it inline.
Multiple documents need multiple handler instances. IModelDoc2 events are per-document. If you open three parts and want FileSaveNotify on all three, you need three DocEventHandler instances, each subscribed to a different document. Application-level events through ISldWorks are the simpler path if you need cross-document monitoring.
ModifyNotify fires extremely frequently. A single rebuild can trigger it dozens of times. Filter by checking the specific property you care about inside the handler, not by trying to prevent the event from firing.
Error handling in handlers is mandatory. An unhandled error inside an event handler terminates the DoEvents loop silently. The macro appears to stop working with no error message. Wrap all handler bodies in On Error GoTo blocks and log failures.
SolidWorks version matters. The set of available events has expanded across versions. Events introduced after SW2018 may not compile in projects targeted at older installations. The SolidWorks API help (accessed via F1 in the IDE) lists the Since version annotation on each event.
When to Switch to a COM Add-In
The DoEvents pattern is appropriate for:
- Development and debugging
- One-off batch automation run by a single developer
- Low-frequency events (document open/close) where timing doesn’t matter
It breaks down for:
- Multi-user environments — each user would need to run the macro and keep it open
- Events that survive macro restarts — there’s no persistent state; if SolidWorks closes the macro, subscriptions are lost
- High-frequency events with complex responses — the single-threaded VBA DoEvents loop can’t keep up
- Distributed automation in production — maintenance and versioning of macro files are unmanageable at scale
The transition from VBA macro to COM add-in for event-driven work is covered in when to upgrade from a SolidWorks macro to a full COM add-in, which covers the specific capabilities that are unavailable in macros: Property Manager Pages, persistent state, and multi-document event dispatch.
The other common pattern to know before committing to VBA events: SolidWorks exposes a ISwAddinBase interface specifically for add-ins, and the in-process architecture makes API calls significantly faster. The in-process vs standalone SolidWorks API guide covers this tradeoff if the performance difference matters for your use case.
Summary
SolidWorks VBA event handlers require the macro to be actively executing. The standard pattern — WithEvents class + module-level instance + DoEvents loop — keeps handlers alive for the macro’s lifetime. Events fire correctly during the loop, and a second macro call stops it cleanly.
For anything that needs to survive the macro session or run in multi-user environments, a COM add-in is the right architecture. The VBA pattern above is the correct bridge for development, scripting, and situations where a macro’s lifetime is long enough to be useful.