6

I was looking into why when running my Windows App, it has a short flash of white background before rendering the actual app (i.e., before WM_ERASEBKGND and WM_PAINT is received).

Now, I just noticed that this problem is also present in the default sample app created by Visual Studio. At least this is the case for me when running under Windows 10,21H1 (in VS2008 & VS2013).

The only thing you have to do, after creating a "new Win32 Project", is change the background color of the window class, e.g., to the color red:

    //wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.hbrBackground = (HBRUSH) CreateSolidBrush(RGB(255, 0, 0));

And then add a WM_ERASEBKGND with a Sleep to the 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);

The Sleep exaggerates the problem, causing the white background to show for at least a second. After that the red background is drawn as expected.

I'm including a short video when running the App with those changes.

For any app, it looks quite unprofessional that the window flashes white before rendering, especially if the interface is dark. So my question is: What is causing this behavior? The background color is set through RegisterClassEx and passed to CreateWindow, before calling ShowWindow(..) So Windows should know that the background color is red. So why does it render it white? Am I missing something?

Ideally, I would like to change this initial background color to something other than white, such as black. But how? I've tried drawing to the window before calling ShowWindow, without luck.

Ralf
  • 111
  • 1
  • 8
  • If there is flickering in your actual program, it probably happens in `WM_PAINT`. Show a minimum version of your `WM_PAINT` routine, it may need double buffering, or it's not clipping child windows. You can also get `WM_ERASEBKGND` to return 1, so that all painting is done in `WM_PAINT` – Barmak Shemirani Oct 26 '21 at 02:34
  • 1
    I have seen this (or something similar) before, and I think I solved it by showing the window using `SetWindowPos` with the `SWP_NOREDRAW` flag (rather than `ShowWindow`) and then forcing a redraw with `RedrawWindow`. – Jonathan Potter Oct 26 '21 at 03:22
  • Thanks @BarmakShemirani, I know, but WM_PAINT is just plain sample app, as show in the code above, and the flash happens before WM_PAINT is even reached. – Ralf Oct 26 '21 at 07:31
  • 1
    Thanks @JonathanPotter!! I couldn't get get it to work without ShowWindow, but adding a `SetWindowPos(hWnd, NULL, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREPOSITION);` just after CreateWindow(..) actually seems to fix the issue! Without it, WM_ERASEBKGND and WM_PAINT is reached *after* the window is shown, but with the SetWindowPos in place, they are reached *before* the window is shown. Or so it seems. I don't understand why though, and will have to test it some more. Also, I will try to work out how to accept your answer, if this is indeed does the trick. – Ralf Oct 26 '21 at 07:58
  • As far as I'm concerned you could refer to the thread:https://stackoverflow.com/questions/50562616/why-make-the-initial-call-to-showwindow The initial call to ShowWindow() in the body of WinMain() before executing the message loop. – Jeaninez - MSFT Oct 26 '21 at 08:41
  • 1
    @JonathanPotter: I take the *before* / *after* statements back. The order of execution is unchanged by adding the SetWindowPos. I tested it with breakpoints instead of using Sleep. What instead seems to happen, is that the time delay between the call to ShowWindow and the WM_ERASEBKGND message gets shorter with the SetWindowPos in place. Hence, this *seems* to work since it hides the flash, but the underlying problem is still there: The window is initialized to white color. So say, I had some extra initialization or had a slow erasebackground function, then the problem would persist. – Ralf Oct 26 '21 at 12:37
  • It doesn't sound like you tried what was suggested: `SetWindowPos(hWnd, NULL, 0,0,0,0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW);` – mnistic Oct 26 '21 at 12:49
  • 1
    Thanks @mnistic, but using that line instead of ShowWindow, the window just shows up all white, and WM_ERASEBKGND or WM_PAINT is never called (at least not until invalidated). This is the opposite of what I want. I want it to appear initially with the hbrBackground color, i.e. non-white. – Ralf Oct 26 '21 at 13:07
  • Thanks @Jeaninez-MSFT, I'm not sure I understood your suggestion, but I've looked at that thread and using WS_VISIBLE instead of ShowWindow does not seem to help on this issue. – Ralf Oct 27 '21 at 10:17
  • 1
    I should mention that I've tried different styles of CreateWindowEx to try to force Windows into clearing the background _before_ showing the window. This includes using CS_OWNDC and WS_EX_COMPOSITED (which actually removes the white background by making the window transparent, but seems to come with its own set of downfalls), and try to render to the window on WM_CREATE. Anyway, none that worked. – Ralf Oct 27 '21 at 10:31
  • And to anyone wondering, Yes, this issue is also on Windows 11 ! – Ralf Oct 27 '21 at 10:31
  • @Ralf Actually `WS_EX_COMPOSITED` saved me! It completely solved the issue for my window. I'm using custom non-Microsoft dark theme, so window looked really ugly when it flushed white at launch. I think that double buffering solved this for me. – ScienceDiscoverer Jul 03 '22 at 08:01

6 Answers6

6

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;
}
mnistic
  • 10,866
  • 2
  • 19
  • 33
  • Thanks @mnistic! Great idea hijacking `WM_SHOWWINDOW` like this, and it indeed seems to work! Making the window transparent and then faking a `WM_ERASEBKGND` is pretty clever. And while I'm not a big fan of animation, it looks like that can simply be turned off using: `AnimateWindow(hWnd, 1, AW_ACTIVATE|AW_BLEND);` However, interestingly, adding a `case WM_ERASEBKGND: return TRUE;` now makes the client DC show up black regardless of hbrBackground. What could be the explanation for that? Anyway, I like it and will be testing it some more now! – Ralf Oct 31 '21 at 20:55
  • Who knows? Probably another bug... :) FYI you can probably just turn off the layered style after showing the window, instead of setting the alpha to 255, but I haven't tested that. – mnistic Oct 31 '21 at 21:44
  • I've tested this on Windows XP, Windows 7 (with and without Aero), Windows 10, and Windows 11. It works in all cases! – Ralf Nov 01 '21 at 23:16
  • However, strangely enough, I noticed that it doesn't work, if I use `CreateWindowEx` with the `WS_EX_LAYERED` flag, like you do. I copied your full sample code directly (just to verify that I didn't screw up), but likewise I can only get it to work by replacing `WS_EX_LAYERED` with `0` in the `CreateWindowEx` call. Otherwise the blend animation stops working. Doesn't really make much sense to me. For `WS_EX_LAYERED`, I noticed that the doc states: _"This style cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC."_, but even removing `CS_CLASSDC` makes no difference. – Ralf Nov 01 '21 at 23:17
  • I hope that one day Microsoft will fix this issue. Meanwhile, I think this trick is a great workaround, and more elegant than my off-screen hack. There might still be some details to work out regarding WS_EX_LAYERED, but I'm accepting this answer! Thanks again @mnistic! – Ralf Nov 01 '21 at 23:32
  • If anyone has comments, alternative solutions, improvements or other ideas, please do share them. Also, since this seems to be a ~12 year old issue, does someone know if this has been bugged to Microsoft? Otherwise, I'd be happy to file a bug. – Ralf Nov 04 '21 at 12:00
  • `AnimateWindow` is bad idea, as it breaks W10 themes and shows W7-styled title bar. – ScienceDiscoverer Jul 03 '22 at 08:04
  • @ScienceDiscoverer Reference for this claim? – mnistic Jul 20 '22 at 09:00
2

A more controversial answer could be that this is simply a bug in Windows.

For reference, (aside from the existing GIFs from Windows 10 that I already posted) here are recordings of the sample app running with and without background erase in Windows XP, Windows 7 and Windows 11.

Windows XP:

Windows XP: Without WM_ERASEBKGND/WM_PAINT: OK (no white background) enter image description here

Windows XP: With WM_ERASEBKGND: OK (no white background) enter image description here

Windows 7:

Windows 7: Without WM_ERASEBKGND/WM_PAINT: NOT OK (white background) enter image description here

Windows 7: With WM_ERASEBKGND: NOT OK (white background) enter image description here

Windows 7: With WM_ERASEBKGND + Sleep: NOT OK (white background) enter image description here

Windows 7 with Aero disabled:

Windows 7 with Aero disabled: Without WM_ERASEBKGND/WM_PAINT: OK (no white background) enter image description here

Windows 7 with Aero disabled: With WM_ERASEBKGND: OK (no white background) enter image description here

Windows 7 with Aero disabled: With WM_ERASEBKGND + Sleep: OK (no white background) enter image description here

Windows 11 (with Animation disabled):

Windows 11: Without WM_ERASEBKGND/WM_PAINT: NOT OK (white background) enter image description here

Windows 11: With WM_ERASEBKGND: OK (no white background) enter image description here

Windows 11: With WM_ERASEBKGND + Sleep: NOT OK (white background) enter image description here

I've added Sleep to tests where it was hard to see the issue.

To sum up:

  • Windows XP: No issue. Everything seems to work as expected.
  • Windows 7: Issue occurs when Aero is enabled (Windows 7 theme), but not when it is disabled (Classic theme).
  • Windows 10: Issue occurs for all tests.
  • Windows 11: Issue occurs, but works without Sleep added. Most likely since this was running on a faster machine.

So although I cannot conclude anything solid from these tests, it does look like this behavior was introduced in Windows 7 with Aero.

If someone can debunk this claim, please comment below.

Ralf
  • 111
  • 1
  • 8
2

I've hit this issue recently. I tried mnistic's solution using layered windows and transparency but it caused problems with rendering pane captions in the MFC app I'm working on. However, I've found a simple solution which appears to work nicely without any need for animation, changing window styles, etc.:

The Desktop Window Manager API enables a window to be "cloaked", so that it isn't shown on the screen but is still internally composited, i.e. still accumulates the results of drawing operations. You can turn "cloaking" on by:

BOOL cloak = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_CLOAK, &cloak, sizeof(cloak));

To avoid the white flash on first showing the window, do the above before calling ShowWindow(). Then do the initial UpdateWindow() to get correct content drawn. Finally, turn "cloaking" off using:

BOOL cloak = FALSE;
DwmSetWindowAttribute(hwnd, DWMWA_CLOAK, &cloak, sizeof(cloak));

to get the final window content displayed.

This should work on all Windows versions which have a Desktop Windows Manager, so Windows Vista and up.

  • Thanks so much @PaulOfTheCoders for your contribution to this thread! Interesting! I did not know about dwmapi. Looking into the **DWMWA_CLOAK** documentation, I notice that it is stated that "Windows 7 and earlier: This value is not supported.": [link](https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute/#:~:text=Windows%207%20and%20earlier:%20This%20value%20is%20not%20supported.) So I doubt this will work on Windows Vista and up as mentioned in the post. – Ralf Nov 19 '22 at 20:43
  • Regardless, I've tested your suggestion, and it does indeed work very nicely under Windows 10. Great!! Under Windows 7, it doesn't work for me, but it doesn't hurt either, i.e. the app still starts up, even though the cloaking isn't working. On Windows XP, the app will fail to start due to the dwmapi.dll linkage. To summarize: I think this is a very elegant solution, if you can live with reliance on dwmapi and without compatibility for Windows 7 and below. Very cool! Thanks again. Much appreciated! – Ralf Nov 19 '22 at 20:45
  • You're absolutely right; it doesn't work on Windows 7. I hadn't had a chance to test on that when I posted and must have missed this detail when first reading the docs. Re reliance on dwmapi.dll, you can use LoadLibrary() to load the DLL after application start-up and call DwmSetWindowAttribute() (using GetProcAddress() to get its function pointer) only if the DLL can be loaded. Then the application won't be absolutely reliant on its existence to be able to start up. – PaulOfTheCoders Nov 21 '22 at 00:26
1

I did some more testing, and want to post a potential answer to this question. Now, this is mainly based on the suggestion by @JonathanPotter, so full credit to him. And while it doesn't really fix the problem, it does alleviate it quite a bit.

Now, ideally, it would be great if Windows would simply render the window with the correct initial background color, but no matter how hard I've tried, I can only get it to update the background color by utilizing WM_ERASEBKGND or WM_PAINT.

So it seems that the time delay between showing the window (i.e. using ShowWindow), and the actual clearing of the background (WM_ERASEBKGND) is the crux of the problem. Hence, it makes sense to profile it. I've done so by recording the time difference between calling ShowWindow and reaching WM_ERASEBKGND using QueryPerformanceCounter.

So on an i7-4960HQ CPU @ 2.60GHz running Window 10, the time between ShowWindow and WM_ERASEBKGND is between 100 - 317ms. It fluctuates quite a bit. This is with a vanilla Win32 Sample App, built in Release without any Sleeps or anything like that, but using a red hbrBackground to show the issue. This means that the white background is clearly visible for a few frames before the red background is drawn. Here is an animated gif captured @ 25Hz: without_SetWindowPos The white background is visible for 3 frames in that animation.

Now the potential fix is to use a combination of SetWindowPos and RedrawWindow before showing the window.

For my tests I simply added these two lines before calling ShowWindow(..):

   SetWindowPos(hWnd, NULL, 0,0,0,0,   SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW);
   RedrawWindow(hWnd, NULL, 0, RDW_INVALIDATE |  RDW_ERASE);

Although RedrawWindow does not seem to make any difference. Profiling again, the time between ShowWindow and WM_ERASEBKGND is now 10 - 23ms. A 10x speed-up!

Again, an animated gif captured (with SetWindowPos) @ 25Hz: with_SetWindowPos This clearly shows that the flash of white background is gone, and thus the problem is fixed. It's like night and day.

Now, I would argue that this is not a fix, but rather a workaround. Since the underlying problem of Windows using a white background color is still there. And since this is a timing issue, I can easily imagine that the white background could show up again, say if the system was sluggish or busy doing other stuff. Similarly, having a faster system means you that you are less likely to see this in the first place, effectively hiding the issue. But simply setting a breakpoint in WM_ERASEBKGND will still show you a white window.

Also, I have no explanation for the speed-up. I tracked the number of messages in the message pump, and they are the same in both scenarios.

Now, I'm still hoping for a better fix. I find it hard to believe that the Microsoft engineers found it cool to fill all freshly created Windows with a hardcoded 0xFFFFFF, so I'm hoping that this color is actually read from somewhere, and thus possible to change, so the initial background matches the hbrBackground.

Please feel free to post alternative answers, questions, or suggestions. I will of course update this thread if I figure out anything else.

Ralf
  • 111
  • 1
  • 8
1

Did some more poking, so here is a different potential answer.

I realized that even if I completely discard WM_PAINT and WM_ERASEBKGND (i.e., return 0 in WM_PAINT and return TRUE in WM_ERASEBKGND), I can still get the app to draw the red background by manually resizing the window! Here is a clip to illustrate:

resize

This means that Windows does indeed know and respect hbrBackground, which is great! For some odd reason it just doesn't clear it to that, but to white instead.

(Incidentally, I went through all the system colors in the registry (HKEY_CURRENT_USER\Control Panel\Colors HKEY_CURRENT_USER\Control Panel\Desktop\Colors) with a "255 255 255" setting and forcibly changed them to see if that would change the initial white background. But no luck. This makes me conclude that the white background is not a system color.)

Anyway, the above lead me to try to programmatically resize the window after ShowWindow. But since I don't want it to flicker on open, do the ShowWindow off-screen.

So here is the code that would replace regular ShowWindow(..):

    int x0 = GetSystemMetrics(SM_XVIRTUALSCREEN); 
    int x1 = GetSystemMetrics(SM_CXVIRTUALSCREEN); 
    
    RECT rect;
    GetWindowRect(hWnd, &rect); 

    // resize and move off-screen
    SetWindowPos(hWnd, NULL, x1-x0, 0, 0, 0, SWP_NOREDRAW );    
      
    // show window
    ShowWindow(hWnd,nCmdShow);  
    
    // restore and redraw
    SetWindowPos(hWnd, NULL, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0 ); 

Now, I would call this a hack. Yet, it does not rely on WM_ERASEBKGND nor WM_PAINT, so there should be less of a timing issue. Also, the window shows up exactly like a regular ShowWindow(...) would, just with the correct hbrBackground, which is what I wanted.

Here is what it looks like @ 25Hz:

offscreen

Notice that there is no flash of white background.

Please note that I've tried to write the code to cater for virtual desktop/multi-monitor, but haven't actually tested that.

But unfortunately everything is not fine and dandy. As I was writing this answer, I did quite a few trial runs with OBSStudio recording @ 60Hz, and went through the footage. There I found one that simply shows trash inside the window frame on open (apparently from Chrome), for just one frame. Here is a slowed-down replay:

replay

I'm stumped. Perhaps that is the real issue ?

Ralf
  • 111
  • 1
  • 8
  • 1
    FWIW Microsoft's own applications suffer from this issue: https://www.reddit.com/r/Windows10/comments/9xm2rs/the_problem_of_the_white_flash_in_the_file – mnistic Oct 29 '21 at 14:26
  • 1
    Thanks so much @mnistic, I wasn't aware of that. But I must admit that I've seen it in several pieces of software. It's especially noticeable in apps with a dark theme as pointed out in that link. I'm leaning more and more towards this being a bug in Windows. – Ralf Oct 29 '21 at 20:56
0

I came across exact same problem and solved it by firstly showing window in minimized state and then restoring it.

So instead of:

ShowWindow(hwnd, SW_SHOW);

I used:

ShowWindow(hwnd, SW_SHOWMINIMIZED);
ShowWindow(hwnd, SW_RESTORE);

No white background.

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77