diff --git a/src/AutoAgent/AutoAgent.cpp b/src/AutoAgent/AutoAgent.cpp index a284ba7..abae7bc 100644 --- a/src/AutoAgent/AutoAgent.cpp +++ b/src/AutoAgent/AutoAgent.cpp @@ -50,7 +50,7 @@ extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt) void CR_SCGXCmdFnc(); void InitApplication() { - mcp::close_mcp(); + mcp::init_mcp(); acrxBuildClassHierarchy(); AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc); diff --git a/src/AutoAgent/mcp.cpp b/src/AutoAgent/mcp.cpp index 4612b6f..a23829b 100644 --- a/src/AutoAgent/mcp.cpp +++ b/src/AutoAgent/mcp.cpp @@ -8,20 +8,24 @@ #pragma comment(lib, "ws2_32.lib") +#define WM_MCP_EXECUTE_TASK (WM_USER + 2048) + static std::thread g_serverThread; static std::atomic g_bRunning(false); static SOCKET g_listenSocket = INVALID_SOCKET; +static HWND g_hMsgWnd = NULL; +static const wchar_t* g_szMsgClassName = L"AutoAgentMcpMsgWndClass"; + // 用于跨线程传递数据的结构体 struct TaskData { std::string rawJson; }; -// 真正执行绘图的回调函数 (此函数由 AutoCAD 主线程调度运行) -static void MainThreadCallback(void *pData) +// 此函数在 AutoCAD 主 UI 线程运行,绝对安全. +void ExecuteTaskInMainThread(TaskData *pTask) { - TaskData *pTask = static_cast(pData); if (pTask == nullptr) { return; @@ -29,7 +33,6 @@ static void MainThreadCallback(void *pData) try { - // 1. 解析 JSON 指令 auto j = nlohmann::json::parse(pTask->rawJson); if (j.contains("cmd") && j["cmd"] == "draw_circle") { @@ -37,43 +40,57 @@ static void MainThreadCallback(void *pData) double y = j.value("y", 0.0); double r = j.value("r", 100.0); - // 2. 锁定当前活动文档 (跨线程操作后的绝对必要步骤) AcApDocument *pDoc = acDocManager->curDocument(); if (pDoc) { - acDocManager->lockDocument(pDoc, AcAp::kWrite, nullptr, nullptr, false); + // 此时在主线程,安全锁文档 + Acad::ErrorStatus es = acDocManager->lockDocument(pDoc, AcAp::kWrite, nullptr, nullptr, false); + if (es == Acad::eOk) + { + AcDbDatabase *pDb = pDoc->database(); + AcDbBlockTable *pBlockTable = nullptr; + pDb->getBlockTable(pBlockTable, AcDb::kForRead); - // 3. 常规 ObjectARX 绘图逻辑 - AcDbDatabase *pDb = pDoc->database(); - AcDbBlockTable *pBlockTable = nullptr; - pDb->getBlockTable(pBlockTable, AcDb::kForRead); + AcDbBlockTableRecord *pBTR = nullptr; + pBlockTable->getAt(ACDB_MODEL_SPACE, pBTR, AcDb::kForWrite); + pBlockTable->close(); - AcDbBlockTableRecord *pBTR = nullptr; - pBlockTable->getAt(ACDB_MODEL_SPACE, pBTR, AcDb::kForWrite); - pBlockTable->close(); + AcGePoint3d center(x, y, 0.0); + AcGeVector3d normal(0.0, 0.0, 1.0); + AcDbCircle *pCircle = new AcDbCircle(center, normal, r); - AcGePoint3d center(x, y, 0.0); - AcGeVector3d normal(0.0, 0.0, 1.0); - AcDbCircle *pCircle = new AcDbCircle(center, normal, r); + AcDbObjectId circleId; + pBTR->appendAcDbEntity(circleId, pCircle); - AcDbObjectId circleId; - pBTR->appendAcDbEntity(circleId, pCircle); + pBTR->close(); + pCircle->close(); - pBTR->close(); - pCircle->close(); - - acDocManager->unlockDocument(pDoc); - acutPrintf(_T("\n[MCP Server] 成功在坐标(%.2f, %.2f)绘制了半径为%.2f的圆\n"), x, y, r); + acDocManager->unlockDocument(pDoc); + acutPrintf(_T("\n[MCP Server] 成功在坐标(%.2f, %.2f)绘制了半径为%.2f的圆\n"), x, y, r); + } } } } catch (...) { - acutPrintf(_T("\n[MCP Server] JSON 解析或执行异常!\n")); + acutPrintf(_T("\n[MCP Server] JSON 解析或绘图异常!\n")); } +} - // 4. 清理由 Socket 线程在堆上分配的内存 - delete pTask; +// 隐式窗口的回调函数 (属于主线程) +LRESULT CALLBACK McpWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_MCP_EXECUTE_TASK) + { + TaskData *pTask = reinterpret_cast(lParam); + if (pTask) + { + ExecuteTaskInMainThread(pTask); + delete pTask; // 执行完毕,清理内存 + } + return 0; + } + return DefWindowProc(hWnd, message, wParam, lParam); } // TCP 监听后台线程 @@ -114,8 +131,15 @@ static void TcpServerLoop() TaskData *pTask = new TaskData; pTask->rawJson = recvBuf; - // 核心:委托 AutoCAD 主线程在下一个消息循环中执行回调 - acDocManager->executeInApplicationContext(MainThreadCallback, pTask); + // 关键转变:通过 PostMessage 发送给主线程拥有的隐式窗口 + if (g_hMsgWnd != NULL) + { + PostMessage(g_hMsgWnd, WM_MCP_EXECUTE_TASK, 0, reinterpret_cast(pTask)); + } + else + { + delete pTask; + } } closesocket(clientSocket); } @@ -130,6 +154,24 @@ namespace mcp { bool init_mcp() { + // 1. 在主线程注册并创建 Message-Only 窗口 + WNDCLASSEX wc = { 0 }; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = McpWndProc; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = g_szMsgClassName; + RegisterClassEx(&wc); + + g_hMsgWnd = CreateWindowEx( + 0, + g_szMsgClassName, + L"AutoAgentMcpMsgWindow", + 0, 0, 0, 0, 0, + HWND_MESSAGE, // 纯消息窗口,无界面、无感知 + NULL, + GetModuleHandle(NULL), + NULL + ); g_bRunning = true; try { @@ -153,6 +195,14 @@ namespace mcp { g_serverThread.join(); } + + if (g_hMsgWnd != NULL) + { + DestroyWindow(g_hMsgWnd); + UnregisterClass(g_szMsgClassName, GetModuleHandle(NULL)); + g_hMsgWnd = NULL; + } + acutPrintf(_T("\n[MCP Server] 插件已卸载,线程已关闭。\n")); return true; }