Programmatically press a button on another application (C, Windows)
Asked Answered
E

12

19

I'm trying to use the following code to press a button on my other application:

HWND ButtonHandle;
if( (wnd = FindWindow(0, "Do you want to save?")) )
{   
   ButtonHandle = FindWindowEx(wnd, 0, "SaveButton", "&Save");
   SendMessage(wnd, WM_COMMAND, MAKEWORD(GetDlgCtrlID(ButtonHandle), BN_CLICKED ), (LPARAM)ButtonHandle);

}

It doesn't work. I tried passing different handles to MAKEWORD and to change the WPARM and LPARAM but nothing.

Any ideas on how to click a button on another application's window?

Code is appreciated. Thanks.

EDIT: The reason it doesn't seem to work permissions. I sent a PostMessage() and the result was an error with GetLastError() = 5 (or Access Denied). Any ideas?

EDIT2 I don't mean to be rude but please please please, I already searched all the API's including getting and setting the regions for the button and then sending a button down and button up, getting the control ID, getting the class ID and a zillion more. The reason I asked the question here in the first place is because I already exhausted my search on the internet. If you know the answer PLEASE POST CODE, do not suggest an API and that's it, show me how does that API solves the problem. It's not hard. thank you.

EDIT 3: The question's answer was selected automatically when the bounty finished. The question still remains without an answer.

Emileeemili answered 3/8, 2009 at 18:25 Comment(6)
Which version of windows is this for?Awaken
Might not be relevant , but i am suggesting you run you program as Admin or run Visual Studio in Admin mode and try if that works.. Also this is what i saw in PostMessage documentation : Microsoft Windows Vista and later. When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).Yonder
Is this in Vista or 7? Does it work in XP? (Download a image to run in Virtual PC from Microsoft and test.) Have you tried using sendkeys?Tessin
Guh. This is why all graphical applications should have CLIs, too. Things like DBUS are even better.Ageratum
Given that the button has a non-standard button class, it's possible that it doesn't send WM_COMMAND to its parent. It may not even support Active Accessibility. So the suggestions to use SendInput to send mouse events may be your best bet.Annmarie
Ii tried with SendInput. It doesn't do anything. Even when setting the dialog as the foreground windowEmileeemili
F
21
  1. Are you sure that "SaveButton" class name is valid? Do you get the button handle?
  2. Try to send messages to ButtonHandle window (directly to the button).

Update: I believe this should work,

SendMessage(ButtonHandle, BM_CLICK, 0, 0);
Felony answered 3/8, 2009 at 18:51 Comment(9)
1) yes. I am sure. I used Spy++ to get that information. 2) I tried sending the message to the handle of the button but nothing happened.Emileeemili
Indeed I do. I get the handle to the window, button and even get the button classEmileeemili
In most cases the sendmessage() you posted works, however in the one case I need it the most it doesn't seem to work. I do get the button handle and I even get the button class. I tried different approached but nothing...Emileeemili
Weird. Did it succeed at least once? You should get the handle every time you send a message and not (store) reuse it. The button handle may not always be the same. There is also the case that it's a custom button control, but still ... I can't think something else right now.Felony
Note that on later versions of windows (Vista+) you are not allowed to send messages to windows that are running with higher privileges.Pathe
@Michael Weller, thanks for the info. Maybe that explains wonderer's problem in case he uses Vista.Felony
I used this is my code. Works. It really doesn't work when non-elevated code tries to send to an elevated application.Rumple
@modosansreves, your comment is interesting but I'm not familiar with the term "non-elevated" code. Can you please clarify it?Felony
@wonderer, if you don't accept an answer, SO will automatically accept an answer when the bounty expires.Felony
B
3

See the following solution, also you can use

SendMessage(ButtonHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(ButtonHandle, WM_LBUTTONUP, 0, 0);

Or

SendMessage(ButtonHandle, BM_CLICK, 0, 0);

HWND buttonHandle = 0;

BOOL CALLBACK GetButtonHandle(HWND handle, LPARAM)
{
 char label[100];
 int size = GetWindowTextA(handle,label,sizeof(label));
 if(strcmp(label,"&Save") == 0)
 {
  buttonHandle = handle;
  return false;
 }
 return true;
}
void main()
{
 HWND windowHandle = FindWindowA(NULL,"Do you want to Save?");
 if(windowHandle != 0)
 {
  BOOL ret = EnumChildWindows(windowHandle,GetButtonHandle,0);

  if(buttonHandle != 0)
  {
   LRESULT res = SendMessage(buttonHandle,BM_CLICK,0,0);
   //SendMessage(buttonHandle,WM_LBUTTONDOWN,0,0); 
   //SendMessage(buttonHandle,WM_LBUTTONUP,0,0);
  }

 }



}

Note: Getting sure from the window text,button text (check if there is space at the end of the window title)

Beth answered 17/9, 2009 at 11:16 Comment(1)
Additionl Info, first make sure the another application is in the foreground, not your application.Alejandro
C
3
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);
SendMessage(btnHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(btnHandle, WM_LBUTTONUP, 0, 0);

You have to send a button click twice. Not sure why (maybe the first click only activates the window of the button), but I'm using this code for a long time and it always worked for me.

Cryptonym answered 17/9, 2009 at 11:28 Comment(0)
F
2

maybe this can help: http://www.cplusplus.com/forum/beginner/8806/

Flickinger answered 3/8, 2009 at 21:42 Comment(0)
O
2

Access Denied errors on SendMessage or PostMessage make no sense unless the process sending the message is running at a lower integrity level than the target process.

This should not be happening unless the process that owns the target window is being run "asAdministrator" or is a service. And its damn hard for services to create windows on the interactive desktop with Windows 6 and up.

You can do some reading about Integrity Levels Here if they apply even remotely to this situation. Internet Explorer is about the only other application that 'opts in' to the integrity security model by purposely lowering the integrity level of itself in order to sandbox itself more effectively.

Oceanid answered 17/9, 2009 at 12:6 Comment(1)
strange. Both the processes are running as administrator. However, the process I am trying to click is a frontend (GUI) for a service. Now, the GUI is not a service, it's a plain win32 GUI app that all it does is call the service manager and update the service.... I wonder if this is causing the GUI to discard the message...Emileeemili
P
1

If you can raise the window containing the button you can send raw mouse event to a position within the boundaries of button.

There are two function to simulate mouse event SendInput and mouse_event. I recommend using mouse_event function. To raise a window you can use ShowWindow. I don't know how to get the handle of a button, but if you have its hWnd its easy to find its absolute position using GetWindowRect function. Try using these, if you run into any problems I will be glad to help.

Or define a custom WM within your application window to handle save request. WM_CUSTOM or WM_USER (cant remember which) marks the start of user defined window messages.

Patrilineage answered 16/9, 2009 at 10:55 Comment(10)
1) example? 2) I can't define any custom message handlers on the application.Emileeemili
Since sample is too long I have updated the answer, please check it.Patrilineage
OK, again. I cannot set a custom message handler on my application or my application "window" and since I specifically asked for a code sample I'd appreciate it if you could give a sample. The reason I asked is because I already went thru all the answers that I could find by myself, including the usual "try X and Y". Well, if you think X and Y works then please show me with code. otherwise I thank you very much for the effor but it's not what I'm looking for.Emileeemili
Im pretty sorry to submit comment before the extra definitions, I had to do some other task and could only be able to edit it now. Check the additional paragraph, I have included functions that you will need. there is no sample that i can supply but im sure that you will figure it using these functionsPatrilineage
Im also pretty sure about mouse_event because its what device drivers use. If you read MSDN article you'll see they recommend it for tablet manufacturer to use in their drivers.Patrilineage
Again, thanks. The problem with SendInput() and mouse_event() is that the mouse pointer HAS to be on top of the place you want to click. What i am trying to do is click a button programmatically, as stated in the question. to do that I need to use SendMessage or PostMessage. I already went thru all this by myself.Emileeemili
in mouse_event you set the coordinates where the event occur or move the mouse to the position you want.Patrilineage
observe: mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN, btnpos.left+(btnpos.right-btnpos.left), btnpos.top(btnpos.bottom-btnpos.top), 0, NULL); mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP, btnpos.left+(btnpos.right-btnpos.left), btnpos.top(btnpos.bottom-btnpos.top), 0, NULL);Patrilineage
Thanks. Still, unless I place the mouse pointer on top of the button it does not click it.Emileeemili
Try SetCursorPos to change where the mouse is. and dont forget to activate the target window.Patrilineage
S
1

When I have to do these kind of things I use SendKeys. It is VB-ish and C# provides a nice interface to use but for C/C++ you'll have to do it <this way>. What is nice with it is that you could write scripts and run them instead of hard coding it in your code.

Sungod answered 17/9, 2009 at 10:21 Comment(0)
F
1

Microsoft is now pushing Active Accessibility (MSAA) for UI Automation, (It has been renamed a number of times over the years) see

Sorry I don’t have any simple code to get you started. As “SendMessage()” does not seem to be working for you, I don’t know of another option apart from “UI Automation”

I am assuming you have check with Spy++ (installed with MsDev) that you message are being send to the correct button etc – and that the button is a standard windows buttons. My first instant would say use “SendMessage()" or "PostMessage()” but given the numbers of answers about “SendMessage()” and the fact it is not working for you. I expect someone is going on…

Fayre answered 21/9, 2009 at 11:43 Comment(0)
P
1

//Send digit 4 to the already opened calc.exe

HWND windowHandle;

windowHandle = FindWindowA(NULL,"Calculator");

if(windowHandle != 0)
   ret = EnumChildWindows(windowHandle,GetButtonHandle,0);

BOOL CALLBACK GetButtonHandle(HWND handle, LPARAM)
{
char label[100];
int size = GetWindowTextA(handle,label,sizeof(label));

if(strcmp(label,"4") == 0)
{
PostMessage(handle ,WM_LBUTTONDOWN,(WPARAM)0x0001,0);
PostMessage(handle ,WM_LBUTTONUP,(WPARAM)0x0001,0);

PostMessage(handle ,WM_LBUTTONDOWN,(WPARAM)0x0001,0);
PostMessage(handle ,WM_LBUTTONUP,(WPARAM)0x0001,0);

return false;
}
return true;
}
Pleuropneumonia answered 1/8, 2020 at 18:25 Comment(0)
B
0

You can use sendkeys (as tr3 said) to send mouse clicks, which is different than using SendMessage. It is also less direct and more hack-ish, but is useful for automation (in VBS).

Also, just a guess but the problem could be that your message handling is broken somewhere by not calling the base class member. Example:

void CMyClass::OnMessageY(CWnd *cwnd)
{
    CBaseClass::OnMessageY(cwnd);
    //... my code
}
Burgh answered 15/9, 2009 at 23:34 Comment(0)
H
0

if you sure ButtonHandle are valid handle you can use pair WM_LBUTTONDOWN and WM_LBUTTONUP message instead of BN_CLICKED

HWND ButtonHandle;
if( (wnd = FindWindow(0, "Do you want to save?")) )
{   
    SendMessage(ButtonHandle, WM_LBUTTONDOWN, MK_LBUTTON, 0);
    SendMessage(ButtonHandle, WM_LBUTTONUP, MK_LBUTTON, 0);
}
Hhour answered 16/9, 2009 at 18:12 Comment(3)
already tried this. it doesn't work. I guess that button doesn't want to get pressed. What I'm getting is an error "access denied" is I use postmessage() or a C000022 or something like that when I use SendMessage. I looked that error and it's basically an access denied as well.Emileeemili
can you press button non-programmatically ;) - may be program hook mouse event to prevent other program click on it's buttonHhour
I sure can. no problem there. I'm beginning to suspect that for some reason the application is ignoring the messagesEmileeemili
E
0

A non-C approach: Use Java and the java.awt.Robot class to move the mouse around perform real clicks (I guess there is something in the Windows World for this, too). Problem: You have to know where your button is :D

Elsey answered 17/9, 2009 at 11:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.