Client rectangle coordinates on screen - c++

How can I get coordinates of a window's client area relative to screen?
I thought about using GetClientRect and ClientToScreen. Also, in a browser window what is ClientRect? Only rectangle with HTML document shown in it, or it includes browser bars and pop-up menus, that can possibly shrink dimension for HTML doc?
I've tried this:
HWND hWnd;
RECT rc;
if (GetClientRect(hWnd, &rc)) // get client coords
{
MapWindowPoints(hWnd, NULL, reinterpret_cast<POINT*>(&rc), 2); // converts rect rc points
return rc.top;
}
But the sad thing is that browser's client rectangle includes all those pop-up browser menus and bars, therefore can't be used to detect accurate coordinates of browsers HTML document space. If anyone got suggestions how it can be done, will try it gladly.

Yes, you can do this with the ClientToScreen function:
RECT rc;
GetClientRect(hWnd, &rc); // get client coords
ClientToScreen(hWnd, reinterpret_cast<POINT*>(&rc.left)); // convert top-left
ClientToScreen(hWnd, reinterpret_cast<POINT*>(&rc.right)); // convert bottom-right
What is the "client" rectangle in a browser depends on the browser implementation. You can use Spy++ to discover this for yourself.

To translate a window's client rectangle to screen coordinates, call the MapWindowPoints function. It implements special handling to always return a valid RECT, even when used in scenarios that involve windows with right-to-left layout:
If hWndFrom or hWndTo (or both) are mirrored windows (that is, have WS_EX_LAYOUTRTL extended style) and precisely two points are passed in lpPoints, MapWindowPoints will interpret those two points as a RECT and possibly automatically swap the left and right fields of that rectangle to ensure that left is not greater than right.
Calling ClientToScreen on both points in contrast fails to account for RTL layouts, and can produce an invalid RECT. It fails to adhere to one of the rectangle coordinate invariants:
The coordinate value of a rectangle's right side must be greater than that of its left side. Likewise, the coordinate value of the bottom must be greater than that of the top.
A reliable function to return a window's client rectangle in screen coordinates would look like this:
RECT client_rect_in_screen_space(HWND const hWnd) {
RECT rc{ 0 };
if (!::GetClientRect(hWnd, &rc)) {
auto const err_val{ ::GetLastError() };
throw std::system_error(err_val, std::system_category());
}
::SetLastError(ERROR_SUCCESS);
if(::MapWindowPoints(hWnd, nullptr, reinterpret_cast<POINT*>(&rc), 2) == 0) {
auto const err_val{ ::GetLastError() };
if (err_val != ERROR_SUCCESS) {
throw std::system_error(err_val, std::system_category());
}
}
return rc;
}
The question update asks for a different, unrelated issue. There is no API built into the system, that allows you to query a web browser's display area for its HTML content. The most promising solution would be to employ UI Automation. The question, however, is too broad to provide a more detailed answer here.

As commented by Raymond Chen, the preferred way of doing this should be something like the following:
inline POINT get_client_window_position(const HWND window_handle)
{
RECT rectangle;
GetClientRect(window_handle, static_cast<LPRECT>(&rectangle));
MapWindowPoints(window_handle, nullptr, reinterpret_cast<LPPOINT>(& rectangle), 2);
const POINT coordinates = {rectangle.left, rectangle.top};
return coordinates;
}

POINT origin;
origin.x = 0;
origin.y = 0;
ClientToScreen(hWnd, &origin);
Now origin is, in screen coords, the top left corner of the client area.
To convert (x,y) from client-area coords to screen coords, add origin.
To do the reverse, subtract.

Related

Windows: Getting a window title bar's height

I was trying to get the height of the title bar of a specific window on Windows. You can replicate it with Notepad. I'm using C++ and none of the codes I found online yielded the correct result. Using e.g. Screenpresso I measured 31 pixels for my window bar height.
The functions I tried are the following:
TitleBarHeight.h:
#pragma once
#include <windows.h>
inline int get_title_bar_thickness_1(const HWND window_handle)
{
RECT window_rectangle, client_rectangle;
GetWindowRect(window_handle, &window_rectangle);
GetClientRect(window_handle, &client_rectangle);
return window_rectangle.bottom - window_rectangle.top -
(client_rectangle.bottom - client_rectangle.top);
}
inline int get_title_bar_thickness_2(const HWND window_handle)
{
RECT window_rectangle, client_rectangle;
GetWindowRect(window_handle, &window_rectangle);
GetClientRect(window_handle, &client_rectangle);
return (window_rectangle.right - window_rectangle.left - client_rectangle.right) / 2;
}
Results:
auto window_handle = FindWindow("Notepad", nullptr);
auto a = get_title_bar_thickness_1(window_handle); // 59
auto b = get_title_bar_thickness_2(window_handle); // 8
auto c = GetSystemMetrics(SM_CXSIZEFRAME); // 4
auto d = GetSystemMetrics(SM_CYCAPTION); // 23
Getting the system metrics with GetSystemMetrics() does not work because windows can have different title bar heights obviously and there is no argument for the window handle.
How can I really get the result of 31?
Assuming that you don't have menu bar, you can map points from client coordinate system to screen one
RECT wrect;
GetWindowRect( hwnd, &wrect );
RECT crect;
GetClientRect( hwnd, &crect );
POINT lefttop = { crect.left, crect.top }; // Practicaly both are 0
ClientToScreen( hwnd, &lefttop );
POINT rightbottom = { crect.right, crect.bottom };
ClientToScreen( hwnd, &rightbottom );
int left_border = lefttop.x - wrect.left; // Windows 10: includes transparent part
int right_border = wrect.right - rightbottom.x; // As above
int bottom_border = wrect.bottom - rightbottom.y; // As above
int top_border_with_title_bar = lefttop.y - wrect.top; // There is no transparent part
Got 8, 8, 8 and 31 pixels (96DPI aka 100% scaling setting)
You should also take into account DPI awareness mode. Especially GetSystemMetrics is tricky because it remembers state for System DPI when your application was launched.
Send a message WM_GETTITLEBARINFOEX to the window, and you will get the bounding rectangle of the title bar.
TITLEBARINFOEX * ptinfo = (TITLEBARINFOEX *)malloc(sizeof(TITLEBARINFOEX));
ptinfo->cbSize = sizeof(TITLEBARINFOEX);
SendMessage(hWnd, WM_GETTITLEBARINFOEX,0, (LPARAM)ptinfo);
int height = ptinfo->rcTitleBar.bottom- ptinfo->rcTitleBar.top;
int width = ptinfo->rcTitleBar.right - ptinfo->rcTitleBar.left;
free(ptinfo);
First, make sure your application is high DPI aware so that the system doesn't lie to you.
Options:
Trust GetSystemMetrics. Nearly any top-level window that actually has a different caption size is doing custom non-client area management which is going to make it (nearly) impossible. The obvious exception is a tool window (WS_EX_TOOLWINDOW) which probably has a SM_CYSMCAPTION height if the WS_CAPTION style is also set.
Get the target window rect and the target window's style. Use AdjustWindowRectEx to determine the size differences with the WS_CAPTION style toggled. I'm not sure if this will work because there may be some interaction between on whether you can have a caption without some kind of border.
Get the target window rect and send WM_HITTEST messages for coordinates that move down the window. Count how many of those get HT_CAPTION in return. Bonus points if you do this with a binary search rather than a linear search. This is probably the hardest and the most reliable way to do it, assuming the window has a rectangular caption area.
If I've understood correctly, it looks like you want to take the border size of the window (which we should be able to gather from the width as there is no title bar) and subtract it from the the verticle size minus the client window...
inline int get_title_bar_thickness(const HWND window_handle)
{
RECT window_rectangle, client_rectangle;
int height, width;
GetWindowRect(window_handle, &window_rectangle);
GetClientRect(window_handle, &client_rectangle);
height = (window_rectangle.bottom - window_rectangle.top) -
(client_rectangle.bottom - client_rectangle.top);
width = (window_rectangle.right - window_rectangle.left) -
(client_rectangle.right - client_rectangle.left);
return height - (width/2);
}

Windows API SetWindowSize() seems to have no effect

I have what should be an extremely simple goal yet it seems intractable. I have to get some precisely-sized screen pix for documentation so I thought it would make sense to add a control to size my app's window to the exact dimensions I want.
I'm working with VS2010.
Here's the relevant code from my wizard-generated dialog:
DlgSetWindowSize::DlgSetWindowSize(CWnd* pParent /*=NULL*/)
: CDialogEx(DlgSetWindowSize::IDD, pParent)
{
m_width = 0;
m_height = 0;
m_parent = pParent;
}
void DlgSetWindowSize::OnBnClickedOk()
{
CDialogEx::OnOK();
SetWindowPos(m_parent, 0, 0, m_width, m_height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER);
}
and here is the call to it:
void CMyAppView::OnWindowSize()
{
DlgSetWindowSize d(this);
if (IDOK == d.DoModal())
{
Invalidate();
}
}
SetWindowPos returns a nonzero value, which indicates success according to the documentation (click here). Of course when running the dialog, I enter the pixel values I want for width and height. The debugger verifies that the expected values are being passed to SetWindowPos, and also that SetWindowPos gives a nonzero return. The bit switches in the last parameter to SetWindowPos are set to ignore the position parameters and only express the size parameters in that call, so that the window should be sized as I want without changing position.
Everything appears to be in order, but the window size doesn't change. I have applied this logic to my app's document window, and when that didn't work, I also applied it to the application's MainFrame window. Zero action.
Am I missing something here, or is there some completely different approach I should be using?
Judging by your use of CDialogEx and the naming convention, I guess you are using MFC, right?
Your call to SetWindowPos() operates on the dialog window itself, as this is a class method.
If you want to call parent's SetWindowPos(), you could do:
m_parent->SetWindowPos(0, 0, 0, m_width, m_height,
SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER);
Also please note that in MFC Document-View architecture, the document doesn't have a window.
Alternatively, you could use Win API call:
::SetWindowPos(m_parent->GetSafeHwnd(), 0, 0, 0, m_width, m_height,
SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER);

C++ mouse click on certain spot in window

I have my function here working, but I am certainly going about it the wrong way.
My program uses FindWindow to find the correct window. I need to double click on a specific location on this window.
I made it work by always putting the window in the same location on the screen, but if I moved the window the program would try click on the hard-coded location I have provided and it would not work.
Here is the function:
void lobbyWindow(HWND main_client)
{
//RECT arect;
// GetWindowRect(main_client, &arect);
SetCursorPos(748,294);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
As you can see I just move the mouse to 748,294 and double click. What I want to do is set the mouse to 100,100 in the main_client window, so if I move the main_client window the mouse still clicks on the correct spot.
Use SendInput() instead, then you can use flags to move the cursor relative to the window -
Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP;
How i can simulate a double mouse click on window ( i khow handle) on x, y coordinate, using SendInput?
http://www.cplusplus.com/forum/windows/97017/
If the window you're clicking in is in another thread/process this approach is fundamentally flawed, because the window can move while you're sending the clicks - meaning even if you have the right position to start with there's no guarantee all the clicks will end up in the same place.
Having said that, you can convert a client-relative coordinate to screen-coordinates using the ClientToScreen API function:
POINT pt = { 100, 100 };
ClientToScreen(main_client, &pt);
Depending on the target window you may find you can simply post it a WM_LBUTTONDBLCLK message to simulate the input at the appropriate position:
PostMessage(main_client, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELPARAM(100, 100));
SetCursor() requires screen coordinates, so you need to calculate where your double-click would be in screen coordinates relative to the window's current screen location. You can do that by either:
using GetWindowRect() to retrieve the window's current screen coordinates, then offset that by the intended relative coordinates.
use ClientToScreen() or MapWindowPoints() to convert relative coordinates to screen coordinates.
Once you have the intended screen coordinates, you can pass them to SetCursor().

Draw changeable text with CDC::DrawText

I want to show on the screen some value, that are changeable. I have following code
void CMainWnd::OnPaint()
{
CPaintDC dc(this);
CRect rcText( 0, 0, 500 ,500 );
wchar_t text[36];
unsigned int num = server->GetNumClients(num);
wsprintf(text, L"Number of connected clients: %d", num);
dc.DrawText(text, &rcText, DT_LEFT);
CFrameWnd::OnPaint();
}
void CMainWnd::OnTimer(UINT timerID)
{
SendMessage(WM_PAINT, 0, 0);
}
It draws text when window appears. But in next calls when text is different the text on the screen didn't change. Using the debugger I can see that OnPaint was called, text has been changed, but on my window text remains the same. GetLastError() returns 0. I think I'm missing something important how DrawText works. I tried to call UpdateWindow() in the end, but nothing changed. For some reason screen is not updates..
You should not send a paint message directly, but instead invalidate the area to be repainted (InvalidateRect(&area) ) and let the system handle it. By only sending a paint, you don't get anything because the system says 'There's no area that needs painting, so for efficiency I won't bother' - or rather, the clip area that constrains painting will be empty (no update area). By invalidating an area you tell the system that that area needs repainting, so the next paint call will have a valid clip area and you will see a change.
(Better to use wsprintf_s() with the buffer size - in fact, as you appear to be using MFC use CString and CString::Format() instead - and you should not call the base class OnPaint() (it has no effect, since when the CPaintDC goes out of scope it clears any update area).

Clear GDI shape inside the loop

I have a program which draw a Rectangle under mouse cursor and show the pixel color, but I can't manage it to clear the shape inside the while loop, if I use 'InvalidateRect()' it clear rectangle too fast and flickering, if not use 'InvalidateRect()' then Rectangle keep duplicating like THIS, how to fix that?
HWND hwnd;
POINT p;
unsigned short R=0, G=0, B=0;
void drawRect()
{
GetCursorPos(&p);
HDC hdc = GetDC(NULL);
HPEN border = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
HBRUSH background = CreateSolidBrush(RGB(R, G, B));
SelectObject(hdc, border);
SelectObject(hdc, background);
Rectangle(hdc, p.x+10, p.y+10, p.x+40, p.y+40);
DeleteObject(border);
DeleteObject(background);
}
void init()
{
while (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
{
grabPixel(); //get RGB color from cursor coordination
drawRect(); //draw preview rectangle under cursor
InvalidateRect(hwnd, NULL, true);
}
}
Note: it doesn't have WinMain() or WndProc()
There are all sorts of things wrong with this. What are you actually trying to do?
From the fact that you're using GetDC(NULL), it looks like this is supposed to be drawing a rectangle on the entire screen.
Where is the hwnd value coming from? If that window does have a message loop (and it probably does), then that's the window being invalidated and redrawing itself.
A note: InvalidateRect merely marks the rectangle as needing-to-be-painted the next time that that application's (actually thread's, more-or-less) message queue is empty. UpdateWindow will cause a WM_PAINT message to be sent immediately.
drawRect isn't cleaning up properly, either. It should call ReleaseDC when it's finished, and it ought to restore the previous drawing objects after it's finished (and most definitely before it deletes them) as well:
HBRUSH oldBackground = SelectObject(hDC, background);
// ...
SelectObject(hDC, oldBackground);
What you probably want to do is, when selection starts, create a window the size of the screen and copy the existing screen into it. Then you can draw all over that intelligently.
The DrawDragRect function (see my blog) is designed for this sort of thing.