fix
修正了mcp的主线程调用问题
This commit is contained in:
1 parent
1fdcdefef1
commit
09f9d69d36
2 files changed
+79
-29
No files matched your search
@@ -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);
|
||||
|
||||
+78
-28
@@ -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<bool> 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<TaskData *>(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<TaskData *>(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<LPARAM>(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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user