未公开BlurBehind导致WM_NCHITTEST响应迟缓问题求助
注:以下内容针对Windows 10。Windows 7也有相同功能,但代码略有不同
若为WM_POPUP|WM_VISIBLE窗口应用未公开的背景模糊功能,代码示例如下:
void BlurBehind(HWND hwnd) { struct ACCENTPOLICY { int na; int nf; int nc; int nA; }; struct WINCOMPATTRDATA { int na; PVOID pd; ULONG ul; }; int error = 0; const HINSTANCE hm = LoadLibrary(L"user32.dll"); if (hm) { typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*); const pSetWindowCompositionAttribute SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hm, "SetWindowCompositionAttribute"); if (SetWindowCompositionAttribute) { ACCENTPOLICY policy = { 4, 0, 155, 0 }; WINCOMPATTRDATA data = { 19, &policy,sizeof(ACCENTPOLICY) }; SetWindowCompositionAttribute(hwnd, &data); error = (GetLastError()); } FreeLibrary(hm); } } int WINAPI WinMain(HINSTANCE hin, HINSTANCE, LPSTR, int) { WNDCLASS c = {NULL}; c.lpszClassName = L"Example"; c.lpfnWndProc = WndProc; c.hInstance = hin; c.style = CS_VREDRAW | CS_HREDRAW; c.hCursor = LoadCursor(NULL, IDC_ARROW); c.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); RegisterClass(&c); HWND hwnd = CreateWindowEx(NULL, L"Example", L"Example", WS_POPUP | WS_VISIBLE, 0, 0, 1000, 600, NULL, NULL, hin, 0); MSG msg; while(GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
同时按如下方式处理WM_NCHITTEST消息(该窗口无默认标题栏):
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { switch (message) { case WM_NCHITTEST: { RECT r; GetWindowRect(hwnd, &r); auto cp = MAKEPOINTS(lp); int dyn = 61; const LRESULT result = DefWindowProc(hwnd, message, wp, lp); if (result == HTCLIENT) { if (cp.x <= r.right - dyn && cp.y <= r.top + 31) return HTCAPTION; else { if (r.right - cp.x < 20 && r.bottom - cp.y < 20) return HTBOTTOMRIGHT; else if (r.right - cp.x < 20) return HTRIGHT; else if (cp.x - r.left < 20 && r.bottom - cp.y < 20) return HTBOTTOMLEFT; else if (r.bottom - cp.y < 20) return HTBOTTOM; else if (cp.x - r.left < 20) return HTLEFT; else { SetClassLong(hwnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW)); return result; } } else { SetClassLong(hwnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW)); return result; } } else return result; } break; default: return DefWindowProc(hwnd, message, wp, lp); } return 1; }
此时HT_事件会出现响应缓慢、卡顿的情况。窗口无法跟随鼠标即时移动,比如晃动鼠标后松开,窗口仍会继续沿路径移动,存在明显延迟。
若不启用模糊效果,移动、调整窗口大小等所有事件都能即时响应鼠标操作。
请问有使用过此类未公开功能的开发者遇到过该问题吗?是否有解决方案?
我找到了一个拖动窗口的技巧(尚未测试),但我还需要实现窗口调整大小的功能。
更新
通过最小化示例测试发现,若完全移除WndProc中的WM_PAINT处理逻辑,卡顿现象会消失。此前WM_PAINT中绘制Direct2D内容,之前使用GDI+时也存在该问题。推测卡顿是因为Windows消息处理为同步模式,需等待绘制完成,但不清楚启用模糊后差异为何如此明显。
另外我发现自己在WM_PAINT和WM_SIZE中都调用了渲染函数(因为WM_SIZE中需要重建Direct2D渲染目标)。
目前我已完全移除WM_PAINT中的渲染调用,仅保留ValidateRect调用,情况有所改善,但调整窗口大小时仍有轻微卡顿。
更新2
但现在未做任何改动,卡顿现象再次出现,即使WM_PAINT和WM_SIZE处理为空。即使完全不处理WM_NCHITTEST,窗口移动或调整大小时仍会卡顿。
可能之前误以为卡顿消失是因为我无意中关闭了模糊效果。
我提供的示例具有代表性,请问其他开发者是否也会遇到相同情况(至少非高端配置设备会出现该问题)?
更新3
发现此前卡顿消失并非因为关闭了代码中的模糊效果,而是Surface Go电量低时Windows启用了节电模式,此时系统会关闭“设置-颜色”中的Transparency effects开关。
手动关闭设置中的Transparency effects后,卡顿也会消失。
但有趣的是,自定义窗口的模糊效果并未消失,而UWP应用、开始菜单等的透明效果则会消失。
内容的提问来源于stack exchange,提问作者isagsadvb

