#include "DrawingBoardMgr.h" #include #define MSG_CLEAR 999 namespace SimplePluginsDrawingBoard { DrawingBoardMgr::DrawingBoardMgr(IOCPClient* ClientObject, int n, void* user): CManager(ClientObject) { m_fAlpha = 150; m_bIsWorking = TRUE; m_bTopMost = TRUE; m_hWnd = NULL; m_textColor = RGB(255,0,0); m_Font = CreateFont(20, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Arial")); m_DrawPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); __CreateThread(NULL, 0, DrawingThreadProc, this, 0, NULL); BYTE bToken = TOKEN_DRAWING_BOARD; m_ClientObject->Send2Server((char*)&bToken, 1); } DrawingBoardMgr::~DrawingBoardMgr() { m_bIsWorking = FALSE; while (m_hWnd) Sleep(200); DeleteObject(m_DrawPen); DeleteObject(m_Font); } DWORD WINAPI DrawingBoardMgr::DrawingThreadProc(LPVOID lpParam) { Mprintf("Start DrawingThreadProc\n"); DrawingBoardMgr* pThis = (DrawingBoardMgr*)lpParam; pThis->RunWindowThread(); pThis->m_bIsWorking = FALSE; Mprintf("Stop DrawingThreadProc\n"); return 0; } void DrawingBoardMgr::RunWindowThread() { const TCHAR CLASS_NAME[] = _T("DrawingBoardClass"); static bool s_classRegistered = false; if (!s_classRegistered) { WNDCLASSEX wc = {}; wc.cbSize = sizeof(WNDCLASSEX); wc.lpfnWndProc = DrawingBoardMgr::WndProc; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = CLASS_NAME; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.hIcon = NULL; // 标题栏图标 wc.hIconSm = NULL; // 小图标(Alt+Tab 和任务栏缩略图) if (!RegisterClassEx(&wc)) { Mprintf("RegisterClassEx failed: %d\n", GetLastError()); } s_classRegistered = true; } DWORD dwStyle = WS_POPUP | WS_BORDER; m_hWnd = CreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_LAYERED, CLASS_NAME, _T(""), dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, GetModuleHandle(NULL), this ); if (m_hWnd == NULL) return; LONG style = GetWindowLong(m_hWnd, GWL_STYLE); style |= WS_BORDER; // 增加窄边框 style &= ~(WS_CAPTION | WS_THICKFRAME); // 去掉标题栏和可调整边框 SetWindowLong(m_hWnd, GWL_STYLE, style); // 去掉系统菜单 SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); BringWindowToTop(m_hWnd); // 透明度 SetLayeredWindowAttributes(m_hWnd, 0, m_fAlpha, LWA_ALPHA); ShowWindow(m_hWnd, SW_SHOW); UpdateWindow(m_hWnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0) && m_bIsWorking) { TranslateMessage(&msg); DispatchMessage(&msg); Sleep(1); } if (m_hWnd) PostMessage(m_hWnd, WM_CLOSE, 0, 0); m_hWnd = NULL; // 线程退出后,窗口已销毁 } LRESULT CALLBACK DrawingBoardMgr::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { DrawingBoardMgr* pThis = nullptr; if (msg == WM_NCCREATE) { CREATESTRUCT* cs = (CREATESTRUCT*)lParam; pThis = (DrawingBoardMgr*)cs->lpCreateParams; SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis); } else { pThis = (DrawingBoardMgr*)GetWindowLongPtr(hWnd, GWLP_USERDATA); } if (pThis) { switch (msg) { case WM_GETICON: return NULL; case WM_WINDOWPOSCHANGED: { WINDOWPOS* pwp = (WINDOWPOS*)lParam; if (pThis->m_bTopMost && !(pwp->flags & SWP_NOZORDER)) { // 如果窗口层级被改变,重新设置为顶层 SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); HPEN hOldPen = (HPEN)SelectObject(hdc, pThis->m_DrawPen); HFONT hOldFont = (HFONT)SelectObject(hdc, pThis->m_Font); pThis->m_locker.lock(); auto paths = pThis->m_Paths; auto currentPath = pThis->m_CurrentPath; auto texts = pThis->m_Texts; pThis->m_locker.unlock(); // 画所有历史路径 for (const auto& path : paths) { if (path.size() >= 2) Polyline(hdc, path.data(), (int)path.size()); } // 画当前路径 if (currentPath.size() >= 2) { Polyline(hdc, currentPath.data(), (int)currentPath.size()); } // 绘制文字 SetTextColor(hdc, pThis->m_textColor); // 设置文本颜色 SetBkMode(hdc, TRANSPARENT); // 设置透明背景 for (const auto& entry : texts) TextOutA(hdc, entry.first.x, entry.first.y, entry.second.c_str(), entry.second.length()); SelectObject(hdc, hOldPen); SelectObject(hdc, hOldFont); EndPaint(hWnd, &ps); return 0; } case WM_DESTROY: PostQuitMessage(0); return 0; case MSG_CLEAR: pThis->m_locker.lock(); pThis->m_Paths.clear(); pThis->m_CurrentPath.clear(); pThis->m_Texts.clear(); pThis->m_locker.unlock(); InvalidateRect(hWnd, NULL, TRUE); break; } } return DefWindowProc(hWnd, msg, wParam, lParam); } VOID DrawingBoardMgr::OnReceive(PBYTE szBuffer, ULONG ulLength) { if (ulLength < 1) return; BYTE cmd = szBuffer[0]; switch (cmd) { case CMD_DRAW_POINT: if (ulLength == sizeof(POINT) + 1) { POINT pt = *(POINT*)(szBuffer + 1); m_locker.lock(); m_CurrentPath.push_back(pt); m_locker.unlock(); if (m_hWnd) InvalidateRect(m_hWnd, NULL, FALSE); } break; case CMD_DRAW_END: m_locker.lock(); if (!m_CurrentPath.empty()) { m_Paths.push_back(m_CurrentPath); m_CurrentPath.clear(); if (m_hWnd) InvalidateRect(m_hWnd, NULL, FALSE); } m_locker.unlock(); break; case CMD_TRANSPORT: m_fAlpha = ulLength > 1 ? szBuffer[1] : m_fAlpha; SetLayeredWindowAttributes(m_hWnd, 0, m_fAlpha, LWA_ALPHA); break; case CMD_TOPMOST: m_bTopMost = ulLength > 1 ? szBuffer[1] : TRUE; SetWindowPos(m_hWnd, m_bTopMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); break; case CMD_MOVEWINDOW: if (ulLength == sizeof(RECT) + 1) { RECT rect = *(RECT*)(szBuffer + 1); if (m_hWnd && IsWindow(m_hWnd)) { ::SetWindowPos(m_hWnd, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } break; case CMD_SET_SIZE: { if (ulLength == sizeof(int) * 2 + 1) { int x[2]; memcpy(x, szBuffer + 1, sizeof(int) * 2); if (IsWindow(m_hWnd)) { ::SetWindowPos(m_hWnd, NULL, 0, 0, x[0], x[1], SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } } break; } case CMD_DRAW_CLEAR: { PostMessage(m_hWnd, MSG_CLEAR, 0, 0);; break; } case CMD_DRAW_TEXT: { ParseRemoteText(szBuffer + 1, ulLength - 1); break; } default: break; } } void DrawingBoardMgr::ParseRemoteText(PBYTE data, ULONG length) { if (length < sizeof(POINT)) return; // 提取位置 POINT pt = *(POINT*)data; PBYTE textData = data + sizeof(POINT); int textLen = (int)(length - sizeof(POINT)); // UTF-8 → WCHAR int wideLen = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)textData, textLen, NULL, 0); if (wideLen <= 0) return; std::wstring wstr(wideLen, 0); MultiByteToWideChar(CP_UTF8, 0, (LPCCH)textData, textLen, &wstr[0], wideLen); #ifdef _UNICODE CString cstr(wstr.c_str()); #else // 若项目使用多字节字符集 int ansiLen = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); std::string cstr; if (ansiLen > 0) { std::string ansi(ansiLen - 1, 0); WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &ansi[0], ansiLen - 1, NULL, NULL); cstr = ansi.c_str(); } #endif // 存入本地图形对象并刷新绘图区域 m_locker.lock(); m_Texts.push_back(std::make_pair(pt, cstr)); m_locker.unlock(); if (m_hWnd) InvalidateRect(m_hWnd, NULL, FALSE); } } // namespace SimplePluginsDrawingBoard