2

I have a png image which has a transparent background and I want to use it as a background for a layered window. The goal is to be able to see through the window in the transparent sections of the image (even if the window is moved or the desktop behind it changes).

I tried lots of stuff, but I only managed to draw what is behind the window (when the window is created the image and the desktop behind is displayed but when the window is moved the transparent background no longer works)

I already checked Creating a transparent window in C++ Win32 and create transparent image from png, winapi but I cannot reproduce similar results

Minimal code example :

I use the following function for UpdateLayeredWindow from How To Use UpdateLayeredWindow

void display(HWND hwnd, const wchar_t* path)
{
    // Load our PNG image
    CImage img;
    img.Load(path);
    // Get dimensions
    int iWidth = img.GetWidth();
    int iHeight = img.GetHeight();
    // Make mem DC + mem  bitmap
    HDC hdcScreen = GetDC(NULL);
    HDC hDC = CreateCompatibleDC(hdcScreen);
    HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, iWidth, iHeight);
    HBITMAP hBmpOld = (HBITMAP)SelectObject(hDC, hBmp);
    // Draw image to memory DC
    img.Draw(hDC, 0, 0, iWidth, iHeight, 0, 0, iWidth, iHeight);

    // Call UpdateLayeredWindow
    BLENDFUNCTION blend = { 0 };
    blend.BlendOp = AC_SRC_OVER;
    blend.SourceConstantAlpha = 255;
    blend.AlphaFormat = AC_SRC_ALPHA;
    POINT ptPos = { 0, 0 };
    SIZE sizeWnd = { iWidth, iHeight };
    POINT ptSrc = { 0, 0 };
    UpdateLayeredWindow(hwnd, hdcScreen, &ptPos, &sizeWnd, hDC, &ptSrc, 0, &blend, ULW_ALPHA);

    SelectObject(hDC, hBmpOld);
    DeleteObject(hBmp);
    DeleteDC(hDC);
    ReleaseDC(NULL, hdcScreen);
}

To create the window :

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
     SetProcessDPIAware();

     GdiplusStartupInput gpStartupInput;
     ULONG_PTR gpToken;
     GdiplusStartup(&gpToken, &gpStartupInput, NULL);

     const wchar_t CLASS_NAME[] = L"Sample Window Class";

     WNDCLASS wc = { };

     wc.lpfnWndProc = WindowProc;
     wc.hInstance = hInstance;
     wc.lpszClassName = CLASS_NAME;

     RegisterClass(&wc);

     // Create the window.

     hwnd = CreateWindowEx(
         0,                              // Optional window styles.
         CLASS_NAME,                     // Window class
         L"Learn to Program Windows",    // Window text
         WS_POPUP | WS_EX_TOPMOST | WS_EX_LAYERED,            // Window style

         // Size and position
         CW_USEDEFAULT, CW_USEDEFAULT,
         700, 700,

         NULL,       // Parent window    
         NULL,       // Menu
         hInstance,  // Instance handle
         NULL        // Additional application data
     );
     if (hwnd == NULL)
     {
         return 0;
     }


     ShowWindow(hwnd, nCmdShow);
     UpdateWindow(hwnd);

     MSG msg;

     while (GetMessage(&msg, NULL, 0, 0))
     {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
     }

     GdiplusShutdown(gpToken);

     return 0;
}

And in the message handler of the window :

case WM_CREATE:
{
     const wchar_t* path = L"path_to_png_image";
     display(hwnd, path);
}
break;

case WM_PAINT:
{
//doing nothing
}
break;

The problem of that code is simply that nothing is drawn : I just get a fully transparent window. Also, I checked and WM_PAINT is called even if I don't use SetLayeredWindowAttributes

EDIT :

The solution as pointed in the commentaries is to set WS_EX_TOPMOST | WS_EX_LAYERED as the first parameter of CreateWindowEx :

hwnd = CreateWindowEx(
         WS_EX_TOPMOST | WS_EX_LAYERED,   // Optional window styles.
         CLASS_NAME,                     // Window class
         L"Learn to Program Windows",    // Window text
         WS_POPUP,            // Window style

         // Size and position
         CW_USEDEFAULT, CW_USEDEFAULT,
         700, 700,

         NULL,       // Parent window    
         NULL,       // Menu
         hInstance,  // Instance handle
         NULL        // Additional application data
     );
Ben W
  • 23
  • 10
  • Check that the path you're providing is right. Don't forget you need to use double-backslashes in string literals. – Jonathan Potter Jun 10 '20 at 23:32
  • Though `WM_PAINT` is called but we can't see what you do in `WM_PAINT`. And put Extended Window Styles `WS_EX_TOPMOST | WS_EX_LAYERED` in the first parameter of `CreateWindowEx`. – Rita Han Jun 11 '20 at 09:55
  • @JonathanPotter I checked the path and it is correct (and within a correct format) – Ben W Jun 11 '20 at 10:17
  • When you move the window, the windows behind it will not be repainted because your window occupies the area. In other words, I think it is not possible to have background with transparent parts. – i486 Jun 11 '20 at 10:20
  • @RitaHan-MSFT I edit my post to add the `WM_PAINT` in which I do absolutely nothing. From what I have read when I use UpdateLayeredWindow without SetLayeredWindowAttributes `WM_PAINT` is no longer called. Which does not appear to be true here and I don't understand why – Ben W Jun 11 '20 at 10:22
  • As pointed by @RitaHan-MSFT `WS_EX_TOPMOST | WS_EX_LAYERED` must be the first parameter. With that modification it perfeclty works ! Thanks – Ben W Jun 11 '20 at 10:42

0 Answers0