任务栏可能:
ABN_FULLSCREENAPP 是最可靠的全屏检测,Windows Shell 自己就是用这个判断是否隐藏任务栏。
反作弊引擎(EAC、BattlEye、Vanguard)会:
利用 WS_EX_NOREDIRECTIONBITMAP:
这个标志让窗口没有重定向表面(Redirection Surface):
缺点: 不是所有截图方式都能规避,Windows.Graphics.Capture 仍可能抓到。
注入 explorer.exe,在内部创建 DirectComposition visual:
优点: 没有独立窗口,反作弊无从检测
缺点: 注入 explorer 本身风险高
不是隐藏,而是真正销毁,截图结束后重建:
缺点: 无法覆盖所有截图方式,有延迟
如果 overlay 只是少量文字/图标,让它截图时无害可见:
现实建议: 如果 overlay 只是显示信息(时钟、监控等),让它截图中可见反而最安全。反作弊针对的是刻意隐藏的覆盖层,一个正常可见的窗口不会触发检测。
| 事件 |
检测方式 |
响应 |
| 全屏游戏进入 |
ABN_FULLSCREENAPP / HSHELL_RUDEAPPACTIVATED |
隐藏 overlay |
| 全屏游戏退出 |
ABN_FULLSCREENAPP(lp=0) / HSHELL_WINDOWACTIVATED |
显示 + 同步位置 |
| 任务栏移动/调整大小 |
ABN_POSCHANGED / Timer 兜底 |
重新定位 |
| 任务栏自动隐藏 |
Timer 检查 GetWindowRect |
跟随隐藏 |
| 场景 |
建议 |
| 不跑游戏/无反作弊 |
WDA_EXCLUDEFROMCAPTURE |
| 有反作弊运行 |
不隐藏,让 overlay 与任务栏融为一体 |
| 需要绝对隐藏 |
WS_EX_NOREDIRECTIONBITMAP + DComp 渲染 |
| 最安全 |
注入 explorer,不创建独立窗口 |
class TaskbarOverlay {
HWND m_hOverlay = NULL;
HWND m_hTray = NULL;
bool IsTaskbarVisible() {
if (!m_hTray || !IsWindowVisible(m_hTray))
return false;
APPBARDATA abd = { sizeof(abd) };
UINT state = (UINT)SHAppBarMessage(ABM_GETSTATE, &abd);
if (state & ABS_AUTOHIDE) {
RECT rc;
GetWindowRect(m_hTray, &rc);
if ((rc.bottom - rc.top) <= 2 || (rc.right - rc.left) <= 2)
return false;
}
return !IsFullscreenAppRunning();
}
bool IsFullscreenAppRunning() {
APPBARDATA abd = { sizeof(abd) };
HWND hFg = GetForegroundWindow();
if (!hFg || hFg == m_hTray) return false;
RECT rcWnd, rcMonitor;
GetWindowRect(hFg, &rcWnd);
HMONITOR hMon = MonitorFromWindow(hFg, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { sizeof(mi) };
GetMonitorInfoW(hMon, &mi);
rcMonitor = mi.rcMonitor;
return (rcWnd.left <= rcMonitor.left &&
rcWnd.top <= rcMonitor.top &&
rcWnd.right >= rcMonitor.right &&
rcWnd.bottom >= rcMonitor.bottom);
}
void SyncOverlay() {
if (IsTaskbarVisible()) {
RECT rc;
GetWindowRect(m_hTray, &rc);
SetWindowPos(m_hOverlay, HWND_TOPMOST,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
SWP_SHOWWINDOW | SWP_NOACTIVATE);
} else {
ShowWindow(m_hOverlay, SW_HIDE);
}
}
};
UINT WM_SHELLHOOK = RegisterWindowMessageW(L"SHELLHOOK");
RegisterShellHookWindow(m_hOverlay);
case WM_SHELLHOOK:
switch (wParam) {
case HSHELL_RUDEAPPACTIVATED:
ShowWindow(m_hOverlay, SW_HIDE);
break;
case HSHELL_WINDOWACTIVATED:
SyncOverlay();
break;
case HSHELL_REDRAW:
SyncOverlay();
break;
}
break;
APPBARDATA abd = { sizeof(abd) };
abd.hWnd = m_hOverlay;
abd.uCallbackMessage = WM_USER + 100;
SHAppBarMessage(ABM_NEW, &abd);
case WM_USER + 100:
if (wParam == ABN_FULLSCREENAPP) {
if (lParam) {
ShowWindow(m_hOverlay, SW_HIDE);
} else {
SyncOverlay();
}
}
break;
static HWND g_hOverlay;
static HWND g_hTray;
LRESULT CALLBACK OverlayProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
static UINT WM_SHELLHOOK;
switch (msg) {
case WM_CREATE:
WM_SHELLHOOK = RegisterWindowMessageW(L"SHELLHOOK");
RegisterShellHookWindow(hwnd);
{
APPBARDATA abd = { sizeof(abd), hwnd, WM_USER + 1 };
SHAppBarMessage(ABM_NEW, &abd);
}
SetTimer(hwnd, 1, 1000, NULL);
break;
case WM_TIMER:
SyncPosition();
break;
case WM_USER + 1:
if (wp == ABN_FULLSCREENAPP) {
ShowWindow(hwnd, lp ? SW_HIDE : SW_SHOWNOACTIVATE);
if (!lp) SyncPosition();
} else if (wp == ABN_POSCHANGED) {
SyncPosition();
}
break;
case WM_DESTROY:
DeregisterShellHookWindow(hwnd);
{
APPBARDATA abd = { sizeof(abd), hwnd };
SHAppBarMessage(ABM_REMOVE, &abd);
}
break;
}
if (msg == WM_SHELLHOOK) {
switch (wp) {
case HSHELL_RUDEAPPACTIVATED:
ShowWindow(hwnd, SW_HIDE);
break;
case HSHELL_WINDOWACTIVATED:
SyncPosition();
ShowWindow(hwnd, SW_SHOWNOACTIVATE);
break;
}
return 0;
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
void SyncPosition() {
RECT rc;
GetWindowRect(g_hTray, &rc);
SetWindowPos(g_hOverlay, HWND_TOPMOST,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
HWND hOverlay = CreateWindowExW(
WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
L"Overlay", NULL,
WS_POPUP | WS_VISIBLE,
x, y, w, h, NULL, NULL, hInst, NULL);
[内核课程]《Windows内核攻防实战》!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。