Refactor: split ScanScreen window-capture into 3 private methods
This commit is contained in:
@@ -171,8 +171,43 @@ LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength)
|
|||||||
VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight)
|
VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHeight)
|
||||||
{
|
{
|
||||||
if (m_hTargetWnd || m_bDynamicForeground) {
|
if (m_hTargetWnd || m_bDynamicForeground) {
|
||||||
// 动态前景模式:每帧查询当前前景窗口,切换时尺寸不变则直接复用,尺寸变化则触发重建
|
if (m_bDynamicForeground && !UpdateDynamicForeground()) return;
|
||||||
if (m_bDynamicForeground) {
|
if (IsIconic(m_hTargetWnd)) return;
|
||||||
|
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));
|
||||||
|
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());
|
||||||
|
Sleep(50);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AUTO_TICK(70, "");
|
||||||
|
#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 < (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();
|
HWND fg = GetForegroundWindow();
|
||||||
if (fg && fg != m_hTargetWnd) {
|
if (fg && fg != m_hTargetWnd) {
|
||||||
RECT wndRc = {}, frameRc = {};
|
RECT wndRc = {}, frameRc = {};
|
||||||
@@ -182,13 +217,12 @@ VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHei
|
|||||||
ULONG newW = (ULONG)(frameRc.right - frameRc.left);
|
ULONG newW = (ULONG)(frameRc.right - frameRc.left);
|
||||||
ULONG newH = (ULONG)(frameRc.bottom - frameRc.top);
|
ULONG newH = (ULONG)(frameRc.bottom - frameRc.top);
|
||||||
if (newW != m_ulFullWidth || newH != m_ulFullHeight) {
|
if (newW != m_ulFullWidth || newH != m_ulFullHeight) {
|
||||||
// 尺寸不同:触发重建,同时把新 HWND 写入 m_NextTargetWnd,
|
// 尺寸不同:让 WorkThread 重建,m_NextTargetWnd 传递新 HWND
|
||||||
// 让 WorkThread 在重建前同步更新 CScreenManager::m_hTargetWnd
|
|
||||||
m_NextTargetWnd = fg;
|
m_NextTargetWnd = fg;
|
||||||
m_bNeedRestart = true;
|
m_bNeedRestart = true;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// 尺寸相同:直接切换,更新阴影偏移和缓存,无需重建
|
// 尺寸相同:直接切换,更新阴影偏移缓存,无需重建
|
||||||
m_ShadowLeft = frameRc.left - wndRc.left;
|
m_ShadowLeft = frameRc.left - wndRc.left;
|
||||||
m_ShadowTop = frameRc.top - wndRc.top;
|
m_ShadowTop = frameRc.top - wndRc.top;
|
||||||
m_CachedWndW = wndRc.right - wndRc.left;
|
m_CachedWndW = wndRc.right - wndRc.left;
|
||||||
@@ -197,34 +231,30 @@ VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHei
|
|||||||
m_hTargetWnd = fg;
|
m_hTargetWnd = fg;
|
||||||
Mprintf("CScreenSpy: 前景切换(同尺寸) -> HWND=%p\n", fg);
|
Mprintf("CScreenSpy: 前景切换(同尺寸) -> HWND=%p\n", fg);
|
||||||
}
|
}
|
||||||
if (!m_hTargetWnd) return; // 当前无前景窗口,冻结上一帧
|
return m_hTargetWnd != NULL; // NULL=无前景窗口,冻结上一帧
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最小化:不绘制,hdcDest 保留上一帧内容(远程端看到冻结画面)
|
// 检测窗口 resize:GetWindowRect 每帧调用;DWM 查询仅在尺寸稳定 300ms 后触发
|
||||||
if (IsIconic(m_hTargetWnd)) return;
|
// 同时更新 m_iScreenX/Y(窗口移动时坐标同步),返回 false 表示本帧跳过
|
||||||
|
bool CScreenSpy::CheckWindowResize()
|
||||||
// 检测窗口尺寸变化:GetWindowRect 廉价,每帧调用;
|
{
|
||||||
// DwmGetWindowAttribute 较重,仅在 GetWindowRect 尺寸稳定后才调用。
|
|
||||||
// 防抖策略:拖拽 resize 期间尺寸持续变化,冻结画面等待稳定 300ms 后再重建,
|
|
||||||
// 避免拖拽过程中每帧重建 DIBSection / 重发 BitmapInfo / 重置编解码器。
|
|
||||||
static const DWORD RESIZE_DEBOUNCE_MS = 300;
|
static const DWORD RESIZE_DEBOUNCE_MS = 300;
|
||||||
RECT fullRc = {};
|
RECT fullRc = {};
|
||||||
GetWindowRect(m_hTargetWnd, &fullRc);
|
GetWindowRect(m_hTargetWnd, &fullRc);
|
||||||
// 窗口每帧都可能移动,实时更新屏幕偏移,保证 PointConversion 坐标正确
|
|
||||||
m_iScreenX = fullRc.left + m_ShadowLeft;
|
m_iScreenX = fullRc.left + m_ShadowLeft;
|
||||||
m_iScreenY = fullRc.top + m_ShadowTop;
|
m_iScreenY = fullRc.top + m_ShadowTop;
|
||||||
int w = fullRc.right - fullRc.left;
|
int w = fullRc.right - fullRc.left;
|
||||||
int h = fullRc.bottom - fullRc.top;
|
int h = fullRc.bottom - fullRc.top;
|
||||||
if (w != m_CachedWndW || h != m_CachedWndH) {
|
if (w == m_CachedWndW && h == m_CachedWndH)
|
||||||
|
return true;
|
||||||
// 尺寸有变化:更新防抖记录
|
// 尺寸有变化:更新防抖记录
|
||||||
if (w != m_PendingWndW || h != m_PendingWndH) {
|
if (w != m_PendingWndW || h != m_PendingWndH) {
|
||||||
m_PendingWndW = w;
|
m_PendingWndW = w;
|
||||||
m_PendingWndH = h;
|
m_PendingWndH = h;
|
||||||
m_SizeChangeTick = GetTickCount();
|
m_SizeChangeTick = GetTickCount();
|
||||||
}
|
}
|
||||||
// 尚未稳定:冻结当前帧,继续等待
|
|
||||||
if (GetTickCount() - m_SizeChangeTick < RESIZE_DEBOUNCE_MS)
|
if (GetTickCount() - m_SizeChangeTick < RESIZE_DEBOUNCE_MS)
|
||||||
return;
|
return false; // 尚未稳定,冻结等待
|
||||||
// 稳定 300ms:查询 DWM 真实帧边界,决定是否重建
|
// 稳定 300ms:查询 DWM 真实帧边界,决定是否重建
|
||||||
RECT frameRc = fullRc;
|
RECT frameRc = fullRc;
|
||||||
if (SUCCEEDED(DwmGetWindowAttribute(m_hTargetWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)))) {
|
if (SUCCEEDED(DwmGetWindowAttribute(m_hTargetWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRc, sizeof(frameRc)))) {
|
||||||
@@ -239,54 +269,23 @@ VOID CScreenSpy::ScanScreen(HDC hdcDest, HDC hdcSour, ULONG ulWidth, ULONG ulHei
|
|||||||
m_ulFullWidth, m_ulFullHeight,
|
m_ulFullWidth, m_ulFullHeight,
|
||||||
frameRc.right - frameRc.left, frameRc.bottom - frameRc.top);
|
frameRc.right - frameRc.left, frameRc.bottom - frameRc.top);
|
||||||
m_bNeedRestart = true;
|
m_bNeedRestart = true;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
m_CachedWndW = w;
|
m_CachedWndW = w;
|
||||||
m_CachedWndH = h;
|
m_CachedWndH = h;
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// PrintWindow → 临时 DC → BitBlt 到目标(用缓存的阴影偏移跳过 DWM 扩展帧)
|
// 前景模式直接 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();
|
HDC hTmp = m_data.GetWindowDC();
|
||||||
HBITMAP hOld = (HBITMAP)SelectObject(hTmp, m_data.GetWindowBmp());
|
HBITMAP hOld = (HBITMAP)SelectObject(hTmp, m_data.GetWindowBmp());
|
||||||
BOOL ok = PrintWindow(m_hTargetWnd, hTmp, PW_RENDERFULLCONTENT);
|
if (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);
|
BitBlt(hdcDest, 0, 0, ulWidth, ulHeight, hTmp, m_ShadowLeft, m_ShadowTop, SRCCOPY);
|
||||||
}
|
|
||||||
SelectObject(hTmp, hOld);
|
SelectObject(hTmp, hOld);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_bVirtualPaint) {
|
|
||||||
// 先用深色填充背景,避免窗口移动时留下残影
|
|
||||||
RECT rcFill = { 0, 0, (LONG)ulWidth, (LONG)ulHeight };
|
|
||||||
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());
|
|
||||||
Sleep(50);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AUTO_TICK(70, "");
|
|
||||||
#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);
|
|
||||||
Sleep(ulJumpSleep);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,6 +266,14 @@ public:
|
|||||||
m_hDeskTopDC = GetDC(NULL);
|
m_hDeskTopDC = GetDC(NULL);
|
||||||
m_data.Create(m_hDeskTopDC, m_iScreenX, m_iScreenY, m_ulFullWidth, m_ulFullHeight);
|
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_)
|
#endif // !defined(AFX_SCREENSPY_H__5F74528D_9ABD_404E_84D2_06C96A0615F4__INCLUDED_)
|
||||||
|
|||||||
Reference in New Issue
Block a user