How to get HWNDs of all windows present on a given monitor? - c++

GetDesktopWindow() returns only the primary desktop. I've also tried EnumDisplayMonitors(), but that doesn't return the desktop handle.

You can do this using the functions that are available, as follows:
Method 1 (less accurate for windows overlapping a monitor boundary)
Enumerate all windows (EnumWindows())
Use MonitorFromWindow() to ignore the ones that aren't on the monitor are on a monitor other than the one you care about.
Method 2 (more accurate)
Get the bounds of the monitor you care about (GetMonitorInfo() function)
Enumerate all windows (EnumWindows())
Check each window's outline rectangle (GetWindowRect() for XP, otherwise DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS)) to see if it intersects (IntersectRect()) with the monitor rectangle.

You can use EnumWindows function and then check that window belongs to the specific monitor:
BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, _In_ LPARAM lParam) noexcept
{
auto const h_monitor{::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL)};
if(h_monitor == g_h_target_minitor)
{
// Do something...
}
return TRUE;
}

Related

Get WM_INPUT from Unity window

About
I am trying to build a custom mouse input for Unity that gets the data directly from the HID. I do this because I want to try if there is any difference (when using my own custom mouse input) to the Unity API that gives me raw mouse input.
Also I need to say that everything I am doing right now does not happen within Unity. I want to build an C++ application and then pass the data to Unity (that's not a part of this question).
This link (MSDN High-Definition Mouse Movement) shows that there are three different types of messages I can use. Due to I need so called "High-Definition Mouse Movement" I need to go with WM_INPUT.
This message can be caught with the WinProc handler as the documentation says. Within that callback the raw mouse data can be accessed. This is what I want to achieve and where I need help.
My current approach
The documentation (link above) gives me this example to register the mouse:
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = gameWindowHandle;
regDeviceDone = RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));
The following two lines were modified by me:
Rid[0].hwndTarget = gameWindowHandle;
There I define the Unity window as target. gameWindowHandle is set by EnumWindows.
The other line I changed is the last one due to there is a syntax error (missing parenthesis).
As far as I understood the documentation right this should be it. Now the following callback should be called when there are WM_INPUT messages sent to the Unity window.
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
) {
printf("%d", uMsg);
switch (uMsg) {
case WM_INPUT:
UINT dwSize = 40;
static BYTE lpb[40];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
{
int xPosRelative = raw->data.mouse.lLastX;
int yPosRelative = raw->data.mouse.lLastY;
printf("X: %d, Y: %d", xPosRelative, yPosRelative);
}
break;
}
return NULL;
}
My problems
The first problem I have is that calling this
regDeviceDone = RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));
does not return true as it should. Instead it returns false and GetLastError gives me error 87 (after googling this I found out it has to do with wrong parameters).
The documentation says to do so but unfortunately it does not work the way I do it.
Another problem is how to keep the application alive. After registering the device I need to wait for the callbacks to trigger (if they would work). How can I achieve that the application does nothing than waiting for the callbacks?
Is my approach even reasonable or am I doing completely wrong and have to use different APIs?
Your approach is wrong. First, RawInput requires Window. A Window under your control with your own WndProc. Hence in your C++ library, you should define a window procedure. Start a thread. In this thread register window class with that procedure. After you succeed in registering your class, create HWND_MESSAGE window, register your devices and enter a while GetMessage... DispatchMessage loop. This should be done in a separate thread. In your window procedure you must now catch WM_INPUT messages. Enjoy.

"SendMessage" to 3 different processes in C++

I want to send keystrokes to multiple processes. For example, if I press “1”, then I want to send the “1” to 3 "Notepad windows". Frist I want to try to send a keystroke to notepad, but it fails on the HWND:
//HANDLE hWin;
HWND windowHandle = FindWindowA(NULL, "Notepad"); //Can’t find a proccess
//Send a key
if( windowHandle ) //This one fails
{
while(true)
{
if( GetAsyncKeyState(VK_F12) != 0 )
{
SendMessageA(windowHandle, WM_KEYDOWN, VK_NUMPAD1, 0);
Sleep(1000);
SendMessageA(windowHandle, WM_KEYUP, VK_NUMPAD1, 0);
}
Sleep(100);
}
}
But the "FindWindow" method is not good enough for my program. There is also no way to get 3 different processes with the same name. So how can I make 3 handles to 3 different processes with the same name? And how can I send key’s to the processes?
You can use EnumWindows for enumerating all the top level windows on the system. You then need to filter through these windows to get the ones you are interested in. Class name is probably a better choice for filtering rather than the window name though. Here is some example code (not tested) of what I have in mind:
BOOL CALLBACK BroadcastToNotepad(HWND hwnd, LPARAM lParam)
{
wchar_t lpClassName[16];
/*
* More reliable to filter by class name. We could additionally filter
* by caption name too if necessary.
*/
if(GetClassName(hwnd, lpClassName, _countof(lpClassName))) {
if(wcscmp(lpClassName, L"Notepad") == 0) {
SendMessage(hwnd, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwnd, WM_KEYUP, (WPARAM)lParam, 0);
}
}
return TRUE;
}
// Some handler which gets invoked when your hotkey is hit.
void handlerKey1(...)
{
EnumWindows(BroadcastToNotepad, (lParam)VK_NUMPAD1)
}
Note the usage of BroadcastToNotepad and how you can have different handlers pass in a different lParam.
One final thing to note is that PostMessage/SendMessage is not a reliable way to simulate keyboard input. This is noted by Raymond Chen here. SendInput is the preferred way for injecting input. However, to use that you will need to ensure the window you want to send to has the keyboard focus.
I recall vaguely having played with something similar to what you are doing in the past. If I remember correctly, you need to send to Notepad's child window (class name = Edit). So the code above needs to be modified as so:
if(wcscmp(lpClassName, L"Notepad") == 0) {
HWND hwndChild = FindWindowEx(hwnd, NULL, L"Edit", NULL);
SendMessage(hwndChild, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwndChild, WM_KEYUP, (WPARAM)lParam, 0);
}
Firstly install Spy++ from Visual Studio which lets you see all the HWND windows in hierarchy ( and which process owns them).
Then you'll see why your FindWindow is failing. You'll also know the exact hierarchy calls to make on FindWindow and GetWindow().
Be aware that since Vista some HWNDs are protected and you cant send to them - but notepad is probably fine.
For sending the key, you can probably just use PostMessage to fire and forget.
First of all, why is while(true) there? Wouldn't you rather want to activate your software on F12 key press than having an infinite loop? That handle is not valid forever, you know.
Second, you'd probably want to use EnumWindows to go through all the windows and find the one you're interested in. Then you'd implement a callback function that'll need to decide on some basis if it wants to act on some window or not (be it name or something else).
SendMessage/SendMessageA/SendMessageW should work just fine when you've found a proper handle for the window you want to target for (save for some special windows that are protected from this).

Window Maximum Maximise

I am looking to create a program where I can set the maximum maximize size (as in the size the window maximises to when you hit the maximise button) and maximize position (X/Y coordinated for the maximised window) for all of the windows that are running. This is so that I can have my Rainmeter visible at all times on my secondary monitor without having to manually resize every window fit inside of it.
I have managed to do this for a simple program I wrote using MINMAXSIZE from the WinAPI. This method seems to work perfectly for my little program, but there is very little documentation on it beside 1 or 2 articles on the internet. I was wondering if this would be the best way to set the maximum maximise size, or if there is another way to do this.
They way I planned to implement this into all of the applications was going to be either DLL Injection or Hooks (neither of which I have any experience with), so I was also wondering your guys' thoughts on these methods.
I know there are a few applications out there that already do this, but I thought this could be a learning experience, and as well, all of the applications I tried do not work very well (although this could be the case with all of them due to the way Windows functions).
If any of you are still unsure about what I am talking about, MaxMax does exactly what I want (although it doesn't work so well, as I stated in my previous paragraph).
Thank you all in advance.
What you're probably looking for is the work area setting, that you can set/retrieve with the SystemParametersInfo function, called with the flags SPI_SETWORKAREA/SPI_GETWORKAREA.
What you want to do is use a global windows hook to handle WM_GETMINMAXINFO. As you may be aware, this is the message that is:
Sent to a window when the size or position of the window is about to
change. An application can use this message to override the window's
default maximized size and position, or its default minimum or maximum
tracking size.
The best way to use this to override the default maximum is to fill in the MINMAXINFO structure like so:
case WM_GETMINMAXINFO: {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMaxTrackSize.x = 100;
mmi->ptMaxTrackSize.y = 100;
return 0;
}
This will allow the default values to be assigned to the sizes you don't care about (min x/y) in this case, leaving you to fiddle with the max values as you please. Your windows hook should be done with SetWindowsHookEx() and should look something like this:
SetWindowsHookEx(WH_CALLWNDPROC, hook_procedure, instance_handle, 0);
hMod (instance_handle) should only be set depending on the circumstances (check the docs for this). The dwThreadId mandates a global hook. Your CallWndProc might looks something like this:
__declspec(dllexport) LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
CWPSTRUCT* cwp = (CWPSTRUCT*)lParam;
if(WM_GETMINMAXINFO == cwp->message) {
DefWindowProc(hWnd, message, wParam, lParam);
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
mmi->ptMaxTrackSize.x = 100;
mmi->ptMaxTrackSize.y = 100;
return 0;
}
return CallNextHookEx(next_hook, nCode, wParam, lParam);
}
Unfortunately something you are going to have to deal with is that the only windows that will be hooked are the ones that had were existing when you made your call to SetWindowsHookEx(). I'm not aware of a clean way of getting past this, short of looping a call to SetWindowsHookEx() (ergh!).
You could potentially do this with DLL injection and effectively subclass every window with EnumWindows, EnumChildWindow and SetWindowLongPtr/SetWindowSubclass. But why go to all that trouble when you could just use SetWindowsHookEx? :)
To alter the x/y, you might have to add an override for WM_SYSCOMMAND and check for SC_MAXIMIZE then use SetWindowPos/MoveWindow to position it properly (if you don't want it on the default 0, 0).

Activate windows under mouse through mouse hook

Basically i make mousestruct in the hook
MOUSEHOOKSTRUCT* str;
Then make it from lparam,
LRESULT CALLBACK MouseProc( int nCode, WPARAM wParam, LPARAM lParam )
{
str = (MOUSEHOOKSTRUCT *) lParam;
...
Then I catch mousemovements
case WM_MOUSEMOVE:
wParm = AU3_WM_MOUSEMOVE;
fromp = WindowFromPoint(str->pt);
Then validate and try not to send to many messages...
if (fromp != currentwindow)
{
currentwindow= fromp;
PostMessage(m_hHwndMouse, wParm,(WPARAM)( (MOUSEHOOKSTRUCT*) lParam )->hwnd, LPARAM(fromp));
}
break;
This sends the mousemove message along with hwnd to my autoit app which inspects hwnd and if that hwnd is not active it activates it.
Func mouse_func($hWndGUI, $MsgID, $wParam, $lParam)
Select
Case $MsgID = $WM_AUTOITMOUSEMOVE
If GUICtrlRead($activateundermouse) = 1 And $sitting = 0 Then
;Local $starttime = _Timer_Init()
If StringInStr(WinGetTitle($lParam), "ID=") Then
If Not WinActive($lParam) Then
;ConsoleWrite("HOVERING NEW, Activate It: " & WinGetTitle($lParam) & #LF)
WinActivate($lParam)
EndIf
;ConsoleWrite("diff is > " & _Timer_Diff($starttime) & #LF)
EndIf
EndIf
This is how I am activating window that is hovered by the mouse but the problem is that rarely autoit wont read the message that should signal new window being hovered(or the dll with hook didnt send it, I dont know)
Also if the window is overlapping another window and both of them are valid windows that should be activate once hovered I get flickering as autoit is constantly trying to activate the current window and the overlapped one, in a loop
Is there something that perhaps I missed or could be doing wrong here?
It's easiest just to use the facility built into Windows. For example on Windows 7 it looks like this:
This capability is present in older versions of Windows too but is not exposed in such a simple interface. Instead you have to set it with a PowerToy or through SystemParametersInfo.
As Raymond Chen explains, this is a user preference which should not be changed without the user's consent.
You can use the blocking SendMessage instead depending on surrounding implementation. PostMessage will send to the window message queue but may not have the priority you expect as it returns without waiting for the processing of the message.
Also check out SetCapture and ReleaseCapture to change which window is capturing mouse events based on a hit test in mouse move as an alternative to forwarding. Only one window at a time can capture mouse events using these functions so that will solve your flicker issue of the windows forwarding messages to each other most likely.

C++: How to center MessageBox?

Using Visual Studio C++ with MFC. How do I center a MessageBox to it's parent window? Currently it centers to the desktop.
You need to install a hook and change the dialog box position on creation.
int MessageBoxCentered(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
// Center message box at its parent window
static HHOOK hHookCBT{};
hHookCBT = SetWindowsHookEx(WH_CBT,
[](int nCode, WPARAM wParam, LPARAM lParam) -> LRESULT
{
if (nCode == HCBT_CREATEWND)
{
if (((LPCBT_CREATEWND)lParam)->lpcs->lpszClass == (LPWSTR)(ATOM)32770) // #32770 = dialog box class
{
RECT rcParent{};
GetWindowRect(((LPCBT_CREATEWND)lParam)->lpcs->hwndParent, &rcParent);
((LPCBT_CREATEWND)lParam)->lpcs->x = rcParent.left + ((rcParent.right - rcParent.left) - ((LPCBT_CREATEWND)lParam)->lpcs->cx) / 2;
((LPCBT_CREATEWND)lParam)->lpcs->y = rcParent.top + ((rcParent.bottom - rcParent.top) - ((LPCBT_CREATEWND)lParam)->lpcs->cy) / 2;
}
}
return CallNextHookEx(hHookCBT, nCode, wParam, lParam);
},
0, GetCurrentThreadId());
int iRet{ MessageBox(hWnd, lpText, lpCaption, uType) };
UnhookWindowsHookEx(hHookCBT);
return iRet;
}
::AfxMessageBox() appears on the center of the MainFrame for me. Which is basically a call to ::MessageBox() with a handle to the MainFrame as the first parameter. Isn't that working for you?
You can't. That's why a lot of people write their own MessageBox classes.
Who said "can't"?
Try this:
This is for Win32 API, written in C. Translate it as you need...
case WM_NOTIFY:{
HWND X=FindWindow("#32770",NULL);
if(GetParent(X)==H_frame){int Px,Py,Sx,Sy; RECT R1,R2;
GetWindowRect(hwnd,&R1); GetWindowRect(X,&R2);
Sx=R2.right-R2.left,Px=R1.left+(R1.right-R1.left)/2-Sx/2;
Sy=R2.bottom-R2.top,Py=R1.top+(R1.bottom-R1.top)/2-Sy/2;
MoveWindow(X,Px,Py,Sx,Sy,1);
}
} break;
Add that to the WndProc code... You can set position as you like, in this case it just centres over the main program window. It will do this for any messagebox, or file open/save dialog, and likely some other native controls. I'm not sure, but I think you may need to include COMMCTRL or COMMDLG to use this, at least, you will if you want open/save dialogs.
I experimented with looking at the notify codes and hwndFrom of NMHDR, then decided it was just as effective, and far easier, not to. If you really want to be very specific, tell FindWindow to look for a unique caption (title) you give to the window you want it to find.
This fires before the messagebox is drawn onscreen, so if you set a global flag to indicate when action is done by your code, and look for a unique caption, you be sure that actions you take will only occur once (there will likely be multiple notifiers). I haven't explored this in detail, but I managed get CreateWindow to put an edit box on a messagebox dialog. It looked as out of place as a rat's ear grafted onto the spine of a cloned pig, but it works. Doing things this way may be far easier than having to roll your own.
Crow.
EDIT: Small correction to handle the problem raised by Raymond Chen. Make sure that parent handles agree throughout, and this should work ok. It does for me, even with two instances of the same program...