我正在研究为什么在运行我的 Windows 应用程序时,在渲染实际应用程序之前(即在接收到 WM_ERASEBKGND 和 WM_PAINT 之前)它会出现短暂的白色背景闪烁。
现在,我刚刚注意到这个问题也存在于 Visual Studio 创建的默认示例应用程序中。至少我在 Windows 10,21H1(在 VS2008 和 VS2013 中)下运行时是这样。
创建“新的 Win32 项目”后,您唯一要做的就是更改窗口类的背景颜色,例如,更改为红色:
//wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.hbrBackground = (HBRUSH) CreateSolidBrush(RGB(255, 0, 0));
Run Code Online (Sandbox Code Playgroud)
然后将带有 Sleep 的 WM_ERASEBKGND 添加到 WndProc:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_ERASEBKGND:
Sleep(1000);
return DefWindowProc(hWnd, message, wParam, lParam);
Run Code Online (Sandbox Code Playgroud)
睡眠夸大了问题,导致白色背景显示至少一秒钟。之后,红色背景将按预期绘制。
我在运行包含这些更改的应用程序时添加了一个简短的视频。
对于任何应用程序来说,窗口在渲染前闪烁白色看起来都很不专业,尤其是在界面是黑色的情况下。所以我的问题是:是什么导致了这种行为?在调用 ShowWindow(..) 之前,通过 RegisterClassEx 设置背景颜色并传递给 CreateWindow,因此 Windows 应该知道背景颜色是红色。那么为什么它会呈现白色呢?我错过了什么吗?
理想情况下,我想将初始背景颜色更改为白色以外的颜色,例如黑色。但如何呢?我在调用 ShowWindow 之前尝试绘制到窗口,但没有运气。
正如 OP 的出色研究所证明的那样,这确实似乎是一个 Windows 错误。
该错误甚至影响了微软开发的应用程序。
问题是什么是最好的解决方法,特别是对于即使在特定版本的 Windows 11(或 Windows 10)中发布修复程序后也需要支持向后兼容性的产品。
主要问题是,正是使窗口可见的行为使得 Windows 在正确应用背景画笔之前使用白色画笔绘制窗口,无论事先在其 DC 中绘制了什么。因此,诸如在显示窗口之前绘制到 DC 中之类的技巧并不令人满意,因为白色背景仍然会显示,即使只显示几帧。
一种似乎效果很好的方法是使窗口可见但完全透明,绘制背景,然后使窗口不透明。我们还需要为窗口的激活设置动画,这样它就不会只是弹出。例如,我们可以WM_SHOWWINDOW为此进行劫持:
case WM_SHOWWINDOW:
{
if (!GetLayeredWindowAttributes(hWnd, NULL, NULL, NULL))
{
SetLayeredWindowAttributes(hWnd, 0, 0, LWA_ALPHA);
DefWindowProc(hWnd, WM_ERASEBKGND, (WPARAM)GetDC(hWnd), lParam);
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
AnimateWindow(hWnd, 200, AW_ACTIVATE|AW_BLEND);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
Run Code Online (Sandbox Code Playgroud)
完整示例代码:
#include "framework.h"
#include "WindowsProject1.h"
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HINSTANCE mInstance;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
mInstance = hInstance;
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
HWND hWnd = CreateWindowExW(WS_EX_LAYERED, szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_SHOWWINDOW:
{
if (!GetLayeredWindowAttributes(hWnd, NULL, NULL, NULL))
{
SetLayeredWindowAttributes(hWnd, 0, 0, LWA_ALPHA);
DefWindowProc(hWnd, WM_ERASEBKGND, (WPARAM)GetDC(hWnd), lParam);
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
AnimateWindow(hWnd, 200, AW_ACTIVATE|AW_BLEND);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
ReleaseDC(hWnd, hdc);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3336 次 |
| 最近记录: |