Advanced acedSSGet() Filter Lists, Keyword Callbacks & acedCommandS() in C++ GRX (dev-cookbook-17)
Master synchronous command invocation via acedCommandS()/acedCmdS() and interactive resbuf selection filters with keyword callbacks in GstarCAD 2027 C++ GRX.
1. Why `acedCommandS()` and `acedCmdS()` Replaced Legacy `acedCommand()`
In modern 64-bit CAD kernels—including GstarCAD 2027 and AutoCAD 2015+—the legacy fiber-based `acedCommand()` and `acedCmd()` variadic functions were superseded by coroutine-safe synchronous wrappers: acedCommandS() and acedCmdS(). Unlike legacy implementations that could leave a partially executed command suspended on the stack, `acedCommandS()` executes an entire native command macro atomically from start to finish before returning control to your C++ caller.
Crucially, any database-resident entity created or modified via `acedCommandS()` must not be held open for write (`AcDb::kForWrite`) by your C++ code at the moment `acedCommandS()` is invoked; doing so will trigger an `Acad::eWasOpenForWrite` lock conflict when the native command attempts to acquire its own write transaction.
2. Relational `resbuf` Filter Trees & Interactive Keyword Callbacks in `acedSSGet()`
Enterprise C++ plugins rarely ask users to select arbitrary entities; instead, they construct relational DXF group code filter trees (`-4` boolean operators `"<AND"`, `"<OR"`, `"<NOT"`, relational bitmasks `"&="`) combined with interactive command-line keyword callbacks (`acedSSSetKwordCallbackPtr`). Below is a complete production example that filters for closed polylines on structural layers with area greater than zero while allowing the user to press [Settings/LayerFilter] mid-selection:
#include "grxHeaders.h"
#include "acedCmdNF.h"
// Callback invoked when user enters a registered keyword during acedSSGet(":K")
struct resbuf* ssCallbackKeywordHandler(const ACHAR* kword) {
if (_wcsicmp(kword, L"Tolerance") == 0) {
double newTol = 0.5;
if (acedGetReal(L"
Enter boundary gap tolerance (mm) <0.5>: ", &newTol) == RTNORM) {
acutPrintf(L"
[GRX] Tolerance updated to %.3f mm.", newTol);
}
}
return nullptr; // Return nullptr to resume selection loop without altering pickset
}
void GcSelectStructuralSlabs() {
// 1. Register interactive keyword callback for ":K" selection mode
acedSSSetKwordCallbackPtr(ssCallbackKeywordHandler);
acedInitGet(0, L"Tolerance LayerFilter");
// 2. Build relational resbuf filter: Closed LWPOLYLINE on S-SLAB* layers
struct resbuf* pFilter = acutBuildList(
RTDXF0, L"LWPOLYLINE",
-4, L"&=",
70, 1, // Bit 1 set = Closed polyline
8, L"S-SLAB*", // Wildcard layer match
RTNONE
);
ads_name ssPick;
const ACHAR* prompts[2] = {
L"
Select structural slab contours or [Tolerance/LayerFilter]: ",
L"
Remove slab contours from calculation: "
};
int rc = acedSSGet(L"_:K:$", prompts, nullptr, pFilter, ssPick);
acutRelRb(pFilter); // Always free resbuf chain to prevent heap leaks
if (rc == RTNORM) {
Adesk::Int32 len = 0;
acedSSLength(ssPick, &len);
acutPrintf(L"
Validated %d closed slab boundary polylines.", len);
acedSSFree(ssPick);
}
}
3. Selection Set Handle Persistence & Pickset Limits (`MAXACTVP` / Selection Slots)
Every call to `acedSSGet()` that returns `RTNORM` allocates one of the CAD kernel's active `ads_name` selection set slots (capped at 128 concurrent active selection sets per document session). Failing to call acedSSFree(ssPick) on every code branch—including early error returns and user `ESC` cancellations where partial picksets were initialized—will eventually exhaust the slot table and cause `acedSSGet()` to return `RTERROR`.
- RAII Wrapper Pattern: Always wrap `ads_name` selection sets inside a lightweight C++ RAII struct (`ScopedSelectionSet`) whose destructor invokes `acedSSFree()` automatically upon scope exit.
- Converting `ads_name` to `AcDbObjectId`: Call `acdbGetObjectId(objId, entName)` immediately inside your iteration loop and store `AcDbObjectIdArray` if the selection needs to persist across multiple commands or modeless palette clicks.
- Synchronous `acedCommandS()` Invocation Flag: Register commands that call `acedCommandS()` with the
ACRX_CMD_MODALflag (orACRX_CMD_SESSIONwhen switching active documents) and terminate your argument list withRTNONE.
4. Combining `AcEdSSGetFilter` Reactor Hooks with Pickfirst Sets
For workflows where you must intercept or veto entities *even when the user runs native GstarCAD commands* like `ERASE`, `MOVE`, or `COPY`, attach an `AcEdSSGetFilter` reactor via `addSSgetFilterInputContextReactor(curDoc(), pMyFilter)`. Overriding `endSSGet(Acad::PromptStatus returnStatus, const AcEdSSGetFlags& flags, AcEdSelectionSetService& service, const AcDbObjectIdArray& selectionSet)` allows your C++ plugin to inspect the final pickset and call `service.remove(index)` on any locked company title block or protected datum grid line before the native command executes.
When invoking `acedCommandS()` with pickfirst selection sets, pass an `ads_name` selection set token via `RTPICKS` inside `acedCmdS( acutBuildList(RTSTR, L"_.CHPROP", RTPICKS, ssPick, RTSTR, L"", RTSTR, L"_Color", RTSHORT, 1, RTSTR, L"", RTNONE) )`—avoiding slow entity-by-entity command loops.
Validate This Workflow in GstarCAD 2027
Engineered by Suzhou Gstarsoft Co., Ltd. (SSE: 688657), GstarCAD 2027 delivers 100% native AC1032 DWG binary fidelity, perpetual licensing sovereignty, and zero-day AutoCAD command parity.