So I'm trying to create a window that only shows its borders and have the rest of the body be see through. I've created a mockup of what that would look like in my head:
Transparent window, only displaying a border and showing whatever would be underneath it
I tried blitting in a buffer with transparent pixels but that did not have the desired effect.
Any ideas ?
-
3WS_EX_LAYERED with a color key: msdn.microsoft.com/en-us/library/ms997507.aspxHans Passant– Hans Passant2018年03月20日 09:39:15 +00:00Commented Mar 20, 2018 at 9:39
-
1@HansPassant: This is really only half a solution. It doesn't explain, how to solve the hard part: Which key color do you use, so as to prevent parts of the non-client area from turning transparent? And given the requirements spelled out, a layered window needlessly wastes resources here.IInspectable– IInspectable2018年03月20日 09:48:47 +00:00Commented Mar 20, 2018 at 9:48
-
You choose the colors your window uses. So you pick the color you want to be transparent. Fuschia is a common choice, for example. And Layered windows are designed for performance and efficiency, they will not "waste resources".Remy Lebeau– Remy Lebeau2025年01月22日 15:38:07 +00:00Commented Jan 22, 2025 at 15:38
1 Answer 1
This is possible by passing the WS_EX_NOREDIRECTIONBITMAP 1 extended window style to a call to CreateWindowEx. This prevents the system from allocating a render surface for the window's client area, leaving the client area completely transparent.
Note, that this does not make the window transparent to mouse clicks. Hit testing is still governed by the window, even if it doesn't have a visible client area.
The following code provides a minimal code sample that showcases the use:
#define UNICODE
#include <Windows.h>
#pragma comment(lib, "user32.lib")
int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int) {
WNDCLASSW wc{};
wc.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpszClassName = L"TransparentWindow";
wc.lpfnWndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT
{
switch (message) {
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
default:
return ::DefWindowProcW(hWnd, message, wParam, lParam);
}
};
::RegisterClassW(&wc);
::CreateWindowExW(WS_EX_NOREDIRECTIONBITMAP, wc.lpszClassName, L"Transparent window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, hInstance, nullptr);
MSG msg{};
while (::GetMessageW(&msg, nullptr, 0, 0) > 0) {
::DispatchMessageW(&msg);
}
return msg.wParam;
}
This produces output similar to the following screenshot:
Screenshot of sample application
More information on the internals, as well as a common use case can be found in Kenny Kerr's excellent June 2014 MSDN Magazine article Windows with C++ : High-Performance Window Layering Using the Windows Composition Engine.
1 This requires desktop composition to be enabled. Desktop composition is available in all supported versions of Windows, but can be disabled by the user/system administrator prior to Windows 8.
12 Comments
CS_HREDRAW | CS_VREDRAW necessary? I removed it and didn't notice any adverse effect. IMO it just generates needless WM_PAINT messages.CS_HREDRAW | CS_VREDRAW in his example, but he doesn't explain why.WM_PAINT messages on resize doesn't make sense. The reason I used those class styles is, because I didn't put any thought into a line of code I must have written a few million times. Kenny Kerr is using those styles presumably because his code eventually is rendering to a Direct3D swap chain. I updated the code in this answer, though, and removed the unnecessary class styles.