From 66c950cecb266c7f0ad403e0bbab9141d163b46a Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Tue, 16 Jun 2026 21:02:46 +0200 Subject: [PATCH] Refactor: split ScanScreen window-capture into 3 private methods --- client/ScreenSpy.cpp | 193 +++++++++++++++++++++---------------------- client/ScreenSpy.h | 8 ++ 2 files changed, 104 insertions(+), 97 deletions(-) diff --git a/client/ScreenSpy.cpp b/client/ScreenSpy.cpp index f68c4af..dd0eac9 100644 --- a/client/ScreenSpy.cpp +++ b/client/ScreenSpy.cpp @@ -171,99 +171,17 @@ LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength) VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight) { if (m_hTargetWnd || m_bDynamicForeground) { - // 动态前景模式:每帧查询当前前景窗口,切换时尺寸不变则直接复用,尺寸变化则触发重建 - if (m_bDynamicForeground) { - HWND fg = GetForegroundWindow(); - if (fg && fg != m_hTargetWnd) { - RECT wndRc = {}, frameRc = {}; - GetWindowRect(fg, &wndRc); - frameRc = wndRc; - DwmGetWindowAttribute(fg, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)); - ULONG newW = (ULONG)(frameRc.right - frameRc.left); - ULONG newH = (ULONG)(frameRc.bottom - frameRc.top); - if (newW != m_ulFullWidth || newH != m_ulFullHeight) { - // 尺寸不同:触发重建,同时把新 HWND 写入 m_NextTargetWnd, - // 让 WorkThread 在重建前同步更新 CScreenManager::m_hTargetWnd - m_NextTargetWnd = fg; - m_bNeedRestart = true; - return; - } - // 尺寸相同:直接切换,更新阴影偏移和缓存,无需重建 - m_ShadowLeft = frameRc.left - wndRc.left; - m_ShadowTop = frameRc.top - wndRc.top; - m_CachedWndW = wndRc.right - wndRc.left; - m_CachedWndH = wndRc.bottom - wndRc.top; - m_PendingWndW = m_PendingWndH = 0; - m_hTargetWnd = fg; - Mprintf("CScreenSpy: 前景切换(同尺寸) -> HWND=%p\n", fg); - } - if (!m_hTargetWnd) return; // 当前无前景窗口,冻结上一帧 - } - - // 最小化:不绘制,hdcDest 保留上一帧内容(远程端看到冻结画面) + if (m_bDynamicForeground && !UpdateDynamicForeground()) return; if (IsIconic(m_hTargetWnd)) return; - - // 检测窗口尺寸变化:GetWindowRect 廉价,每帧调用; - // DwmGetWindowAttribute 较重,仅在 GetWindowRect 尺寸稳定后才调用。 - // 防抖策略:拖拽 resize 期间尺寸持续变化,冻结画面等待稳定 300ms 后再重建, - // 避免拖拽过程中每帧重建 DIBSection / 重发 BitmapInfo / 重置编解码器。 - static const DWORD RESIZE_DEBOUNCE_MS = 300; - RECT fullRc = {}; - GetWindowRect(m_hTargetWnd, &fullRc); - // 窗口每帧都可能移动,实时更新屏幕偏移,保证 PointConversion 坐标正确 - m_iScreenX = fullRc.left + m_ShadowLeft; - m_iScreenY = fullRc.top + m_ShadowTop; - int w = fullRc.right - fullRc.left; - int h = fullRc.bottom - fullRc.top; - if (w != m_CachedWndW || h != m_CachedWndH) { - // 尺寸有变化:更新防抖记录 - if (w != m_PendingWndW || h != m_PendingWndH) { - m_PendingWndW = w; - m_PendingWndH = h; - m_SizeChangeTick = GetTickCount(); - } - // 尚未稳定:冻结当前帧,继续等待 - if (GetTickCount() - m_SizeChangeTick < RESIZE_DEBOUNCE_MS) - return; - // 稳定 300ms:查询 DWM 真实帧边界,决定是否重建 - RECT frameRc = fullRc; - if (SUCCEEDED(DwmGetWindowAttribute(m_hTargetWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)))) { - m_ShadowLeft = frameRc.left - fullRc.left; - m_ShadowTop = frameRc.top - fullRc.top; - } else { - m_ShadowLeft = m_ShadowTop = 0; - } - if ((ULONG)(frameRc.right - frameRc.left) != m_ulFullWidth || - (ULONG)(frameRc.bottom - frameRc.top) != m_ulFullHeight) { - Mprintf("CScreenSpy: 窗口尺寸变化 %dx%d -> %dx%d,触发重建\n", - m_ulFullWidth, m_ulFullHeight, - frameRc.right - frameRc.left, frameRc.bottom - frameRc.top); - m_bNeedRestart = true; - return; - } - m_CachedWndW = w; - m_CachedWndH = h; - } - - // PrintWindow → 临时 DC → BitBlt 到目标(用缓存的阴影偏移跳过 DWM 扩展帧) - HDC hTmp = m_data.GetWindowDC(); - HBITMAP hOld = (HBITMAP)SelectObject(hTmp, m_data.GetWindowBmp()); - BOOL ok = PrintWindow(m_hTargetWnd, hTmp, PW_RENDERFULLCONTENT); - if (!ok) ok = PrintWindow(m_hTargetWnd, hTmp, 0); - if (ok) { - BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hTmp, m_ShadowLeft, m_ShadowTop, SRCCOPY); - } - SelectObject(hTmp, hOld); + if (!CheckWindowResize()) return; + CaptureWindowContent(hdcDest, hdcSour, ulWidth, ulHeight); return; } - if (m_bVirtualPaint) { - // 先用深色填充背景,避免窗口移动时留下残影 RECT rcFill = { 0, 0, (LONG)ulWidth, (LONG)ulHeight }; - HBRUSH hBrush = CreateSolidBrush(RGB(30, 30, 30)); // 深灰色背景 + HBRUSH hBrush = CreateSolidBrush(RGB(30, 30, 30)); FillRect(hdcDest, &rcFill, hBrush); DeleteObject(hBrush); - int n = 0; if (n = EnumWindowsTopToDown(NULL, EnumHwndsPrint, (LPARAM)&m_data.SetScreenDC(hdcDest))) { Mprintf("EnumWindowsTopToDown failed: %d!!! GetLastError: %d\n", n, GetLastError()); @@ -275,18 +193,99 @@ VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHei #if COPY_ALL BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hdcSour, m_iScreenX, m_iScreenY, SRCCOPY); #else - const ULONG ulJumpLine = 50; - const ULONG ulJumpSleep = ulJumpLine / 10; - - for (int i = 0, ulToJump = 0; i < ulHeight; i += ulToJump) { - ULONG ulv1 = ulHeight - i; - - if (ulv1 > ulJumpLine) - ulToJump = ulJumpLine; - else - ulToJump = ulv1; - BitBlt(hdcDest, 0, i, ulWidth, ulToJump, hdcSour,0, i, SRCCOPY); + const ULONG ulJumpLine = 50; + const ULONG ulJumpSleep = ulJumpLine / 10; + for (int i = 0, ulToJump = 0; i < (int)ulHeight; i += ulToJump) { + ULONG ulv1 = ulHeight - i; + ulToJump = (ulv1 > ulJumpLine) ? ulJumpLine : ulv1; + BitBlt(hdcDest, 0, i, ulWidth, ulToJump, hdcSour, 0, i, SRCCOPY); Sleep(ulJumpSleep); } #endif } + +// 每帧跟踪前景窗口;切换时尺寸相同直接复用,尺寸不同触发重建 +// 返回 false 表示本帧跳过(冻结) +bool CScreenSpy::UpdateDynamicForeground() +{ + HWND fg = GetForegroundWindow(); + if (fg && fg != m_hTargetWnd) { + RECT wndRc = {}, frameRc = {}; + GetWindowRect(fg, &wndRc); + frameRc = wndRc; + DwmGetWindowAttribute(fg, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)); + ULONG newW = (ULONG)(frameRc.right - frameRc.left); + ULONG newH = (ULONG)(frameRc.bottom - frameRc.top); + if (newW != m_ulFullWidth || newH != m_ulFullHeight) { + // 尺寸不同:让 WorkThread 重建,m_NextTargetWnd 传递新 HWND + m_NextTargetWnd = fg; + m_bNeedRestart = true; + return false; + } + // 尺寸相同:直接切换,更新阴影偏移缓存,无需重建 + m_ShadowLeft = frameRc.left - wndRc.left; + m_ShadowTop = frameRc.top - wndRc.top; + m_CachedWndW = wndRc.right - wndRc.left; + m_CachedWndH = wndRc.bottom - wndRc.top; + m_PendingWndW = m_PendingWndH = 0; + m_hTargetWnd = fg; + Mprintf("CScreenSpy: 前景切换(同尺寸) -> HWND=%p\n", fg); + } + return m_hTargetWnd != NULL; // NULL=无前景窗口,冻结上一帧 +} + +// 检测窗口 resize:GetWindowRect 每帧调用;DWM 查询仅在尺寸稳定 300ms 后触发 +// 同时更新 m_iScreenX/Y(窗口移动时坐标同步),返回 false 表示本帧跳过 +bool CScreenSpy::CheckWindowResize() +{ + static const DWORD RESIZE_DEBOUNCE_MS = 300; + RECT fullRc = {}; + GetWindowRect(m_hTargetWnd, &fullRc); + m_iScreenX = fullRc.left + m_ShadowLeft; + m_iScreenY = fullRc.top + m_ShadowTop; + int w = fullRc.right - fullRc.left; + int h = fullRc.bottom - fullRc.top; + if (w == m_CachedWndW && h == m_CachedWndH) + return true; + // 尺寸有变化:更新防抖记录 + if (w != m_PendingWndW || h != m_PendingWndH) { + m_PendingWndW = w; + m_PendingWndH = h; + m_SizeChangeTick = GetTickCount(); + } + if (GetTickCount() - m_SizeChangeTick < RESIZE_DEBOUNCE_MS) + return false; // 尚未稳定,冻结等待 + // 稳定 300ms:查询 DWM 真实帧边界,决定是否重建 + RECT frameRc = fullRc; + if (SUCCEEDED(DwmGetWindowAttribute(m_hTargetWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)))) { + m_ShadowLeft = frameRc.left - fullRc.left; + m_ShadowTop = frameRc.top - fullRc.top; + } else { + m_ShadowLeft = m_ShadowTop = 0; + } + if ((ULONG)(frameRc.right - frameRc.left) != m_ulFullWidth || + (ULONG)(frameRc.bottom - frameRc.top) != m_ulFullHeight) { + Mprintf("CScreenSpy: 窗口尺寸变化 %dx%d -> %dx%d,触发重建\n", + m_ulFullWidth, m_ulFullHeight, + frameRc.right - frameRc.left, frameRc.bottom - frameRc.top); + m_bNeedRestart = true; + return false; + } + m_CachedWndW = w; + m_CachedWndH = h; + return true; +} + +// 前景模式直接 BitBlt(零闪烁);指定窗口模式用 PrintWindow(处理遮挡) +void CScreenSpy::CaptureWindowContent(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight) +{ + if (m_bDynamicForeground) { + BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hdcSour, m_iScreenX, m_iScreenY, SRCCOPY); + } else { + HDC hTmp = m_data.GetWindowDC(); + HBITMAP hOld = (HBITMAP)SelectObject(hTmp, m_data.GetWindowBmp()); + if (PrintWindow(m_hTargetWnd, hTmp, PW_RENDERFULLCONTENT)) + BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hTmp, m_ShadowLeft, m_ShadowTop, SRCCOPY); + SelectObject(hTmp, hOld); + } +} diff --git a/client/ScreenSpy.h b/client/ScreenSpy.h index 6f20952..4e7b50d 100644 --- a/client/ScreenSpy.h +++ b/client/ScreenSpy.h @@ -266,6 +266,14 @@ public: m_hDeskTopDC = GetDC(NULL); m_data.Create(m_hDeskTopDC, m_iScreenX, m_iScreenY, m_ulFullWidth, m_ulFullHeight); } + +private: + // 前景跟踪:切换或尺寸变化时更新 m_hTargetWnd,返回 false 表示本帧冻结 + bool UpdateDynamicForeground(); + // 防抖 + 重建检测:更新 m_iScreenX/Y,返回 false 表示本帧冻结 + bool CheckWindowResize(); + // 执行实际像素拷贝(BitBlt 或 PrintWindow) + void CaptureWindowContent(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight); }; #endif // !defined(AFX_SCREENSPY_H__5F74528D_9ABD_404E_84D2_06C96A0615F4__INCLUDED_)