app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
Drew Owens
0.9.1 (a62fe72): the data root moves out of ab's directory to %LOCALAPPDATA%\adom-solidworks-bridge; legacy files that…
192302d
20d ago
Writes, dialogs and targeting — swx
Read this before anything that writes — saving, exporting over a file, setting properties or parameters, running a script or macro, closing someone's document — and before touching any dialog or window. Part of the solidworks-bridge skill — SKILL.md beside this folder has the rules that never bend. Trust each verb's own reply (_hint, errorCode) over anything written here.
Rules and gotchas
- Target docs by
path, not by activating them — every doc verb takes an optionalpathand falls back to the active document. - Files land on the DESKTOP filesystem. From a cloud container, pull
exports back with ab's
pull_file. - Background etiquette: the bridge never foregrounds SOLIDWORKS; responses
carry
windowEtiquette:"background". - A modal dialog inside SOLIDWORKS blocks every verb (COM is single
threaded). If verbs time out, ask the user about a dialog, or check
swx_status. The bridge holds the COM object in a separate child process, so a wedged call is killed and replaced rather than taking the bridge down — the verb after a timeout works again. swx_props_setchanges memory, not the file — follow withswx_save.swx_com_eval {"expr":"...","reason":"..."}is the maintainer escape hatch: a late-bound COM call chain rooted at$sw(the application) or$doc(the active document), e.g.$sw.RevisionNumber()or$doc.Extension.CustomPropertyManager("").GetNames(). String, number and boolean literal arguments. For prototyping a capability against a real install before it becomes a verb;reasonis required.swx_screenshotdelegates to ab's genericdesktop_screenshot_window;swx_state {}bundles that with the dialog census and this machine's measured verb timings in one call (screenshot:falseto skip the callbacks).- Do not dismiss dialogs casually.
swx_dismiss_blocking_dialogs {}REPORTS by default. Closing needsclose:true+reason+confirm; a "Save changes?" / Document Recovery / licence dialog is never closed at all; and a window that cannot be PROVED to belong to SOLIDWORKS is never closed either (ab's window list carries no owner pid, so a bare dialog class could be any application's).swx_close_windowrefuses the same way, witherrorCode:"window_not_attributed". Closing a stuck SOLIDWORKS modal through the window manager has been observed to take the whole application down — and on Connected editions the bridge cannot start it again. - ☠ THE BIG ONE: writes are DENY-BY-DEFAULT. The bridge refuses to change
any document it did not create, and to write over any file that already
exists. That includes things that do not look like writes:
swx_props_set(it dirties the document, so the user's own Ctrl+S would commit it),swx_rebuild_check(it callsForceRebuild3),swx_bom {format:"csv"}(it writes a file),swx_pack_and_go, andswx_stop/swx_close_all_documents/swx_close_doc(they end or disturb the user's session). Checkswx_status.userWrites("disabled"/"enabled") before planning anything that writes. Refusals areerrorCode:"user_writes_disabled"or"destination_exists"; the response listsbridgeDocuments(each with thepaththat names it unambiguously) so you can retarget. Exporting to a path that does not exist yet, from any document, always works — that is the normal way to get data out. To reach the user's own document or overwrite a file, the bridge process must have been started withSWX_ALLOW_USER_WRITES=1AND the call must carryallowUserWrites:true+confirm:true+reason. You cannot grant this to yourself — ask the user. Every exception is recorded inswx_action_ledger. - The affected verbs:
swx_save,swx_save_as,swx_export*,swx_bom {format:"csv"},swx_pack_and_go,swx_close_doc,swx_props_set,swx_set_parameter,swx_set_preference,swx_run_script,swx_run_macro(always — see below),swx_rebuild_check,swx_stop,swx_kill,swx_close_all_documents,swx_open {readOnly:false}, and BOTH dialog-closing verbs —swx_dismiss_blocking_dialogs {close:true}andswx_close_window— for any dialog at all, including ones the bridge itself raised. Reading is never gated, and neither is exporting to a path that does not exist yet. swx_run_macrois gated ALWAYS, with no safe case. A macro is a VBA project with the whole SOLIDWORKS API in scope and the filesystem behind it: it can model, save over anything, batch-export, delete, or quit, to documents this bridge has never seen — and nothing about the call predicts which. So the two questions that bound every other write ("is the document ours", "is the destination clear") do not apply, and running one needs the full opt-in chain every time, even for a macro the bridge itself shipped. In the default posture it refuses.moduleandproceduredefault tomain/main(the macro recorder's defaults); the macro is unloaded after the run, so an edited.swpruns its new code.swx_run_macrohas not yet been run against a live SOLIDWORKS — its response says so (provenLive:false), and the bundledresources/macros/adom-hello.swpis a placeholder, not a working macro.- What the opt-in actually opens. When the user HAS opted in, in full, the
bridge does the thing — it does not refuse anyway.
swx_close_docandswx_close_all_documents {closeUnsaved:true}will close and discard the user's own unsaved documents on that chain, with each one named inswx_action_ledger. Both the server and the com host check the identical chain independently, so a partial chain is refused twice and a complete one is honoured by both. - A destination this bridge cannot CHECK is not free. Every write is gated on
whether something is already at the destination, and that question has three
answers: occupied (needs the opt-in), free (a bridge-created document may be
written there without one), and unverifiable — a path this process cannot
stat, which refuses with
destination_unverifiableand is NOT waivable by the opt-in. It is not a permission question. The bridge and SOLIDWORKS do not share a filesystem view, so a UNC path the bridge cannot see may hold a file SOLIDWORKS would overwrite. Check it yourself, or write somewhere the bridge can see. swx_close_windowis gated exactly likeswx_dismiss_blocking_dialogs. It refuses an application window (errorCode:"window_is_application_frame"— closing a SOLIDWORKS frame quits it, which isswx_stop's job); it refuses a dialog that asks about the user's own work at any posture (errorCode:"dialog_belongs_to_the_user"— Save changes?, Document Recovery, licence); and closing anything else needs the process-level write opt-in.swx_window_infomarks every window withisFrameandclosable, andclosablealready folds in all three refusals — trust it.swx_screenshotcaptures SOLIDWORKS' own window, by handle. A free-texttitleContainsis ignored; passhwndonly ifswx_window_infoattributes it to SOLIDWORKS. For anything else on the desktop, ab's owndesktop_screenshot_windowis the right tool and carries the user's approval prompt.swx_openis READ-ONLY by default, andreadOnly:falseis a WRITE. A read-write open takes a write lock on the user's file and converts-and-dirties an older-version document in memory, so it goes through the same gate as every other write. Reading is always free.swx_close_doc {save:false}on a DIRTY document the bridge did not create additionally needsconfirm:true+reason(errorCode:"unsaved_documents") — and if SOLIDWORKS will not say whether the document is dirty, it is treated as dirty. There is no undo. Do NOT reach forswx_close_doc {save:false}to "undo" a change to someone's document: that discards the entire document, not your edits. Undo belongs to the user, in SOLIDWORKS.- Destructive verbs ask first.
swx_close_all_documents,swx_close_window,swx_killandswx_close_doc-on-dirty-work needconfirm:true(andswx_close_window/swx_killareason).swx_stopandswx_killrefuse outright while documents are unsaved (errorCode:"unsaved_documents") — passforce:trueonly if the user has said the changes are disposable.swx_close_all_documents {closeUnsaved:true}additionally needs areason, and it discards ONLY the bridge's own scratch documents: the user's unsaved ones are left open and listed inskipped. swx_props_setIS gated now. The old justification — "in memory until a gated save" — ignored the persist route that cannot be gated: the user. It leaves their document dirty, so SOLIDWORKS prompts "Save changes?" on close and their own Ctrl+S writes bridge-authored data into their file.- Name the document with
targetDocument, not by hoping. Every mutating verb resolves its target ONCE —targetDocument(a title), elsepath, else the active document — and the bridge acts on exactly that. AtargetDocumentnaming nothing open is an error (document_not_open), never a silent fall back to whatever the user has in front of them; naming two different documents two ways isambiguous_target. PrefertargetDocumentwhenever it matters: SOLIDWORKS' active document follows window focus, and the user may click something else mid-run. swx_com_evalevaluates READS ONLY, by allowlist. Members beginningGet/Is/Has/Can/Enum, plus a short list of navigation properties (ActiveDoc,Extension,SketchManager,FeatureManager,Frame,Name,Value,Mass, …). Everything else — including members nobody has thought of — is refused witherrorCode:"eval_member_blocked"and a reason. It is an allowlist rather than a banned list because a banned list has to anticipate every dangerous member of an API with thousands, and the first one missedSendMsgToUser2(a modal that wedges the host),SetUserPreferenceToggleandForceRebuild3. If a genuine READ is refused, that is a bug worth filing.swx_save_asRENAMES by default. The open document becomes that file and takes its name — which is what you want for a scratch part, and emphatically not what you want on someone'sbracket.sldprt.copy:truewrites the file and leaves the document alone; that is what everyswx_export*does.swx_reset_workspaceis the safe cleanup: it closes only documents this bridge process created and has not saved. The list lives in memory, not on disk, because SOLIDWORKS names new documentsPart1/Assem1and a remembered title would eventually match a document the USER made. A bridge restart therefore forgets and the sweep closes nothing.
# Writes, dialogs and targeting — swx
Read this before anything that writes — saving, exporting over a file, setting properties or parameters, running a script or macro, closing someone's document — and before touching any dialog or window. Part of the `solidworks-bridge` skill — `SKILL.md` beside this folder has the rules that never bend. Trust each verb's own reply (`_hint`, `errorCode`) over anything written here.
## Rules and gotchas
- **Target docs by `path`**, not by activating them — every doc verb takes an
optional `path` and falls back to the active document.
- **Files land on the DESKTOP filesystem.** From a cloud container, pull
exports back with ab's `pull_file`.
- **Background etiquette:** the bridge never foregrounds SOLIDWORKS; responses
carry `windowEtiquette:"background"`.
- **A modal dialog inside SOLIDWORKS blocks every verb** (COM is single
threaded). If verbs time out, ask the user about a dialog, or check
`swx_status`. The bridge holds the COM object in a separate child process, so
a wedged call is killed and replaced rather than taking the bridge down — the
verb after a timeout works again.
- **`swx_props_set` changes memory, not the file** — follow with `swx_save`.
- `swx_com_eval {"expr":"...","reason":"..."}` is the maintainer escape hatch:
a late-bound COM call chain rooted at `$sw` (the application) or `$doc` (the
active document), e.g. `$sw.RevisionNumber()` or
`$doc.Extension.CustomPropertyManager("").GetNames()`. String, number and
boolean literal arguments. For prototyping a capability against a real
install before it becomes a verb; `reason` is required.
- `swx_screenshot` delegates to ab's generic `desktop_screenshot_window`;
`swx_state {}` bundles that with the dialog census and this machine's
measured verb timings in one call (`screenshot:false` to skip the callbacks).
- **Do not dismiss dialogs casually.** `swx_dismiss_blocking_dialogs {}`
REPORTS by default. Closing needs `close:true` + `reason` + `confirm`; a
"Save changes?" / Document Recovery / licence dialog is never closed at all;
and a window that cannot be PROVED to belong to SOLIDWORKS is never closed
either (ab's window list carries no owner pid, so a bare dialog class could be
any application's). `swx_close_window` refuses the same way, with
`errorCode:"window_not_attributed"`.
Closing a stuck SOLIDWORKS modal through the window manager has been observed
to take the whole application down — and on Connected editions the bridge
cannot start it again.
- **☠ THE BIG ONE: writes are DENY-BY-DEFAULT.** The bridge refuses to change
any document it did not create, and to write over any file that already
exists. That includes things that do not look like writes: `swx_props_set`
(it dirties the document, so the user's own Ctrl+S would commit it),
`swx_rebuild_check` (it calls `ForceRebuild3`), `swx_bom {format:"csv"}` (it
writes a file), `swx_pack_and_go`, and `swx_stop` /
`swx_close_all_documents` / `swx_close_doc` (they end or disturb the user's
session).
Check `swx_status.userWrites` (`"disabled"` / `"enabled"`) before planning
anything that writes. Refusals are `errorCode:"user_writes_disabled"` or
`"destination_exists"`; the response lists `bridgeDocuments` (each with the
`path` that names it unambiguously) so you can retarget. **Exporting to a path
that does not exist yet, from any document, always works** — that is the
normal way to get data out. To reach the user's own document or overwrite a
file, the bridge process must have been started with
`SWX_ALLOW_USER_WRITES=1` AND the call must carry `allowUserWrites:true` +
`confirm:true` + `reason`. **You cannot grant this to yourself — ask the
user.** Every exception is recorded in `swx_action_ledger`.
- **The affected verbs:** `swx_save`, `swx_save_as`, `swx_export*`,
`swx_bom {format:"csv"}`, `swx_pack_and_go`, `swx_close_doc`,
`swx_props_set`, `swx_set_parameter`, `swx_set_preference`,
`swx_run_script`, `swx_run_macro` (**always** — see below),
`swx_rebuild_check`, `swx_stop`, `swx_kill`,
`swx_close_all_documents`, `swx_open {readOnly:false}`, and BOTH dialog-closing
verbs — `swx_dismiss_blocking_dialogs {close:true}` and `swx_close_window` —
for any dialog at all, including ones the bridge itself raised. **Reading is never gated**, and
neither is exporting to a path that does not exist yet.
- **`swx_run_macro` is gated ALWAYS, with no safe case.** A macro is a VBA
project with the whole SOLIDWORKS API in scope and the filesystem behind it:
it can model, save over anything, batch-export, delete, or quit, to documents
this bridge has never seen — and nothing about the *call* predicts which.
So the two questions that bound every other write ("is the document ours",
"is the destination clear") do not apply, and running one needs the full
opt-in chain every time, even for a macro the bridge itself shipped. In the
default posture it refuses. `module` and `procedure` default to `main`/`main`
(the macro recorder's defaults); the macro is unloaded after the run, so an
edited `.swp` runs its new code. **`swx_run_macro` has not yet been run
against a live SOLIDWORKS** — its response says so (`provenLive:false`), and
the bundled `resources/macros/adom-hello.swp` is a placeholder, not a working
macro.
- **What the opt-in actually opens.** When the user HAS opted in, in full, the
bridge does the thing — it does not refuse anyway. `swx_close_doc` and
`swx_close_all_documents {closeUnsaved:true}` will close and discard the
user's own unsaved documents on that chain, with each one named in
`swx_action_ledger`. Both the server and the com host check the identical
chain independently, so a partial chain is refused twice and a complete one
is honoured by both.
- **A destination this bridge cannot CHECK is not free.** Every write is gated on
whether something is already at the destination, and that question has three
answers: occupied (needs the opt-in), free (a bridge-created document may be
written there without one), and **unverifiable** — a path this process cannot
stat, which refuses with `destination_unverifiable` and is NOT waivable by the
opt-in. It is not a permission question. The bridge and SOLIDWORKS do not share a
filesystem view, so a UNC path the bridge cannot see may hold a file SOLIDWORKS
would overwrite. Check it yourself, or write somewhere the bridge can see.
- **`swx_close_window` is gated exactly like `swx_dismiss_blocking_dialogs`.**
It refuses an application window (`errorCode:"window_is_application_frame"` —
closing a SOLIDWORKS frame quits it, which is `swx_stop`'s job); it refuses a
dialog that asks about the user's own work at any posture
(`errorCode:"dialog_belongs_to_the_user"` — Save changes?, Document Recovery,
licence); and closing anything else needs the process-level write opt-in.
`swx_window_info` marks every window with `isFrame` and `closable`, and
`closable` already folds in all three refusals — trust it.
- **`swx_screenshot` captures SOLIDWORKS' own window, by handle.** A free-text
`titleContains` is ignored; pass `hwnd` only if `swx_window_info` attributes
it to SOLIDWORKS. For anything else on the desktop, ab's own
`desktop_screenshot_window` is the right tool and carries the user's approval
prompt.
- **`swx_open` is READ-ONLY by default, and `readOnly:false` is a WRITE.** A
read-write open takes a write lock on the user's file and converts-and-dirties
an older-version document in memory, so it goes through the same gate as every
other write. Reading is always free. `swx_close_doc {save:false}` on a DIRTY document the bridge
did not create additionally needs `confirm:true` + `reason`
(`errorCode:"unsaved_documents"`) — and if SOLIDWORKS will not say whether the
document is dirty, it is treated as dirty.
**There is no undo.** Do NOT reach for `swx_close_doc {save:false}` to "undo"
a change to someone's document: that discards the entire document, not your
edits. Undo belongs to the user, in SOLIDWORKS.
- **Destructive verbs ask first.** `swx_close_all_documents`, `swx_close_window`,
`swx_kill` and `swx_close_doc`-on-dirty-work need `confirm:true` (and
`swx_close_window`/`swx_kill` a `reason`). `swx_stop` and `swx_kill` refuse
outright while documents are unsaved (`errorCode:"unsaved_documents"`) — pass
`force:true` only if the user has said the changes are disposable.
`swx_close_all_documents {closeUnsaved:true}` additionally needs a `reason`,
and it discards ONLY the bridge's own scratch documents: the user's unsaved
ones are left open and listed in `skipped`.
- **`swx_props_set` IS gated now.** The old justification — "in memory until a
gated save" — ignored the persist route that cannot be gated: the user. It
leaves their document dirty, so SOLIDWORKS prompts "Save changes?" on close
and their own Ctrl+S writes bridge-authored data into their file.
- **Name the document with `targetDocument`, not by hoping.** Every mutating
verb resolves its target ONCE — `targetDocument` (a title), else `path`, else
the active document — and the bridge acts on exactly that. A `targetDocument`
naming nothing open is an error (`document_not_open`), never a silent fall
back to whatever the user has in front of them; naming two different documents
two ways is `ambiguous_target`. Prefer `targetDocument` whenever it matters:
SOLIDWORKS' active document follows window focus, and the user may click
something else mid-run.
- **`swx_com_eval` evaluates READS ONLY, by allowlist.** Members beginning
`Get`/`Is`/`Has`/`Can`/`Enum`, plus a short list of navigation properties
(`ActiveDoc`, `Extension`, `SketchManager`, `FeatureManager`, `Frame`,
`Name`, `Value`, `Mass`, …). Everything else — including members nobody has
thought of — is refused with `errorCode:"eval_member_blocked"` and a reason.
It is an allowlist rather than a banned list because a banned list has to
anticipate every dangerous member of an API with thousands, and the first one
missed `SendMsgToUser2` (a modal that wedges the host), `SetUserPreferenceToggle`
and `ForceRebuild3`. If a genuine READ is refused, that is a bug worth filing.
- **`swx_save_as` RENAMES by default.** The open document becomes that file and
takes its name — which is what you want for a scratch part, and emphatically
not what you want on someone's `bracket.sldprt`. `copy:true` writes the file
and leaves the document alone; that is what every `swx_export*` does.
- **`swx_reset_workspace` is the safe cleanup**: it closes only documents this
bridge process created and has not saved. The list lives in memory, not on
disk, because SOLIDWORKS names new documents `Part1`/`Assem1` and a
remembered title would eventually match a document the USER made. A bridge
restart therefore forgets and the sweep closes nothing.