add
测试mcp基座
This commit is contained in:
1 parent
c0966ad334
commit
1afcc10a61
6 files changed
+489
-96
No files matched your search
@@ -3,74 +3,157 @@
|
||||
#include <Windows.h>
|
||||
#include "tchar.h"
|
||||
#include "rxregsvc.h"
|
||||
#include <gdiplus.h>
|
||||
#pragma comment(lib, "gdiplus.lib")
|
||||
|
||||
#pragma comment(lib, "rxapi.lib")
|
||||
#pragma comment(linker, "/export:acrxGetApiVersion,PRIVATE")
|
||||
|
||||
class mcp_Tool_DrawCircleCpp : public mcp_Tool
|
||||
namespace
|
||||
{
|
||||
// 辅助函数:Base64 编码
|
||||
std::string Base64Encode(const unsigned char *data, size_t length)
|
||||
{
|
||||
static const char encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
size_t out_len = 4 * ((length + 2) / 3);
|
||||
std::string ret(out_len, '\0');
|
||||
size_t i;
|
||||
char *p = const_cast<char *>(ret.c_str());
|
||||
|
||||
for (i = 0; i < length - 2; i += 3)
|
||||
{
|
||||
*p++ = encoding_table[(data[i] >> 2) & 0x3F];
|
||||
*p++ = encoding_table[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = encoding_table[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
|
||||
*p++ = encoding_table[data[i + 2] & 0x3F];
|
||||
}
|
||||
if (i < length)
|
||||
{
|
||||
*p++ = encoding_table[(data[i] >> 2) & 0x3F];
|
||||
if (i == (length - 1))
|
||||
{
|
||||
*p++ = encoding_table[((data[i] & 0x3) << 4)];
|
||||
*p++ = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = encoding_table[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = encoding_table[((data[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
*p++ = '=';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 辅助函数:获取 GDI+ 图像编码器的 Clsid (用于转 PNG)
|
||||
int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
|
||||
{
|
||||
UINT num = 0, size = 0;
|
||||
Gdiplus::GetImageEncodersSize(&num, &size);
|
||||
if (size == 0) return -1;
|
||||
Gdiplus::ImageCodecInfo *pImageCodecInfo = (Gdiplus::ImageCodecInfo *)(malloc(size));
|
||||
if (pImageCodecInfo == NULL) return -1;
|
||||
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
|
||||
for (UINT j = 0; j < num; ++j)
|
||||
{
|
||||
if (_tcscmp(pImageCodecInfo[j].MimeType, format) == 0)
|
||||
{
|
||||
*pClsid = pImageCodecInfo[j].Clsid;
|
||||
free(pImageCodecInfo);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
free(pImageCodecInfo);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
class mcp_Tool_ViewportScreenshot: public mcp_Tool
|
||||
{
|
||||
public:
|
||||
virtual nlohmann::json Execute(const nlohmann::json &args) override
|
||||
{
|
||||
// 1. 从大模型传入的参数中提取坐标和半径
|
||||
double x = args.value("cx", 0.0);
|
||||
double y = args.value("cy", 0.0);
|
||||
double r = args.value("r", 100.0);
|
||||
// 1. 获取 AutoCAD 当前文档的绘图区句柄
|
||||
HWND hWnd = adsw_acadDocWnd();
|
||||
if (!hWnd)
|
||||
return mcp_Tool_Utility::make_error(-32000, L"无法获取CAD绘图区窗口句柄");
|
||||
|
||||
// 2. 获取当前活动文档
|
||||
AcApDocument* pDoc = acDocManager->curDocument();
|
||||
if (pDoc == nullptr)
|
||||
// 2. 初始化 GDI+
|
||||
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
|
||||
ULONG_PTR gdiplusToken;
|
||||
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
|
||||
|
||||
std::string base64Result;
|
||||
bool success = false;
|
||||
|
||||
// 3. 开始 Windows GDI 抓屏逻辑 (使用花括号限定生命周期)
|
||||
{
|
||||
return { { "error", { { "code", -32000 }, { "message", "当前没有打开的 CAD 文档" } } } };
|
||||
}
|
||||
RECT rc;
|
||||
GetClientRect(hWnd, &rc);
|
||||
int width = rc.right - rc.left;
|
||||
int height = rc.bottom - rc.top;
|
||||
|
||||
// 3. 极其重要:因为调用发起者是隐式窗口消息,并非标准 CAD 命令,必须显式锁文档!
|
||||
acDocManager->lockDocument(pDoc);
|
||||
AcDbDatabase* pDb = pDoc->database();
|
||||
HDC hdcScreen = GetDC(hWnd);
|
||||
HDC hdcMem = CreateCompatibleDC(hdcScreen);
|
||||
HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, width, height);
|
||||
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
|
||||
|
||||
AcDbBlockTable* pBlockTable = nullptr;
|
||||
Acad::ErrorStatus es = pDb->getBlockTable(pBlockTable, AcDb::kForRead);
|
||||
|
||||
if (es == Acad::eOk)
|
||||
{
|
||||
AcDbBlockTableRecord* pModelSpace = nullptr;
|
||||
es = pBlockTable->getAt(ACDB_MODEL_SPACE, pModelSpace, AcDb::kForWrite);
|
||||
|
||||
if (es == Acad::eOk)
|
||||
// 将屏幕内容块拷贝到内存位图中
|
||||
BitBlt(hdcMem, 0, 0, width, height, hdcScreen, 0, 0, SRCCOPY);
|
||||
|
||||
// 将 GDI 位图转换为 GDI+ 位图
|
||||
Gdiplus::Bitmap bmp(hBitmap, NULL);
|
||||
|
||||
// 创建内存流,将位图以 PNG 格式保存到内存,而非硬盘
|
||||
IStream *pStream = nullptr;
|
||||
if (CreateStreamOnHGlobal(NULL, TRUE, &pStream) == S_OK)
|
||||
{
|
||||
// 在底层 C++ 层面构造实体对象
|
||||
AcGePoint3d center(x, y, 0.0);
|
||||
AcGeVector3d normal(0.0, 0.0, 1.0); // Z轴法线
|
||||
AcDbCircle* pCircle = new AcDbCircle(center, normal, r);
|
||||
CLSID pngClsid;
|
||||
GetEncoderClsid(L"image/png", &pngClsid);
|
||||
|
||||
// 追加到模型空间
|
||||
AcDbObjectId circleId;
|
||||
pModelSpace->appendAcDbEntity(circleId, pCircle);
|
||||
|
||||
// 释放对象
|
||||
pCircle->close();
|
||||
pModelSpace->close();
|
||||
if (bmp.Save(pStream, &pngClsid, NULL) == Gdiplus::Ok)
|
||||
{
|
||||
// 从内存流中读取二进制数据
|
||||
STATSTG stat;
|
||||
pStream->Stat(&stat, STATFLAG_NONAME);
|
||||
ULONG size = stat.cbSize.LowPart;
|
||||
|
||||
std::vector<BYTE> buffer(size);
|
||||
LARGE_INTEGER liZero = {};
|
||||
pStream->Seek(liZero, STREAM_SEEK_SET, NULL);
|
||||
|
||||
ULONG bytesRead;
|
||||
pStream->Read(buffer.data(), size, &bytesRead);
|
||||
|
||||
// 转换为 Base64
|
||||
base64Result = Base64Encode(buffer.data(), size);
|
||||
success = true;
|
||||
}
|
||||
pStream->Release();
|
||||
}
|
||||
pBlockTable->close();
|
||||
|
||||
// 清理 GDI 资源
|
||||
SelectObject(hdcMem, hOldBitmap);
|
||||
DeleteObject(hBitmap);
|
||||
DeleteDC(hdcMem);
|
||||
ReleaseDC(hWnd, hdcScreen);
|
||||
}
|
||||
|
||||
// 4. 解锁文档并刷新显示
|
||||
acDocManager->unlockDocument(pDoc);
|
||||
acedUpdateDisplay();
|
||||
// 4. 关闭 GDI+
|
||||
Gdiplus::GdiplusShutdown(gdiplusToken);
|
||||
|
||||
// 5. 返回标准 MCP 结果,如果失败可以根据 es 的值返回 make_error
|
||||
if (es == Acad::eOk)
|
||||
// 5. 按照 MCP 多模态协议返回数据
|
||||
if (success)
|
||||
{
|
||||
return mcp_Tool_Utility::make_text_result(L"C++底层接口已成功在模型空间生成圆形!");
|
||||
return mcp_Tool_Utility::make_mixed_result(L"已成功截取 AutoCAD 当前视口画面。", base64Result, "image/png");
|
||||
}
|
||||
else
|
||||
{
|
||||
return mcp_Tool_Utility::make_error((int)es, L"ObjectARX 内部错误");
|
||||
return mcp_Tool_Utility::make_error(-32001, L"截图或图像编码失败");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 使用宏导出 C 风格工厂函数,供基座的 LoadLibrary 解析
|
||||
EXPORT_MCP_TOOL(mcp_Tool_DrawCircleCpp, CreateDrawCircleToolCpp)
|
||||
EXPORT_MCP_TOOL(mcp_Tool_ViewportScreenshot, CreateViewportScreenshotTool)
|
||||
|
||||
|
||||
Reference in new issue
Block a user