From 2fa27b1161b8dd964635a3ec42b2959d6d8e9ad3 Mon Sep 17 00:00:00 2001 From: LittleGuo Date: Fri, 9 Oct 2026 23:42:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(mcp=5Fframe):=20c++=5Fdll=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=A0=A1=E9=AA=8C=E9=BB=98=E8=AE=A4=E5=8F=AA=E6=AF=94?= =?UTF-8?q?=E5=A4=A7=E7=89=88=E6=9C=AC=EF=BC=8CR24.3=20=E5=8D=95=E7=8B=AC?= =?UTF-8?q?=E5=88=86=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - acrxGetApiVersion 返回值编码为 (major<<16)|minor,据此拆分大/小版本 (实测 R22.0=0x160000、R23.0=0x170000)。 - 同一大版本内二进制兼容(R24.0/R24.1/R24.2 可共用 ARX;2019/R23.0 编的 ARX 可跑在 2020/R23.1 上),故默认只比大版本,避免为每个小版本各编一份。 - 唯一例外 R24.3:换了 MSVC toolset,与 R24.0~R24.2 不兼容, 对 major==24 再按 minor>=3 分组判定。 - 同步更新 CODEBUDDY.md 与 ai.memory 说明。 --- CODEBUDDY.md | 2 +- ai.memory/project_mcp_frame_dll_loader.md | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CODEBUDDY.md b/CODEBUDDY.md index 108aa4a..8316754 100644 --- a/CODEBUDDY.md +++ b/CODEBUDDY.md @@ -61,7 +61,7 @@ python agent_loop.py # 完整 Agent 循环(多回合 截图/缩放/结 - **`lisp_inline`**:把 `script_template` 中的 `{占位符}` 替换为调用参数,`sendStringToExecute` 到命令行(异步)。布尔值转 `T`/`nil`。见 `mcp_tool.cpp:43`。 - **`lisp_file`**:先 `(load "文件" nil)` 再执行 `call_template`,`file_name` 相对于配置中的 `lisp_directory` 解析。见 `mcp_tool.cpp:61`。 -- **`c++_dll`**:`LoadLibrary` 第三方 DLL。**同一 DLL 全进程只加载一次**(句柄由 `mcp_configParser.cpp` 的模块缓存 `g_loadedDllModules` 持有,`mcp_Tools::clear()` 时统一 `FreeLibrary`),仅**首次加载**时校验 ARX 大版本:用**编译期常量** `(ARX<<16)|SUB_ARX` 与 DLL 导出的 `acrxGetApiVersion` 比对,不匹配则拒绝加载防崩溃。注意运行时的 `acrxGetApiVersion`(`rxapi.lib` 的 `acrxGetApiVersionImpl`)是**有状态实现**,返回值会随调用次数变化,不能用作基座侧版本。取 `factory_function` 与 `Destroy_` 创建/销毁工具,用 `mcp_Tool_DllProxy` 托管工具对象生命周期(RAII,**不再 FreeLibrary 模块**)。跨 DLL 通过 `SetConfigAbiSafe` / `ExecuteAbiSafe` 传 C 字符串,JSON 序列化全部在 DLL 内部完成。见 `mcp_configParser.cpp`、`mcp_tool.cpp`。 +- **`c++_dll`**:`LoadLibrary` 第三方 DLL。**同一 DLL 全进程只加载一次**(句柄由 `mcp_configParser.cpp` 的模块缓存 `g_loadedDllModules` 持有,`mcp_Tools::clear()` 时统一 `FreeLibrary`),仅**首次加载**时校验版本兼容性:`acrxGetApiVersion` 返回值编码为 `(major<<16)|minor`,**默认只比大版本**(同一大版本内二进制兼容,如 R24.0/R24.1/R24.2 可共用同一 ARX);**唯一例外 R24.3**(换了 MSVC toolset,与 R24.0~R24.2 不兼容),对 `major==24` 再按 `minor>=3` 分组。基座侧版本用**编译期常量** `ARX`/`SUB_ARX`。注意运行时的 `acrxGetApiVersion`(`rxapi.lib` 的 `acrxGetApiVersionImpl`)是**有状态实现**,返回值会随调用次数变化,不能用作基座侧版本。取 `factory_function` 与 `Destroy_` 创建/销毁工具,用 `mcp_Tool_DllProxy` 托管工具对象生命周期(RAII,**不再 FreeLibrary 模块**)。跨 DLL 通过 `SetConfigAbiSafe` / `ExecuteAbiSafe` 传 C 字符串,JSON 序列化全部在 DLL 内部完成。见 `mcp_configParser.cpp`、`mcp_tool.cpp`。 ### 插件 ABI(`inc/mcp_plugin_api.h`) diff --git a/ai.memory/project_mcp_frame_dll_loader.md b/ai.memory/project_mcp_frame_dll_loader.md index d230c50..a69bbe0 100644 --- a/ai.memory/project_mcp_frame_dll_loader.md +++ b/ai.memory/project_mcp_frame_dll_loader.md @@ -18,9 +18,19 @@ type: project - **后果**:AutoCAD 以 ARX 加载基座时**会先调用一次**该函数;此后基座再调用得到的是脏值, 而 plugins DLL(`LoadLibrary` 加载、无人调用过)首次返回正确值 → 两者不等 → 绝大多数 DLL 工具被判为「版本不匹配」而被 `FreeLibrary` 跳过(表现为只注册 9/26)。 -- **How to apply**:基座侧版本一律用**编译期常量** `(ARX<<16)|SUB_ARX`;**不要**把运行时 +- **How to apply**:基座侧版本一律用**编译期常量** `ARX`/`SUB_ARX`;**不要**把运行时 `acrxGetApiVersion()` 的返回值当版本用。实现见 `mcp_configParser.cpp::LoadDllTool`。 +### 返回值编码 + 「只比大版本」的兼容判定 + +- 返回值 = **`(major << 16) | minor`**(大版本=高 16 位,小版本=低 16 位)。实测(反汇编各版本 + SDK 的 `rxapi.lib`):R22.0 → `0x160000`(0x16=22)、R23.0 → `0x170000`(0x17=23); + 故 R23.1 → `0x170001`,R24.0 → `0x180000`。 +- 判定规则:**默认只比大版本**——同一大版本内二进制兼容(如 R24.0/R24.1/R24.2 可共用同一 + ARX;AutoCAD 2019=R23.0 编的 ARX 可跑在 2020=R23.1 上)。**唯一例外是 R24.3**:它换了 + MSVC toolset,与 R24.0~R24.2 **不兼容**,故对 `major==24` 再按 `minor>=3` 分一次组。 +- 若「大小版本全比」,会误拒用同大版本其它小版本 SDK 编的 DLL,逼着为每个小版本各编一份,浪费。 + ## 坑 2:DLL 工具写数据库必须先锁文档 - **事实**:工具执行链 `TcpServerLoop → PostMessage(WM_MCP_EXECUTE_TASK) → ExecuteTaskInMainThread` From 3b1cc88262f25769190752fb762dbc3b6a1bc708 Mon Sep 17 00:00:00 2001 From: LittleGuo Date: Fri, 9 Oct 2026 23:42:31 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(mcp=5Fframe):=20=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=A0=A1=E9=AA=8C=E6=BA=90=E7=A0=81=EF=BC=88?= =?UTF-8?q?=E4=B8=8A=E4=B8=80=E6=8F=90=E4=BA=A4=E5=9B=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=90=8D=E5=A4=A7=E5=B0=8F=E5=86=99=E6=BC=8F=E5=B8=A6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2fa27b1 只带上了文档,未纳入源文件。此处补上 mcp_ConfigParser.cpp 的实际改动: - acrxGetApiVersion 返回值按 (major<<16)|minor 拆分大/小版本; - 默认只比大版本(同一大版本内二进制兼容); - 例外 R24.3 与 R24.0~R24.2 因 MSVC toolset 不同,按 minor>=3 分组判定。 --- src/cad_mcp_frame/mcp_ConfigParser.cpp | 32 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/cad_mcp_frame/mcp_ConfigParser.cpp b/src/cad_mcp_frame/mcp_ConfigParser.cpp index dcc111a..d95b9a3 100644 --- a/src/cad_mcp_frame/mcp_ConfigParser.cpp +++ b/src/cad_mcp_frame/mcp_ConfigParser.cpp @@ -158,13 +158,35 @@ void mcp_ConfigParser::LoadDllTool(const nlohmann::json &toolConfig, const std:: // 基座自身版本取「编译期常量」,它稳定可靠; // 不能依赖运行时的 acrxGetApiVersion()——该函数由 rxapi.lib 中的有状态实现提供, // 其返回值会随调用次数变化(首次为正确值,之后被进程内全局污染),无法用于比对。 - int arx_version = (ARX << 16) | SUB_ARX; - int dll_version = getVerFn(); - mcpLoadLog("arx_version=" + std::to_string(arx_version) + " dll_version=" + std::to_string(dll_version)); + // 返回值编码为 (major << 16) | minor:高 16 位=大版本,低 16 位=小版本。 + const int arx_version = (ARX << 16) | SUB_ARX; + const int arx_major = ARX; + const int arx_minor = SUB_ARX; - if (arx_version != dll_version) + const int dll_version = getVerFn(); + const int dll_major = (dll_version >> 16) & 0xFFFF; + const int dll_minor = dll_version & 0xFFFF; + + mcpLoadLog("arx_version=" + std::to_string(arx_version) + + " dll_version=" + std::to_string(dll_version) + + " (arx " + std::to_string(arx_major) + "." + std::to_string(arx_minor) + + " / dll " + std::to_string(dll_major) + "." + std::to_string(dll_minor) + ")"); + + // 兼容性判定: + // - 同一大版本内通常二进制兼容(如 R24.0/R24.1/R24.2 可共用同一 ARX),故默认只比大版本; + // - 唯一例外是 R24.3:它换了 MSVC toolset,与 R24.0~R24.2 不兼容,需单独分组比较。 + bool compatible = false; + if (dll_major == arx_major) { - // 平台大版本不匹配,强行加载必定闪崩,果断踢掉整个模块! + if (arx_major == 24) + compatible = ((arx_minor >= 3) == (dll_minor >= 3)); // 24.3+ 与 24.0~24.2 互不兼容 + else + compatible = true; + } + + if (!compatible) + { + // 平台不兼容,强行加载必定闪崩,果断踢掉整个模块! FreeLibrary(hMod); g_loadedDllModules.erase(key); AfxMessageBox(ver_errMsg);