5

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 ?

  • 2
    WS_EX_LAYERED with a color key: https://msdn.microsoft.com/en-us/library/ms997507.aspx – Hans Passant Mar 20 '18 at 09: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 Mar 20 '18 at 09:48

1 Answers1

13

This is possible by passing the WS_EX_NOREDIRECTIONBITMAP1 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.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • @JonathanPotter: I believe that is true. Starting with Windows 8, you cannot turn off desktop composition. And given that Windows 7 SP1 has been on [extended support](https://support.microsoft.com/en-us/help/13853/windows-lifecycle-fact-sheet) for more than 3 years, I didn't think of spelling this out explicitly. It still warrants a note, though. – IInspectable Mar 20 '18 at 10:08
  • @IInspectable Thanks for the notes, it won't be a problem for me since I use the Desktop Duplication API, which doesn't run on anything lower than Windows 8. – justin kohlenberg Mar 20 '18 at 10:28
  • Is `CS_HREDRAW | CS_VREDRAW` necessary? I removed it and didn't notice any adverse effect. IMO it just generates needless `WM_PAINT` messages. – zett42 Mar 20 '18 at 18:47
  • I see that Kenny Kerr has used `CS_HREDRAW | CS_VREDRAW` in his example, but he doesn't explain why. – zett42 Mar 20 '18 at 18:54
  • @zett42: Those class styles are indeed not necessary. When there is nothing to draw, requesting `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. – IInspectable Mar 20 '18 at 20:22
  • The wonderful _ScreenToGif_ utility is another example of this approach (or, at least, some approach with equivalent results) in action. – Lightness Races in Orbit Mar 20 '18 at 20:24
  • @IInspectable is it possible to remove also the title bar? There should be a rectangle. – Latif Uluman Apr 16 '20 at 09:43
  • @lat: Of course you can remove the title bar. You just have to apply the appropriate [window styles](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles). `WS_CAPTION` controls, whether a window has a title bar. – IInspectable Apr 16 '20 at 10:35
  • @IInspectable I understand that if I give the WS_CAPTION to the style, the title bar is shown. I tried your above code, but it also has no this style. But still title bar is shown. What is the point I misunderstood? – Latif Uluman Apr 16 '20 at 10:42
  • @lat: The code in this answer uses the `WS_OVERLAPPEDWINDOW` style, which is a combination of styles, including `WS_CAPTION`. The documentation lists all window styles. – IInspectable Apr 16 '20 at 10:57
  • @IInspectable I read the documentation. When I use a style that does not use WS_CAPTION , all of the window becomes transparent even the borders. So nothing shown. What I want is only a rectangle which is the borders of the window. I could not find it on the documantation. – Latif Uluman Apr 16 '20 at 11:09
  • @lat: I cannot comment on code I cannot see. If you don't know how to create a window with only a border but no caption bar, consider [asking a question](https://stackoverflow.com/questions/ask). Unless [this question](https://stackoverflow.com/q/39731497/1889329) or [this one](https://stackoverflow.com/q/7442939/1889329) works for you. – IInspectable Apr 16 '20 at 11:42