C++ GRX SDK · Selection & Command Engine

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_MODAL flag (or ACRX_CMD_SESSION when switching active documents) and terminate your argument list with RTNONE.

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.

Download Free 30-Day Evaluation → Explore AutoCAD Migration Hub Take Skill Certification Quiz