Is it possible to create a winapi window with only borders
Asked Answered
T

1

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 ?

Twyla answered 20/3, 2018 at 9:31 Comment(2)
WS_EX_LAYERED with a color key: msdn.microsoft.com/en-us/library/ms997507.aspxFreehearted
@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.Eddi
E
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.

Eddi answered 20/3, 2018 at 9:36 Comment(12)
@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 for more than 3 years, I didn't think of spelling this out explicitly. It still warrants a note, though.Eddi
@Eddi 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.Twyla
Is CS_HREDRAW | CS_VREDRAW necessary? I removed it and didn't notice any adverse effect. IMO it just generates needless WM_PAINT messages.Harquebusier
I see that Kenny Kerr has used CS_HREDRAW | CS_VREDRAW in his example, but he doesn't explain why.Harquebusier
@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.Eddi
The wonderful ScreenToGif utility is another example of this approach (or, at least, some approach with equivalent results) in action.Fluid
@Eddi is it possible to remove also the title bar? There should be a rectangle.Lilongwe
@lat: Of course you can remove the title bar. You just have to apply the appropriate window styles. WS_CAPTION controls, whether a window has a title bar.Eddi
@Eddi 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?Lilongwe
@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.Eddi
@Eddi 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.Lilongwe
@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. Unless this question or this one works for you.Eddi

© 2022 - 2024 — McMap. All rights reserved.