Click here to Skip to main content
15,886,422 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
Hi,

Im on Windows7 and I need to simulate a mouse click( without moving the cursor ) in a window that is minimized or hidden
Is this possible ? And if so, then how can I do this( what APIs should I use, and will work in all windowses ) ?

OBS: I need only the code scheme in general, not in a specific language.

! ! THANKYOU SO MUCH !!
Posted


[Edit]
Please note, this is brittle in the way described below. Restarting the "client" application generates a new classname based on the Hash of the AppDomain. This means the code below will work once and only once. Please see my other next answer for a less brittle Windows7-specific implementation. If doing this in c++, you will need to call the equivalent code as the second answer.


Yes. In principle, this is easy.
1. Use User32.dll (.net needs to import this)
2. Use FindWindow to get an address pointer to the window containing the control.
3. Use FindWIndowEx to get an address pointer to the button you want to click
4. Use SendMessage to place a button click message onto the windows message queue.

Now it depends which language you are using, I will assume c# here (and give links to a c++ version). The C++ version is simpler as it can import windows assemblies directly.

First import the relevant methods from user32 (I tested on 64 bit win7, so don't be put off by the name):

C#
// This is the button click message value. In windows it is called BM_CLICK
// I have kept the name here
private const int BM_CLICK = 0x00F5; 

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);



Now for the business end I created a dummy app. The form had a caption "FormToBeCaptured" (this is important). On it there was a text box and a button with the text "Add" in it. The event handler was hooked up so that if the button is pressed it appends "a" to the existing text in the text box.

C#
IntPtr hwndChild = IntPtr.Zero;
IntPtr hwnd=IntPtr.Zero;
hwnd = FindWindow(null, "FormToBeCaptured");
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "WindowsForms10.BUTTON.app.0.bf7771_r15_ad1", "Add");
SendMessage(hwndChild, BM_CLICK, IntPtr.Zero, IntPtr.Zero);


The first two lines initalize empty pointers. The 3rd line finds the window via its caption, you may need to find another way to do this, but this is the simplest.
This line was problematic (it gets the Pointer to the button to be pressed via its caption:
C#
hwndChild = FindWindowEx(hwnd, IntPtr.Zero, "WindowsForms10.BUTTON.app.0.bf7771_r15_ad1", "Add");
SendMessage(hwndChild, BM_CLICK, IntPtr.Zero, IntPtr.Zero);

The class name was "Button" in windows XP. In Windows 7 (possibly Vista too) this seems no longer to be the case. I used SPY++ (it is installed with Visual Studio) to examine the window. It seems the class name is now "WindowsForms10.BUTTON.app.0.bf7771_r15_ad1". This seems a bit brittle to me - you may need to check the class name on your machine, it may break on update too. If the class name is different in spy++, change it obviously.

The last line sends a message to the Windows Message Queue with the pointer to the button, the message as BM_CLICK and the lParam and wParam as IntPtr.Zero, in effect null.

As I say this is brittle, it will probably change between windows versions and possibly between updates. I've suggested spy++, but also you might find the following useful:

pinvoke.net[^] - a wiki of interop calls for .net
C++ example here with VB6[^] Note this is targeted at earlier windows versions.
 
Share this answer
 
Comments
Manfred Rudolf Bihy 6-May-12 18:29pm    
Nice answer! 5+
Berkay Öntürk 9-Mar-16 22:11pm    
Can we simulate a Mouse click and 'movement' in a minimized window ?? I need to move that.
This solution is better for Win7 (and probably Vista - I can't check on this)
As I said in my original answer, finding the button control is brittle as it relies on the class name. Each time the application being targeted is run, the class name changes in Window7. Luckily I re-ran the code and spotted this, the last time I did this stuff was under XP and much seems to have changed under the hood. I have kept the original answer as it explains the basics which I want to keep, and editing it will make it even harder to read.

Anway instead of the FindWindowEx method, we can loop through the child controls to find a control captioned "Add" with a class name that starts with

Again, the Pinvoke Methods (new in bold):

C#
private const int BM_CLICK = 0x00F5;
//Used by EnumChildWindows returns true if should continue
public delegate bool CallBack(IntPtr hwnd, IntPtr lParam);

//Enumerate the child controls until the callback returns false
[DllImport("User32.dll")]
public static extern bool EnumChildWindows(IntPtr hWndParent, CallBack lpEnumFunc, IntPtr lParam);

//Gets the text of the window at the passed pointer address
[DllImport("User32.dll")]
public static extern Int32 GetWindowText(IntPtr hWnd, StringBuilder s, int nMaxCount);

//Gets the classname of the window at the passed pointer address
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam)


Next the "main" method becomes
C#
IntPtr hwnd=IntPtr.Zero;

//Get a handle for the Calculator Application main window
hwnd = FindWindow(null, "FormToBeCaptured");
//Should check for empty hwnd (== IntPtr.Zero)
//Enumerate through Children until callback returns false
EnumChildWindows(hwnd, ButtonClickCallback, IntPtr.Zero);


Now the new stuff to actually make the click, details in comments.
C#
//Get the text in the object at the pointer
private static string GetText(IntPtr hWnd)
{
    StringBuilder formDetails = new StringBuilder(256);
    GetWindowText(hWnd, formDetails, 256);
    return formDetails.ToString().Trim();
}

//Get the class name of the object at the pointer
private static string GetClassName(IntPtr hWnd)
{
    StringBuilder formDetails = new StringBuilder(256);
    GetClassName(hWnd, formDetails, 256);
    return formDetails.ToString().Trim();
}
        
// This method is called as a delegate each time a child is enumerated. 
// If it returns false, enumeration will stop.  
// The whole tree will be traversed without further code despite only
// passing the root.
public bool ButtonClickCallback(IntPtr hWnd, IntPtr lParam)
{
    string text = GetText(hWnd);
    string className = GetClassName(hWnd);

    //OK: These need to be class level feilds or something
    if (text == "Add" && className.StartsWith("WindowsForms10.BUTTON"))
    {
        //send BN_CLICKED message
        SendMessage(hWnd, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
        return false;
    }
    return true;
}


:cool:
 
Share this answer
 
Comments
Manfred Rudolf Bihy 6-May-12 18:29pm    
Great answer! 5+
mouse event handle by windows and always response to screen
and in application handle by function inside the code

if you have source code of minimized or hidden window and you can modify this , you can use other way to communication between 2 application
otherwise
try change status of window then
use mouse event simulation method
bat user may be detect you :)
 
Share this answer
 
Comments
Keith Barrow 7-May-12 3:50am    
"mouse event handle by windows and always response to screen"
Actually, this isn't strictly true, but you need to go down to the OS level rather than the application level.

All interaction with the GUI works by queuing messages through the Windows Message Queue.As long as you have the address pointer to the thing you want to interact with and the message code, you can interact with it. This works even if the target is hidden. If you think about it this is logical: otherwise Windows would not be able to un-minimise a minimised window.


Please look at my answers, they give the instructions to do this. I tested this code on Windows 7, but have used the method in the 1st answer under XP.

I have not voted on your answer BTW
Realy really great post Keith Barrow :) Very high quality :) But I have some questions :

1. I don't want to click on a control, its just a simple spot in a window, no button, no nothing,so it will work ?

2. Will work with any window I will try ?

3. If the OS will get updated or a new SP, possibly the code won't work anymore ?
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900