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
);