移除窗口大小调整边框后出现黑边 如何让窗口填充绘制该区域
问题描述
当我移除窗口的大小调整边框后,窗口四周会残留黑色区域。既然大小调整边框已经被移除,是否有方法可以让窗口占用/绘制这些区域?
复现步骤
通过如下命令启动Chrome浏览器实例:chrome.exe -app=https://www.google.com
初始测试代码
移除大小调整边框的代码:
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE); lStyle &= ~(WS_THICKFRAME | WS_DLGFRAME); SetWindowLong(hwnd, GWL_STYLE, lStyle);
相关窗口样式说明:
WS_THICKFRAME(0x00040000L):该样式表示窗口拥有可调整大小的边框,与WS_SIZEBOX样式等效。WS_DLGFRAME(0x00400000L):该样式表示窗口拥有对话框常用风格的边框,带此样式的窗口不能包含标题栏。
残留的黑色区域分布在窗口的左侧、右侧和底部,效果如下:
按照通用建议调用携带SWP_FRAMECHANGED参数的SetWindowPos()后,没有产生任何可见变化,相关代码如下:
int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED; SetWindowPos(hWnd, 0, 0, 0, 0, 0, flags);
完整测试代码:
#include <iostream> #include <windows.h> int main() { HWND hwnd = FindWindowW( L"Chrome_WidgetWin_1", nullptr ); //HWND hwnd = FindWindowW( L"Notepad", nullptr ); std::cout << "hwnd: " << hwnd << "\n"; LONG lStyle = GetWindowLong( hwnd, GWL_STYLE ); lStyle &= ~( WS_THICKFRAME | WS_DLGFRAME ); SetWindowLong( hwnd, GWL_STYLE, lStyle ); int flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED; auto swp = SetWindowPos( hwnd, 0, 0, 0, 0, 0, flags ); std::cout << "swp: " << swp << "\n"; }
解决方案
黑边问题来自三个层面的疏漏,按如下步骤修改即可完全消除黑边,让窗口客户区铺满整个窗口区域:
- 错误标记拦截了客户区重算:调用
SetWindowPos时传入的SWP_NOSENDCHANGING标记会阻止系统向窗口发送WM_NCCALCSIZE消息,而客户区尺寸的重新计算完全依赖该消息,这会直接导致SWP_FRAMECHANGED失效。 - 未清理扩展样式和DWM缓存:仅修改
GWL_STYLE不足以移除所有边框预留,扩展窗口样式中的边框标记、DWM(桌面窗口管理器)缓存的非客户区渲染策略,都会继续给窗口预留边框/阴影的位置,这部分未被划给客户区的预留区域就是看到的黑边。 - Chromium系窗口的自定义渲染逻辑:Chrome这类基于Chromium的窗口会自行实现非客户区绘制逻辑,不会自动响应外部修改的窗口样式,需要主动触发它重算客户区范围。
修正后的完整实现
首先需要引入DWM相关头文件和库,补全样式清理、DWM配置、强制重算逻辑:
#include <iostream> #include <windows.h> #include <dwmapi.h> // 链接DWM库 #pragma comment(lib, "dwmapi.lib") int main() { HWND hwnd = FindWindowW(L"Chrome_WidgetWin_1", nullptr); if (!hwnd) { std::cout << "Target window not found\n"; return 1; } std::cout << "hwnd: " << hwnd << "\n"; // 1. 清理基础窗口样式中的所有边框标记 LONG lStyle = GetWindowLong(hwnd, GWL_STYLE); lStyle &= ~(WS_THICKFRAME | WS_DLGFRAME | WS_BORDER); SetWindowLong(hwnd, GWL_STYLE, lStyle); // 2. 清理扩展窗口样式中的所有边缘相关标记 LONG lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); lExStyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_DLGMODALFRAME); SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle); // 3. 重置DWM非客户区渲染配置,清除边框预留 BOOL ncRenderPolicy = DWMNCRP_ENABLED; DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderPolicy, sizeof(ncRenderPolicy)); BOOL allowNcPaint = TRUE; DwmSetWindowAttribute(hwnd, DWMWA_ALLOW_NCPAINT, &allowNcPaint, sizeof(allowNcPaint)); // 将DWM框架边距设为0,彻底消除1px残留边框 MARGINS zeroMargins = {0, 0, 0, 0}; DwmExtendFrameIntoClientArea(hwnd, &zeroMargins); // 4. 重新计算窗口框架,注意移除SWP_NOSENDCHANGING标记 int flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED; BOOL swpRes = SetWindowPos(hwnd, 0, 0, 0, 0, 0, flags); // 5. 主动触发Chromium窗口重算客户区、强制重绘 SendMessageW(hwnd, WM_NCCALCSIZE, 0, 0); RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW); std::cout << "SetWindowPos result: " << swpRes << "\n"; return 0; }
补充说明
- 上述代码对记事本等系统原生窗口同样生效,不需要额外适配。
- 如果需要保留窗口阴影,不需要修改
DWMWA_NCRENDERING_POLICY配置,仅清理样式、移除错误标记、重算客户区即可。 - 如果操作的是更高版本的Chrome,可在发送
WM_NCCALCSIZE时把wParam设为TRUE,附带NCCALCSIZE_PARAMS参数指定客户区铺满整个窗口,兼容性更好。
内容的提问来源于stack exchange,提问作者Cesar
相关产品推荐
相关产品推荐

