If you put your window against a white background, take a screenshot and measure at which distance you get a perfectly white pixel you could be surprised. If your window isn't selected, the shadow will be different (visually, I don't know if the rectangle size will change).
What I measured was:
Here is a screenshot with the border rectangle. Only the edge pixels are completely white. The size you get from GetWindowRect is probably bigger than the ones I measured. Don't use my numbers in your code, they are just an illustration and depending on which version of Windows you use, you'll get different results.
Wait, so the entire white border represents how big the drop shadow is and the reason I think it's smaller is because of the decrease in alpha?
So I used the function DwmGetWindowAttribute(wnd, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT))
and it worked great. But I want to calculate for a given rect, how much bigger it must be to account for the drop shadow.
You want to create a window, with a client area big enough to encapsulate another window (including the title bar and shadow) ?
Isn't it what your original code did, except you need to call GetWindowRect
on the other window and pass that to AdjustWindowRect
?
HWND small_window = CreateWindow(...); GetWindowRect(small_window, &rect); AdjustWindowRect(&rect, style, 0); HWND big_window = CreateWindow(...); SetWindowPos( big_window, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0));
If it's not, can you explain in detail what you want, without expecting previous knowledge from our part, and if possible provide a simple application we can compile and try things on ?
Yeah, sorry for being unclear. Let's say I have a desired size (not counting the drop shadow) for my window. I can't call DwmGetWindowAttribute
because my window isn't at that size yet, so I must first SetWindowPos
, then call DwmGetWindowAttribute
, and then SetWindowPos
it again. Because unlike AdjustWindowRect
which only takes a rect and style (without any HWND
), DwmGetWindowAttribute
requires you to pass in an HWND
and implicitly use its size. Here's an example:
RECT rect = ...; SetWindowPos(wnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); RECT smallRect; DwmGetWindowAttribute(wnd, DWMWA_EXTENDED_FRAME_BOUNDS, &smallRect, sizeof(RECT)); // No drop shadow rect.left -= smallRect.left - rect.left; rect.top -= smallRect.top - rect.top; rect.right += rect.right - smallRect.right; rect.bottom += rect.bottom - smallRect.bottom; SetWindowPos(wnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
Is there any more function like AdjustWindowRect
where I can give it arbitrary sizes and let it do its calculation?
I'm sorry if I don't understand correctly, but if you have a "desired size" for the window, why do you need DwmGetWindowAttribute
? That would just give you the size you already know.
Is there any more function like AdjustWindowRect where I can give it arbitrary sizes and let it do its calculation ?
If you know the window rect: use it.
If you know the client rect and want the window rect: use AdjustWindowRect.
If you have a window handle and want the client rect: use GetClientRect.
If you have a window handle and want the drop shadow rect: use GetWindowRect.
If you have a window handle and want the window rect: DwmGetWindowAttribute.
In your example code, I believe (I might be mistaken) that rect
and smallRect
are equal so anything after the second line doesn't change anything.
No, rect
and smallRect
differ because SetWindowPos
includes the drop shadow to the size you pass in (and by "after the second line doesn't change anything" you meant the DwmGetWindowAttribute
line). The window has some size, I want to change it to rect
. But because SetWindowPos
includes the drop shadow in its calculation, the window appears smaller than I wanted. So that's why I must do it like that.
If you don't show the window first, than all the sizes are the same. But this doesn't help.
After doing some testing (next time, please provide a simple code we can compile so I don't have to write it), I think I understand what you're saying.
The difference in size is not the drop shadow, it's the invisible window resize handles. Below you'll find some code to create a window that "looks like" it is the size you request. There is a 8 pixel resize handle on the left, right, and bottom. There is a 1 pixel resize handle at the top of the window. So you just need to compensate for those. I used AdjustWindowRect
to compute the size of the handles (except the top one), but it might not be reliable as Microsoft might change how windows look in the future, or the user might have someway to change that.
/* cl main.c -nologo -Zi -Od gdi32.lib user32.lib dwmapi.lib */ #include <windows.h> #include <dwmapi.h> #define Assert(cond) do { if (!(cond)) __debugbreak(); } while (0) static LRESULT CALLBACK WindowProc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam) { LRESULT result = 0; switch ( msg ) { case WM_DESTROY: { PostQuitMessage( 0 ); } break; default: { result = DefWindowProcW( window, msg, wparam, lparam ); } break; } return result; } INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) { WNDCLASSW window_class = { 0 }; window_class.lpfnWndProc = WindowProc; window_class.lpszClassName = L"Dave"; window_class.hCursor = LoadCursorW( 0, ( LPCWSTR ) IDC_ARROW ); ATOM atom = RegisterClassW( &window_class ); Assert( atom ); #if 1 RECT desired_rect = { .left = 200, .top = 200, .right = 400, .bottom = 400 }; RECT dummy_rect = desired_rect; AdjustWindowRect( &dummy_rect, WS_OVERLAPPEDWINDOW, 0 ); /* Those offset are resized handle (invisible). */ int left_offset = desired_rect.left - dummy_rect.left; int right_offset = dummy_rect.right - desired_rect.right; int top_offset = desired_rect.top - dummy_rect.top; /* The top resize handle is a single pixel, and this offset also contains the title bar. */ int bottom_offset = dummy_rect.bottom - desired_rect.bottom; RECT window_rect = desired_rect; window_rect.left -= left_offset; window_rect.right += right_offset; window_rect.top -= 1; window_rect.bottom += bottom_offset; HWND window = CreateWindowExW( 0, L"Dave", L"Dave", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, 0, 0, 0, 0 ); SetWindowPos( window, HWND_TOP, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, SWP_NOZORDER ); #else HWND window = CreateWindowExW( 0, L"Dave", L"Dave", WS_OVERLAPPEDWINDOW, 200, 200, 400, 400, 0, 0, 0, 0 ); RECT rect = { .left = 200, .top = 200, .right = 400, .bottom = 400 }; SetWindowPos( window, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER ); RECT small_rect; DwmGetWindowAttribute( window, DWMWA_EXTENDED_FRAME_BOUNDS, &small_rect, sizeof( small_rect ) ); Assert( rect.left == small_rect.left ); Assert( rect.right == small_rect.right ); Assert( rect.top == small_rect.top ); Assert( rect.bottom == small_rect.bottom ); RECT new_rect = rect; new_rect.left -= ( small_rect.left - rect.left ); new_rect.top -= ( small_rect.top - rect.top ); new_rect.right -= ( rect.right - small_rect.right ); new_rect.bottom -= ( rect.bottom - small_rect.bottom ); SetWindowPos( window, HWND_TOP, new_rect.left, new_rect.top, new_rect.right - new_rect.left, new_rect.bottom - rect.top, SWP_NOZORDER ); Assert( rect.left == new_rect.left ); Assert( rect.right == new_rect.right ); Assert( rect.top == new_rect.top ); Assert( rect.bottom == new_rect.bottom ); #endif ShowWindow( window, SW_SHOW ); BOOL running = 1; while ( running ) { MSG msg; while ( PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ) ) { if ( msg.message == WM_QUIT ) { running = 0; break; } TranslateMessage( &msg ); DispatchMessageA( &msg ); } } return 0; }
If you don't show the window first, than all the sizes are the same. But this doesn't help.
Yeah, I should have been clearer. The window is showing and I want to resize it.
next time, please provide a simple code we can compile so I don't have to write it
My bad. I thought the small code snippet was enough.
Just to be clear, what I'm trying to do is to set the outer rect (not including the drop shadow) of a window to the inner rect (client area) of another window. This was different from what I asked originally (the post with the image of two windows). Originally, I wanted to set the inner rect of a window to the outer rect of another window. After learning about DwmGetWindowAttribute
, I updated my code, and seeing that it worked, I changed my question to:
So I used the function DwmGetWindowAttribute(wnd, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT)) and it worked great. But I want to calculate for a given rect, how much bigger it must be to account for the drop shadow.
This is where your confusion comes from. Sorry about that. I hope we're on the same page now.
The difference in size is not the drop shadow, it's the invisible window resize handles.
What are resize handles? So you're telling me SetWindowPos
doesn't account for the drop shadow but for these invisible handles? If so then why did DwmGetWindowAttribute
work?
What are resize handles?
When you move your mouse near the edge of the window, the cursor change to be an arrow to let you know that if you click you can resize the window. In previous version of windows those were clearly visible as thick borders. Starting with windows 10 (or 8) those borders aren't visible anymore, but they are still part of the window rect.
If you look for WM_NCHITTEST
and other non-client rect (NC means non-client) things you might get a better understanding of how things work.
So you're telling me SetWindowPos doesn't account for the drop shadow but for these invisible handles? If so then why did DwmGetWindowAttribute work?
DwmGetWindowAttribute
seems to report the visible part of the window, not containing the resize handles. I had never used it before today so I don't know much more. I only figure that SetWindowPos
was taking the invisible borders into account by testing today and trying to make sens of it.
But like I said, the code in my previous post isn't reliable. If you run it on Windows 7, since the borders are visible there it will most likely not work.
Just to be clear, what I'm trying to do is to set the outer rect (not including the drop shadow) of a window to the inner rect (client area) of another window.
This should do it.
/* cl main.c -nologo -Zi -Od gdi32.lib user32.lib dwmapi.lib */ #include <windows.h> #include <dwmapi.h> #define Assert(cond) do { if (!(cond)) __debugbreak(); } while (0) static LRESULT CALLBACK WindowProc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam) { LRESULT result = 0; switch ( msg ) { case WM_DESTROY: { PostQuitMessage( 0 ); } break; default: { result = DefWindowProcW( window, msg, wparam, lparam ); } break; } return result; } INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) { WNDCLASSW window_class = { 0 }; window_class.lpfnWndProc = WindowProc; window_class.lpszClassName = L"Dave"; window_class.hCursor = LoadCursorW( 0, ( LPCWSTR ) IDC_ARROW ); ATOM atom = RegisterClassW( &window_class ); Assert( atom ); HWND big_window = CreateWindowExW( 0, L"Dave", L"Dave", WS_OVERLAPPEDWINDOW, 200, 200, 400, 400, 0, 0, 0, 0 ); HWND small_window = CreateWindowExW( 0, L"Dave", L"Dave", WS_OVERLAPPEDWINDOW, 200, 200, 400, 400, 0, 0, 0, 0 ); RECT small_window_rect; GetClientRect( big_window, &small_window_rect ); // Client rect isn't in screenspace. POINT top_left; top_left.x = small_window_rect.left; top_left.y = small_window_rect.top; POINT bottom_right; bottom_right.x = small_window_rect.right; bottom_right.y = small_window_rect.bottom; ClientToScreen( big_window, &top_left ); ClientToScreen( big_window, &bottom_right ); small_window_rect.left = top_left.x; small_window_rect.top = top_left.y; small_window_rect.right = bottom_right.x; small_window_rect.bottom = bottom_right.y; RECT dummy_rect = small_window_rect; AdjustWindowRect( &dummy_rect, WS_OVERLAPPEDWINDOW, 0 ); int left_offset = small_window_rect.left - dummy_rect.left; int right_offset = dummy_rect.right - small_window_rect.right; int top_offset = small_window_rect.top - dummy_rect.top; int bottom_offset = dummy_rect.bottom - small_window_rect.bottom; RECT window_rect = small_window_rect; window_rect.left -= left_offset; window_rect.right += right_offset; window_rect.top -= 1; window_rect.bottom += bottom_offset; SetWindowPos( small_window, HWND_TOP, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, SWP_NOZORDER ); ShowWindow( big_window, SW_SHOW ); ShowWindow( small_window, SW_SHOW ); BOOL running = 1; while ( running ) { MSG msg; while ( PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ) ) { if ( msg.message == WM_QUIT ) { running = 0; break; } TranslateMessage( &msg ); DispatchMessageA( &msg ); } } return 0; }
But isn't the calculation wrong?
RECT dummy_rect = small_window_rect; AdjustWindowRect( &dummy_rect, WS_OVERLAPPEDWINDOW, 0 ); int left_offset = small_window_rect.left - dummy_rect.left; RECT window_rect = small_window_rect; window_rect.left -= left_offset; // Which equals to // window_rect.left = window_rect.left - left_offset // = small_window_rect.left - left_offset // = small_window_rect.left - (small_window_rect.left - dummy_rect.left) // = dummy_rect.left // The same thing can be applied to right/top/bottom
It's not for window_rect.top
as we use 1 instead of top_offset
.
It's seems similar because I used the client rect of the big window to compute the offsets, but you could use any rect because we are only interested by how much AdjustWindowRect
changes the size of the rect we passed it. If you use another rect you can't simplify the expressions.
But after thinking about it its expected, as the client rect of the window on windows 10, as the borders are not visible, is the visual edge of the window. So I think you can just do:
RECT window_rect = small_window_rect; AdjustWindowRect( &small_window_rect, WS_OVERLAPPEDWINDOW, 0 ); window_rect.top = small_window_rect.top - 1;
It's seems similar because I used the client rect of the big window to compute the offsets, but you could use any rect because we are only interested by how much AdjustWindowRect changes the size of the rect we passed it. If you use another rect you can't simplify the expressions.
I don't understand what you're trying to say. Was my math wrong or something?
RECT window_rect = small_window_rect; AdjustWindowRect( &small_window_rect, WS_OVERLAPPEDWINDOW, 0 ); window_rect.top = small_window_rect.top - 1;
But won't this just set the small window to the big window? Because small_window_rect
is the client rect of the big window, so AdjustWindowRect
will return the full rect of the big window.
AdJustWindowRect(&small_window_rect, WS_OVERLAPPEDWINDOW, 0); RECT big_window_rect; GetWindowRect(big_window, &big_window_rect); // small_window_rect == big_window_rect?
This is also my problem with the original code, because if window_rect = dummy_rect
then wouldn't the 2 windows have the same rect? What I want to do is to set the outer rect of the small window to the inner rect of the big window, which means the small window will have a smaller rect.
Just in case I still didn't understand what you wanted, did you run my code ? Isn't it doing what you wanted ?
I don't understand what you're trying to say. Was my math wrong or something?
Your math is correct. What I meant is that to compute the offsets, you can use any rectangle, pass it AdjustWindowRect
and measure the difference. If you do that, than the expressions won't simplify.
/* Compute offsets with unrelated rectangles. */ RECT dummy_rect = { .left = 123, .top = 234, .right = 456, .bottom = 567}; RECT adjusted_rect = dummy_rect; AdjustWindowRect( &adjusted_rect, WS_OVERLAPPEDWINDOW, 0 ); int left_offset = dummy_rect.left - adjusted_rect.left; int right_offset = adjusted_rect.right - dummy_rect.right; int top_offset = dummy_rect.top - adjusted_rect.top; int bottom_offset = adjusted_rect.bottom - dummy_rect.bottom;
The reason the things simplify is because visually the small window is only different at the top edge. The left, right and bottom edges are the same in the two windows.
There was an error in the code snippet I posted (I didn't run that one before posting). AdjustWindowRect
needed to use &window_rect
instead of &small_window_rect
.
RECT window_rect = small_window_rect; AdjustWindowRect( &window_rect, WS_OVERLAPPEDWINDOW, 0 ); window_rect.top = small_window_rect.top - 1;
It produces this result: