What is the inverse of AdjustWindowRect and AdjustWindowRectEx?


We saw over a decade ago (my goodness I've been doing this way too long) that the Adjust­Window­Rect and Adjust­Window­Rect­Ex functions do not take menu wrapping into account because they don't take a window handle parameter, so they don't know what menu to test for wrapping. Still, they are useful functions if you aren't worried about menu wrapping because they let you do window size calculations without a window handle (say, before you create your window).

But those functions take a proposed client rectangle and return the corresponding non-client rectangle by inflating the rectangle by the appropriate borders, caption, scroll bars, and other non-client goo. But how do you go the other way? Say you have a proposed window rectangle and you want to know what client rectangle would result from it?

Adjust­Window­Rect and Adjust­Window­Rect­Ex can do that too. You just have to apply a negative sign.

The idea here is that we use the Adjust­Window­Rect­Ex function to calculate how much additional non-client area gets added due to the styles we passed. To make the math simple, we ask for a zero client rectangle, so that the resulting window is all non-client.

−10 0 +10
−50   |  
     
 
0 —
+10      

We pass in the empty rectangle represented by the dot in the middle, and the Adjust­Window­Rect­Ex expands the rectangle in all dimensions. We see that it added ten pixels to the left, right, and bottom, and it added fifty pixels to the top. (Numbers are for expository purposes. Actual numbers will vary.)

From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.

BOOL UnadjustWindowRectEx(
    LPRECT prc,
    DWORD dwStyle,
    BOOL fMenu,
    DWORD dwExStyle)
{
  RECT rc;
  SetRectEmpty(&rc);
  BOOL fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
  if (fRc) {
    prc->left -= rc.left;
    prc->top -= rc.top;
    prc->right -= rc.right;
    prc->bottom -= rc.bottom;
  }
  return fRc;
}

Note that the top and left are subtracted, so that the two negative signs cancel out.

Comments (17)
  1. henke37 says:

    That doesn't sound like the function doing the inversion…

  2. Anon says:

    @henke37

    My psychic commenting powers tell me you're engaging in pedantry.

  3. Douglas says:

    @xor88 But then your helper function has to perform memory allocation. In C# one doesn't care about memory ownership (i.e. who is responsible for deleting it), but in C one must. It is beneficial for simple helper methods to not participate in the management of memory. In this case memory management is left to the caller.

  4. Jim says:

    The C version could return a copy without performing memory allocation, by returning the struct by value. In C++ you even have the return value optimization, which eliminates the copy on return (and I believe the copy from the return value into a new stack object can also be elided). In this case the copy would have only been four words anyway.

    Of course this isn't always a solution. If you have a bigger structure, the function is being called in a tight loop, and the RVO doesn't save you (e.g. if you're updating an existing object instead of creating a new one on the stack), then returning by value may not be sensible. In this particular case these are unlikely to apply to any caller, but we'd need a time machine anyway.

  5. The RECT functions typically accept separate input and output parameters. Here's a more idiomatic implementation:

    BOOL UnadjustWindowRectEx(

       _Out_ LPRECT pShrunk,

       LPCRECT pOriginal,

       DWORD dwStyle,

       BOOL fMenu,

       DWORD dwExStyle)

    {

     RECT rc;

     SetRectEmpty(&rc);

     BOOL fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);

     if (fRc) {

       pShrunk->left = pOriginal->left – rc.left;

       pShrunk->top = pOriginal->top – rc.top;

       pShrunk->right = pOriginal->right – rc.right;

       pShrunk->bottom = pOriginal->bottom – rc.bottom;

     }

     return fRc;

    }

    This would (equally idiomatically) be called by passing the same pointer to the first two args:

    RECT rc = …;

    // shrink rectangle

    if (!UnadjustWindowRectEx(&rc, &rc, …)) { … }

    // rectangle is now shrunk

    This makes it clear that no memory is being allocated, and that the RECT is being modified in-place. It does place a burden on the called function not to read from the input after it's written to the output, since they might be the same RECT!

  6. > The RECT functions typically accept separate input and output parameters

    A counterexample to this general trend is AdjustWindowRectEx itself, which takes a single input/output parameter. So it makes sense for UnadjustWindowRectEx to do the same.

  7. AsmGuru62 says:

    I bet TCM_ADJUSTRECT was invented after developers learned (from AdjustWindowRect) that opposite operation also sometimes needed.

  8. MV says:

    So…. we're taking this interesting tidbit of math as a chance to observe that two different languages, with different expectation of how memory is handled, end up with different "best practices" and that neither is a good fit for the other.  Better late than never, I suppose.

  9. Nick says:

    > We saw over a decade ago (my goodness I've been doing this way too long)

    Then this is probably a good time to say thank you for ten years of consistent and consistently high quality writing.  I can only hope that Microsoft sees the immense value in your blog archives (as well as others at Microsoft) and ensures it stays online and available even long after you stop writing.

    Coincidentally, Eric Lippert also just celebrated (perhaps "recognized" is a better word!) his 10th anniversary of blogging.  Both of you are fantastic writers and your sites a pleasure to read.  Thank you!

  10. xor88 says:

    Having used C# for years in a functional style, viewing idiomatic C is quite a contrast. In C# I'd probably have created a new rectangle and returned directly that expression, or put it into a fresh variable. I wouldn't have modified *prc or overwritten it.

    Even if I had wanted to use the return-value-by-out-param style I'd have create a fresh rect and assigned it to *prc instead of mutating its members.

  11. Marcel K. says:

    > We saw over a decade ago (my goodness I've been doing this way too long)

    Which means that you've been part of my daily routine for 10 years as I haven't missed a single article since… wow, that's a weird thought! Can't imagine the withdrawal symptoms should you ever decide to stop… so cheers for the next 10 years :-)

  12. Sven2 says:

    But what if the window manager one day decides to use different styles of window decoration for different window sizes? E.g., it could place the title bar on the side for very narrow windows. Or the close button in the center if the window is exactly square. Then your UnadjustWindowRectEx would be wrong.

  13. Neil says:

    The other approach is to simply create a window of the desired size and style and use ClientToScreen and GetClientRect on it.

  14. Brian_EE says:

    @Sven2: "Then your UnadjustWindowRectEx would be wrong."

    Then wouldn't AdjustWindowRectEx return the correct coordinates for the "new" style? If so, then I fail to see how the Unadjust code would be wrong.

  15. Damien says:

    @Brian_EE

    I think Sven was saying that different styles might be used – so the non-client area for a square, 0x0 window may have a different "shape" (be narrower or wider, in general) than the non-client area for a window of whatever size was passed to UnadjustWindowRectEx.

  16. 640k says:

    @Damien: In the same way Windows doesn't support transactions for the file system any longer, Windows doesn't support transactions for the windowing system either. Windows does never guarantee that the border size and other system global decoration settings hasn't changed between the Adjust­Window­Rect* and CreateWindows* functions calls.

  17. kero says:

    >Note that the top and left are subtracted, so that the two negative signs cancel out.

    Dear Raymond, happy to read your article.

    This way of representing the geometry of the window, I also think the best.

    So use myself in my native utilities for many years, here are the screenshots for examples:

    http://www.geocities.ws/…/htspy.png (see "w-cl: [3,22, -3, -3]"),

    http://www.geocities.ws/…/printlayered.jpg (see "rtdif: [4,23, -4, -4]").

Comments are closed.