修正了mcp的主线程调用问题
This commit is contained in:
gjm committed 2026-09-29 22:33:28 +08:00
1 parent 1fdcdefef1
commit 09f9d69d36
2 files changed
+64 -14

No files matched your search

+1 -1
View File
@@ -50,7 +50,7 @@ extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
void CR_SCGXCmdFnc(); void CR_SCGXCmdFnc();
void InitApplication() void InitApplication()
{ {
mcp::close_mcp(); mcp::init_mcp();
acrxBuildClassHierarchy(); acrxBuildClassHierarchy();
AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc); AddCommand(_T("SHCRACKTOOLS"), _T("CR_SCGX"), _T("CR_SCGX"), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, CR_SCGXCmdFnc);
+63 -13
View File
@@ -8,20 +8,24 @@
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
#define WM_MCP_EXECUTE_TASK (WM_USER + 2048)
static std::thread g_serverThread; static std::thread g_serverThread;
static std::atomic<bool> g_bRunning(false); static std::atomic<bool> g_bRunning(false);
static SOCKET g_listenSocket = INVALID_SOCKET; static SOCKET g_listenSocket = INVALID_SOCKET;
static HWND g_hMsgWnd = NULL;
static const wchar_t* g_szMsgClassName = L"AutoAgentMcpMsgWndClass";
// 用于跨线程传递数据的结构体 // 用于跨线程传递数据的结构体
struct TaskData struct TaskData
{ {
std::string rawJson; std::string rawJson;
}; };
// 真正执行绘图的回调函数 (此函数由 AutoCAD 主线程调度运行) // 此函数在 AutoCAD 主 UI 线程运行,绝对安全.
static void MainThreadCallback(void *pData) void ExecuteTaskInMainThread(TaskData *pTask)
{ {
TaskData *pTask = static_cast<TaskData *>(pData);
if (pTask == nullptr) if (pTask == nullptr)
{ {
return; return;
@@ -29,7 +33,6 @@ static void MainThreadCallback(void *pData)
try try
{ {
// 1. 解析 JSON 指令
auto j = nlohmann::json::parse(pTask->rawJson); auto j = nlohmann::json::parse(pTask->rawJson);
if (j.contains("cmd") && j["cmd"] == "draw_circle") if (j.contains("cmd") && j["cmd"] == "draw_circle")
{ {
@@ -37,13 +40,13 @@ static void MainThreadCallback(void *pData)
double y = j.value("y", 0.0); double y = j.value("y", 0.0);
double r = j.value("r", 100.0); double r = j.value("r", 100.0);
// 2. 锁定当前活动文档 (跨线程操作后的绝对必要步骤)
AcApDocument *pDoc = acDocManager->curDocument(); AcApDocument *pDoc = acDocManager->curDocument();
if (pDoc) if (pDoc)
{ {
acDocManager->lockDocument(pDoc, AcAp::kWrite, nullptr, nullptr, false); // 此时在主线程,安全锁文档
Acad::ErrorStatus es = acDocManager->lockDocument(pDoc, AcAp::kWrite, nullptr, nullptr, false);
// 3. 常规 ObjectARX 绘图逻辑 if (es == Acad::eOk)
{
AcDbDatabase *pDb = pDoc->database(); AcDbDatabase *pDb = pDoc->database();
AcDbBlockTable *pBlockTable = nullptr; AcDbBlockTable *pBlockTable = nullptr;
pDb->getBlockTable(pBlockTable, AcDb::kForRead); pDb->getBlockTable(pBlockTable, AcDb::kForRead);
@@ -67,13 +70,27 @@ static void MainThreadCallback(void *pData)
} }
} }
} }
}
catch (...) 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<TaskData *>(lParam);
if (pTask)
{
ExecuteTaskInMainThread(pTask);
delete pTask; // 执行完毕,清理内存
}
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
} }
// TCP 监听后台线程 // TCP 监听后台线程
@@ -114,8 +131,15 @@ static void TcpServerLoop()
TaskData *pTask = new TaskData; TaskData *pTask = new TaskData;
pTask->rawJson = recvBuf; pTask->rawJson = recvBuf;
// 核心:委托 AutoCAD 主线程在下一个消息循环中执行回调 // 关键转变:通过 PostMessage 发送给主线程拥有的隐式窗口
acDocManager->executeInApplicationContext(MainThreadCallback, pTask); if (g_hMsgWnd != NULL)
{
PostMessage(g_hMsgWnd, WM_MCP_EXECUTE_TASK, 0, reinterpret_cast<LPARAM>(pTask));
}
else
{
delete pTask;
}
} }
closesocket(clientSocket); closesocket(clientSocket);
} }
@@ -130,6 +154,24 @@ namespace mcp
{ {
bool init_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; g_bRunning = true;
try try
{ {
@@ -153,6 +195,14 @@ namespace mcp
{ {
g_serverThread.join(); g_serverThread.join();
} }
if (g_hMsgWnd != NULL)
{
DestroyWindow(g_hMsgWnd);
UnregisterClass(g_szMsgClassName, GetModuleHandle(NULL));
g_hMsgWnd = NULL;
}
acutPrintf(_T("\n[MCP Server] 插件已卸载,线程已关闭。\n")); acutPrintf(_T("\n[MCP Server] 插件已卸载,线程已关闭。\n"));
return true; return true;
} }