C++ (CLR) mouse click to hidden window - c++

In a windows C++ CLR project I am trying to build a function that can send a mouse click to absolute x, y coords in a out of focus / minimized / hidden window.
The function is controlled by pressing a button on the app GUI.
Doing some research on google and on stackoverflow I put together the code below:
void click(int x, int y) {
POINT pt;
pt.x = x;
pt.y = y;
HWND hWnd = FindWindowEx(NULL, NULL, L"winWithThisClass", NULL);
if (hWnd)
{
SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
Sleep(130);
SendMessage(hWnd, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(pt.x, pt.y));
}
}
The above code does send a click to the minimized window, but not at specified coords.
The click is sent to the minimized window at the coords of the mouse pressing button on the GUI.
So whereever the GUI is located on the desktop, when I press the button that runs the function, the click is sent to the minimized window at the GUI's button coords.
Any ideeas on how I can make this code work as intended?
Thanks

Related

SendMessage C++ Right mouse button doesn't work left mouse button works?

I want to use SendMessage to complete part of my remote control program. Case I think it will control the computer program without make it get focus.
Here is my demo. I use windows mspaint to test it. It works when I post "Left button down", so I am sure that my HWND is right(The drawing area handle got by spy++ or some similar tools). It draws a point in my "drawing area" in mspaint.
But when I select a color and run the folling code. It doesn't work. Nothing happens in my drawing area on the same mspaint program.
Here is my code:
#include <stdio.h>
#include <Windows.h>
int main(){
HWND hwnd;
printf("Please input your handle\n");
scanf("%d", &hwnd);
int x = 100, y = 100;
//int right = SendMessageA(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(x, y)); //It works
int wrong = SendMessageA(hwnd, WM_RBUTTONDOWN, MK_RBUTTON, MAKELPARAM(x, y)); //It doesn't work
printf("%d", wrong);
printf("%d", GetLastError());
return 0;
}
I am confused because the two lines are so similar. Is there any bugs in mspaint?
I am confused because the two lines are so similar. Is there any bugs
in mspaint?
These two code lines can be executed successfully and they do send WM_LBUTTONDOWN and WM_RBUTTONDOWN messages to the target window. I can confirm this via target a own created window application and monitor mouse message in it. So maybe the difference depends on how does the mspaint application handle left and right mouse button click.
The following code works for me for both left and right mouse buttons. You can have a try.
int x = 100, y = 100;
// Left mouse button click
int lResult = SendMessageA(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(x, y));
Sleep(500);
lResult = SendMessageA(hwnd, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(x, y));
x += 10;
// Right mouse button click
int rResult = SendMessageA(hwnd, WM_RBUTTONDOWN, MK_RBUTTON, MAKELPARAM(x, y));
Sleep(500);
rResult = SendMessageA(hwnd, WM_RBUTTONUP, MK_RBUTTON, MAKELPARAM(x, y));
printf("Done.");
The test result of above code:
Note: As other communities already pointed out, using SendInput instead of SendMessage to Synthesizes keystrokes, mouse motions, and button clicks.

PostMessage not clicking buttons

I've played with PostMessage just clicking different tabs which seemed to work. But I'm trying to automate some button clicks and when running this function it highlights the button as if I was hovering over it but doesn't click.
I thought somehow the button changing of colour made the boolean false so I added the exception of the buttons colour whilst it's hovered. Made no difference, and I do not wish to use SetCursorPos & simulate a mouseclick using SendInput. I wish to understand the problem/issue I'm having as to why it's not clicking.
void click(const std::vector<uint_16>& x, const uint_16& y)
{
for(uint_8 i = 0; i < 5; i++)
{
if(content::MyClass().firstMatch(GetPixel(hdc, x[i], y)))
{
PostMessage(hwnd, WM_LBUTTONDOWN, 0, MAKELPARAM(x[i], y));
return;
}
}
if(content::MyClass().secondMatch(GetPixel(hdc, x[4], y)))
{
PostMessage(hwnd, WM_LBUTTONDOWN, 0, MAKELPARAM(x[4], y));
}
}
The solution you are using is unreliable because you are short-circuiting the input system on the window and not specifically targeting which button you are trying to press.
As for the reason your code is not currently working, you only send a WM_LBUTTONDOWN message to the window. Since most buttons work off a combination of WM_LBUTTONDOWN and WM_LBUTTONUP your program isn't causing the buttons click method to activate.
Adding PostMessage(hwnd, WM_LBUTTONUP, 0, MAKELPARAM(x[i], y)); after the mouse down will cause the button click to register.
In future as a more reliable solution that will specifically target a button on the window and click it you may want to look at the BM_CLICK PostMessage argument. Using this instead of trying to emulate a mouse click is more correct because windows will trigger events that may otherwise be forgotten when using the mouse down and mouse up post commands.
An example:
int retVal = 0;
HANDLE hwndDialog;
HANDLE hwndButton;
/* First, see if the dialog box (titled "Inactivity Warning" ) is currently open. */
hwndDialog = FindWindow( 0, "Inactivity Warning" );
if ( hwndDialog == 0 ) return;
/* Now get a handle to the "Resume" button in the dialog. */
hwndButton = FindWindowEx( hwndDialog, 0, 0, "Resume" );
/* After making sure that the dialog box is the active window, click "Resume". */
retval = SetActiveWindow( hwndDialog );
/* converted from SendMessage. */
retval = PostMessage( hwndButton, BM_CLICK, 0, 0 );
Source found Here, Converted from VB by me.
For some further reading on the input system Here is a good article.
A Blog Post by Raymond Chen goes into a bit of detail about these commands and their caveats as well.

Send "button pressed" message to UnityWndClass

I have 3D game and i need to press buttons in this game from another application. Spy++ says that window of the game is UnityWndClass. When i pressing buttons in game, window recives only mouse messages like WM_SETCURSOR, WM_LBUTTONDOWN etc, but when i try to send WM_LBUTTONDOWN and WM_LBUTTONUP with coordinates of the button from another window nothing happend. Why? Any ideas how to press buttons?
WM_SETCURSOR is for changing the cursor image of your own window. WM_LBUTTONDOWN and WM_LBUTTONUP are notification messages sent after mouse click. WM_LBUTTONXXX messages are probably not handled by the main Window procedure.
To fake mouse input, you can use mouse_event or SendInput. For example, this moves cursor to x=10 / y=10 and and clicks left mouse.
int x = 10 * 65536 / GetSystemMetrics(SM_CXSCREEN);
int y = 10 * 65536 / GetSystemMetrics(SM_CYSCREEN);
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x, y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Or use SendInput for multiple calls (see comments)
INPUT input[3];
for (int i = 0; i < 3; i++)
{
memset(&input[i], 0, sizeof(INPUT));
input[i].type = INPUT_MOUSE;
}
input[0].mi.dx = x;
input[0].mi.dy = y;
input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
input[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
input[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(3, input, sizeof(INPUT));
Window should be at least visible and preferably be active. You can use SetForegroundWindow. See this example to make sure window is visible and active.
Other alternatives:
Use Spy++ to get information about the button which you want to press.
For example, if this was Windows Calculator, you can spy on calculator's buttons, it shows calculator button 2 has ID set to 0x84. You can send WM_COMMAND with WPARAM set to 0x84 to simulate button press.
HWND hwnd = FindWindow(0, L"Calculator");
if (IsWindow(hwnd))
{
//this should put "234" in to calculator's edit box
SendMessage(hwnd, WM_COMMAND, 0x84, 0);
SendMessage(hwnd, WM_COMMAND, 0x85, 0);
SendMessage(hwnd, WM_COMMAND, 0x86, 0);
}

C++ simulate left mouse click on minimized program

I have been searching for a bit about this particular problem I am having, I want to be able to simulate a left mouse click on a program that I am currently attached to.
Right now, I create a thread that checks a database for certain values, and when those values come back (the ones I am looking for), I want to be able to then send a left mouse click in any x,y coord of the program (while minimized).
How can this be done for Windows 7? Thanks!
EDIT: Here is how I am calling the thread ...
HWND child = GetActiveWindow();
if ( child == NULL )
MessageBox(0,"Couldn't get the child hwnd!","",0);
DWORD ID;
HANDLE thread_check_game = CreateThread ( NULL , 0 , (LPTHREAD_START_ROUTINE) game_check_thread , (LPVOID)child, 0 , &ID ); CloseHandle ( game_check_thread );
and then ...
DWORD WINAPI game_check_thread(LPVOID lpParam) {
HWND Window;
Window = (HWND)lpParam;
// ... some other code ...
// ...
WORD mouseX = 398;
WORD mouseY = 398;
SendMessage(Window,WM_LBUTTONDOWN,MK_LBUTTON,MAKELPARAM(mouseX,mouseY));
SendMessage(Window, WM_LBUTTONUP, MK_LBUTTON, MAKELPARAM(mouseX, mouseY));
Write("Sent Left Click\n");
ExitThread(0);
return 0;
}
If you want to fire a mouse event in your application, use the SendMessage function, and your message will appear in the window with handle hWnd's message pump.
SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(mousePosX, mousePosY));
You may need to notify for WM_LBUTTONUP, depending on the way you application handles it's mouse events.

Receiving WM_MOUSEMOVE from child controls on MFC CDialog

I have my dialog derived from CDialog and I want to close it once user moves mouse cursor away from it. To do so, I've added OnMouseLeave handler which calls OnCancel(). As I understand, for WM_MOUSELEAVE events to be sent in time, TrackMouseEvent must be called inside OnMouseMove routine. So the whole code is as following:
void CDlgMain::OnMouseLeave()
{
CDialog::OnMouseLeave();
// Close dialog when cursor is going out of it
OnCancel();
}
void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
CDialog::OnMouseMove(nFlags, point);
}
It works fine, but the dialog is closed when user hovers some of its child controls (like buttons he wants to click on :) ). It is because child controls do not send WM_MOUSEMOVE to the parent dialog.
The only function I found to "propagate" WM_MOUSEMOVE messages from child controls is SetCapture(). And it does the job, but 1) user cannot click any button after that and 2) mouse icon changes to hourglasses. So this is not an option.
Any suggestions?
Update I placed TrackMouseEvent call to the PreTranslateMessage routine which is called correctly on any mouse move events (even hovering the child controls). The strange thing is WM_MOUSELEAVE is still generated when user hovers child control! Seems like TrackMouseEvent knows what control is hovered now. Any ideas how to fix this?
I would try CDialog::PreTranslateMessage() if this is a modal dialog. If you still cannot detect mouse movements inside the children, the only option left is SetWindowsHookEx + WH_MOUSE.
When 2 dialog get event, then rise child dialog's WM_MOUSELEAVE event by force.
see the code, below
void CDlgParent::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd* cwnd = this->GetDlgItem(IDC_CHILDRENNAME);
::SendMessage(cwnd->m_hWnd, WM_MouseLeave());
CDialog::OnMouseMove(nFlags, point);
}
void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
::SetFocus(this->mhWnd);
CDialog::OnMouseMove(nFlags, point);
}
How do you think?
I think I understand the problem now. It's indeed a bit tricky. I think you need a timer to guarantee that the subsequent WM_MOUSEMOVE message is handled (you have to test this).
BOOL CTestDgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_MOUSEMOVE)
{
TCHAR buffer[255];
::GetWindowText(pMsg->hwnd, buffer, 255);
TRACE(_T("WM_MOUSEMOVE: %s\n"), buffer);
}
return CDialogEx::PreTranslateInput(pMsg);
}
Handle WM_MOUSELEAVE, wait for WM_MOUSEMOVE. Did it arrive? No -> dismiss dialog. Yes -> restart.
Thanks for all your help, guys. I wasn't able to make TrackMouseEvent properly, so I end up implementing solution with timer. On each tick I check position of mouse cursor to be inside my dialog area and ensure it is still foreground. That works perfect for me, though it is a little hack.
void CALLBACK EXPORT TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
// This is a little hack, but suggested solution with TrackMouseEvent is quite
// unreliable to generate WM_MOUSELEAVE events
POINT pt;
RECT rect;
GetCursorPos(&pt);
GetWindowRect(hWnd, &rect);
HWND hFGW = GetForegroundWindow();
// Send leave message if cursor moves out of window rect or window
// stops being foreground
if (!PtInRect(&rect, pt) || hFGW != hWnd)
{
PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
}
}