31

Given handle of a Win32 window, I need to find position of it relative to its parent window.

I know several functions (e.g.; GetWindowRect() and GetClientRect()), but none of them explicitly return the desired coordinates.

How do I do this?

dbc
  • 104,963
  • 20
  • 228
  • 340
hkBattousai
  • 10,583
  • 18
  • 76
  • 124

5 Answers5

46

The solution is using the combined power of GetWindowRect() and MapWindowPoints().

GetWindowRect() retrieves the coordinates of a window relative to the entire screen area you see on your monitor. We need to convert these absolute coordinates into relative coordinates of our main window area. The MapWindowPoints() transforms the coordinates given relative to one window into relative to another. So we need a "handle" of the screen area and the handle of the parent window of the control which we are trying to find coordinates of. The screen are is a "window" in Windows terminology and it is called "Desktop". We can access the handle of Desktop by the constant HWND_DESKTOP defined in WinUser.h (including Windows.h is enough). And we can get the handle of our parent window simply by calling the Win32 function GetParent(). Now we have all the parameters required to call the MapWindowPoints() function.

RECT YourClass::GetLocalCoordinates(HWND hWnd) const
{
    RECT Rect;
    GetWindowRect(hWnd, &Rect);
    MapWindowPoints(HWND_DESKTOP, GetParent(hWnd), (LPPOINT) &Rect, 2);
    return Rect;
}

MapWindowPoints() is defined as:

int MapWindowPoints(
  _In_     HWND hWndFrom,
  _In_     HWND hWndTo,
  _Inout_  LPPOINT lpPoints,
  _In_     UINT cPoints
);

MapWindowPoints() transform the coordinates relatively from hWndFrom to hWndTo. In our case, we do the transformation from Desktop (HWND_DESKTOP) to our parent window (GetParent(hWnd)). Therefore, the resulting RECT structure holds the relative coordinates of our child window (hWnd) relative to its parent window.

hkBattousai
  • 10,583
  • 18
  • 76
  • 124
  • 2
    Today I used fair amount of time to find a solution to this problem. There are a lot of wrong and misleading information about this matter in the internet and even in this site. It is a big tragedy that Win32 does not give an explicitly function to gather this information, and it is also awful that it is very hard to find a good explanation and example code for the solution of this simple problem. I am sharing the solution here for helping other users that will search for it in the future. – hkBattousai Aug 03 '13 at 16:32
  • 1
    @Xearinox I realize I'm raising the dead here, but... Yes. On multiple monitors, depending on which is the primary display, the method will return either highly negative or highly positive values to represent coordinates relative to the primary display. Try it yourself using `SetCursorPos`, and see where the mouse cursor lands. – RectangleEquals Jun 13 '15 at 08:18
  • if you use it in a class, which subclasses `CWnd` (like `CDialog`), you might want to prefix the sys-function calls with `::` like `::MapWindowPoints(...)` – Gunther Struyf Jan 30 '17 at 11:14
  • Unfortunately this doesn't work if the parent window is minimized. – Jonathan Potter Apr 18 '17 at 03:49
16

that is the solution I am using either for windows or for controls (child windows)

RECT rc;
GetClientRect(hWnd,&rc);
MapWindowPoints(hWnd,GetParent(hWnd),(LPPOINT)&rc,2);
thinlizzy
  • 668
  • 8
  • 13
  • Shouldnt it be MapWindowPoints(hWnd,GetParent(hWnd),(LPPOINT)&rc,1); – Erdinc Ay Oct 21 '14 at 13:26
  • Why would it be that, @Erdinc? There are *two* points in a rect. – Rob Kennedy Nov 07 '14 at 21:12
  • 2
    There is indeed one rect, @Erdinc. And there are two points in it. The final parameter of `MapWindowPoints` expects the number of *points* to map. In fact, if you don't pass 2, the function might not do what you expect in the case of mirrored (right-to-left) windows. See the [remarks in the documentation](http://msdn.microsoft.com/en-us/library/dd145046.aspx). – Rob Kennedy Nov 07 '14 at 21:23
  • Thanks for this! This works great, even on multiple monitors. IMO this should be the accepted answer. – dbeachy1 Jun 11 '15 at 20:20
4

I know it's been answered before, but it's much easier to just get a child window's rect in screen coordinates, get it's position (POINT ptCWPos = {rectCW.left, rectCW.top};) and use the ScreenToClient() function, that will transform the screen coordinate point to the window's client coordinate point:

PS: I know that looks like a lot more code, but most of it is fiddling with the rect position; in most cases one will actually need the rect position rather than the whole rect.


HWND hwndCW, hwndPW; // the child window hwnd
                     // and the parent window hwnd
RECT rectCW;
GetWindowRect(hwndCW, &rectCW); // child window rect in screen coordinates
POINT ptCWPos = { rectCW.left, rectCW.top };
ScreenToClient(hwndPW, &ptCWPos); // transforming the child window pos
                                  // from screen space to parent window space
LONG iWidth, iHeight;
iWidth = rectCW.right - rectCW.left;
iHeight = rectCW.bottom - rectCW.top;

rectCW.left = ptCWPos.x;
rectCW.top = ptCWPos.y;
rectCW.right = rectCW.left + iWidth;
rectCW.bottom = rectCW.right + iHeight; // child window rect in parent window space

  • -1 because this is wrong on mirrored windows; see the Remarks on [`MapWindowPoints`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapwindowpoints). – user541686 Dec 31 '21 at 00:33
0

Here's a very basic typedef struct based on the above answers:

typedef struct tagRectCl {
    static RECT rectOut;
    RECT RectCl(int ctrlID, HWND &hwndCtrl, HWND &ownerHwnd)
    {
    RECT rectIn;
    GetWindowRect(hwndCtrl, &rectIn); //get window rect of control relative to screen
    MapWindowPoints(NULL, ownerHwnd, (LPPOINT)&rectIn, 2);
    rectOut = rectIn;
    return rectOut;
    }
    RECT RectCl(int ctrlID)
    {
       // for rectOut already populated
       return rectOut;
    }
    };
}RectCl;

RECT RectCl::rectOut = {};

The idea being to extend ctrlID over a range of controls, where the storage for the corresponding rectOuts can be considered for a more accommodating structure.
Usage: the following returns the converted rect:

RECT rect1 = RectCl().RectCl(IDC_CTRL1, hWndCtrl, hWndOwner);

The following is a partly coded function that takes elements of both answers into something usable for dialogs - particularly for move/resizing control co-ordinates, the original reason for landing at this page.
It accepts as parameters an integral control ID from a resource or HMENU item from CreateWindow, and a handle for its container.
One should also consider whether ownerHwnd is minimized before calling the function by listening for SIZE_MINIMIZED in WM_SIZE.

BOOL ProcCtrl(int ctrlID, HWND ownerHwnd)
{
    RECT rcClient = {0};        
    HWND hwndCtrl = GetDlgItem(ownerHwnd, ctrlID);
    if (hwndCtrl)
    {
    GetWindowRect(hwndCtrl, &rcClient); //get window rect of control relative to screen
    MapWindowPoints(NULL, ownerHwnd, (LPPOINT)&rcClient,2);

    /* Set extra scaling parameters here to suit in either of the following functions
    if (!MoveWindow(hwndCtrl, rcClient.left, rcClient.top, 
    rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, TRUE))
    {
        //Error;
        return FALSE;
    }
    //if (!SetWindowPos(hwndCtrl, NULL, (int)rcClient.left, (int)(rcClient.top),
    //(int)(rcClient.right - rcClient.left), (int)(rcClient.bottom - rcClient.top), SWP_NOZORDER))
    {
        //Error;
        //return FALSE;
    }
    }
    else
    {
        //hwndCtrl Error;
        return FALSE;
    }
    return TRUE;
}
Laurie Stearn
  • 959
  • 1
  • 13
  • 34
0

Simpler

BOOL CAuxFormView::OnInitDialog()
{
    // run the default
    CDialog::OnInitDialog();
    // define a rectangular
    CRect rc1;
    // get area of the control to the screen coordinates
    m_Viewer_Ctrl.GetWindowRect(rc1);
    // transform the screen coordinates relevant to the Dialog coordinates
    ScreenToClient(rc1);
    // move and refresh the control to the new area
    m_Viewer_Ctrl.MoveWindow(rc1);

}
db-hopper
  • 23
  • 6