This indeed seems to be a Windows bug as demonstrated by the excellent research by the OP.
The bug is even affecting applications developed by Microsoft.
The question is what is best workaround, especially for products that need to support backwards compatibility even after a fix is released in a specific version of Windows 11 (or Windows 10).
The main problem is that it is the act of making the window visible that makes Windows paint it with the white brush prior to correctly applying the background brush, regardless of what was painted into its DC beforehand. Therefore tricks such as painting into the DC prior to showing the window are unsatisfying, as the white background will still be shown, even if only for a few frames.
One method that seems to work well is to make the window visible, but fully transparent, paint the background, and then make the window opaque. We also need to animate the activation of the window, so it doesn't just pop in. For example, we can hijack WM_SHOWWINDOW
for this:
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;
Full sample code:
#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;
}