据我了解,Windows 在给定窗口的(重新)绘制方面进行了分工;分为背景删除和前景绘画。发送 WM_ERASEBKGND
消息以准备给定窗口的无效部分进行绘制,通常此准备包括删除背景,以便实际绘制可以从干净的 Canvas 开始。在我看来,这条消息总是在 Windows 使给定窗口的一部分无效时发送(因此基本上总是与正在发布的 WM_PAINT
消息一起发送)。每当应用程序本身使给定窗口(的一部分)无效时,InvalidateRect
函数的最后一个参数指定是否发送 WM_ERASEBKGND
。所以我写了一个小程序来测试我的假设,但它的行为让我有点困惑。这是说程序:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hwInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Run the message loop.
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int eb_count = 0; // counts number of WM_ERASEBKGND messages
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
wchar_t text[40];
wsprintf(text, L"Number of WM_ERASEBKGND messages: %i\n", eb_count);
GetClientRect(hwnd, &rect);
DrawText(hdc, text, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_RBUTTONDOWN: // repaint whenever RBUTTONDOWN
InvalidateRect(hwnd, NULL, FALSE);
UpdateWindow(hwnd);
return 0;
case WM_ERASEBKGND:
eb_count++;
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
我在窗口过程中处理WM_ERASEBKGND
(这是我的开关中的一个案例),所以它不应该被传递给默认窗口过程。但是,我没有执行任何实际的背景删除(我只是增加了一个静态变量)并且我返回 0 以指示实际上没有发生删除。在我看来,在这个程序中,背景永远不应该被删除。然而,这确实发生在两个不同的实例中。
每当我最大化窗口时,无效部分的背景确实会被类背景画笔删除。但这怎么可能呢?窗口过程在收到 WM_ERASEBKGND
消息时当然不会做这样的事情。
每当 DrawText
重新绘制它的字符串时,类似的事情就会发生。我希望递增的数字会相互叠加(当然会导致难以辨认的困惑)。因此,“DrawText”函数似乎也以某种方式删除了它在其中绘制字符串的矩形的背景。
我的最后一个问题与我的假设有关,即只要 Windows 使窗口的一部分无效,就会发送 WM_ERASEBKGND
消息。我注意到,每当窗口被另一个窗口覆盖并随后被覆盖时,似乎没有发送任何 WM_ERASEBKGND
消息。这是否意味着我的假设是错误的?
抱歉阅读时间过长,但对于回答这些问题的任何和所有帮助将不胜感激。
最佳答案
... my assumption that a WM_ERASEBKGND message is sent whenever Windows invalidates part of a window. I noticed that whenever the window is covered by another window and subsequently uncovered, no WM_ERASEBKGND message seems to be sent ...
那是因为,从 Vista 开始,我们有 Desktop Window Manager (DWM)潜伏在,呃,背景中。这会缓冲所有屏幕窗口的内容,这样 Windows 就不需要在部分内容未被覆盖时发出 WM_ERASEBKGND 或 WM_PAINT 请求 - 它只需将所谓的后台缓冲区复制回屏幕即可。
[部分] 窗口仍然会失效 - 无论是您还是操作系统 - 但不像 XP 时代那样频繁。例如,尝试最小化和恢复窗口 - 然后必须重新绘制它。当您这样做时,DWM 可能会在窗口最小化时丢弃后台缓冲区以节省内存。
除此之外,其他人在评论中所说的。
关于winapi - 删除窗口背景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50721591/