Solved

Simulating a mouse click

Posted on 1998-04-14
14
514 Views
Last Modified: 2013-12-03
I'm using the following code to simulate a mouse click in another window (process). The other window (process) is typically disabled for input. Just before doing the below code, I enable the window. I disable it after doing the mouse input code. This seems like it should be enough to allow the simulated mouse input to take place. However, this in itself doesn't work. I actually had to set a timer after doing this code. The timer allowed enough time (50 milliseconds) for the mouse input to occur before I again disabled the window. I don't understand this. Can someone explain it or tell me a better way?

-----------------------------------------------------------------------------------------------------
    DWORD data = 0,
                    dx = 0,
                    dy = 0,
                    extra = 0,
                    flag = 0;

    // If select is true, actually reposition the mouse. Otherwise, just pretend it has moved.

    if (select)
    {
        double tmp;

        POINT pt;

        RECT ScrSize;

        // Get the current cursor position and desktop resolution

        {
            GetCursorPos(&pt);

            HWND hWnd = ::GetDesktopWindow();

            ::GetWindowRect(hWnd, &ScrSize);
        }

        flag = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

        tmp = (double) pt.x / (double) ScrSize.right * 65536.0;

        if (tmp - int(tmp) > 0.49)
            dx = (unsigned long) tmp + 1;
        else
            dx = (unsigned long) tmp;

        tmp = (double) pt.y / (double) ScrSize.bottom * 65536.0;

        if (tmp - int(tmp) > 0.49)
            dy = (unsigned long) tmp + 1;
        else
            dy = (unsigned long) tmp;

        mouse_event(flag, dx, dy, data, extra);
    }

    dx = 0;
    dy = 0;
    flag = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_MOVE;

    mouse_event(flag, dx, dy, data, extra);

    flag = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_MOVE;

    mouse_event(flag, dx, dy, data, extra);
0
Comment
Question by:flfmdll
  • 9
  • 5
14 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 1412744
Try to narrow down the problem.  If the target window is not dissabled at all, does it work?  If so, the problem is related to the fact that it is disabled..  If it doesn't work, the probelm might be related to coordinates.

have you tried using Spy or a similar utility to see if the target window is getting the mouse message?
0
 

Author Comment

by:flfmdll
ID: 1412745
Like I said, if I use a timer to delay disabling the window, the code works fine. The cursor is repositioned just where it should be. So if I,

1) enable the window
2) simulate the mouse input
3) set a timer
4) when the timer fires, disable the window

everything works fine. However, if I,

1) enable the window
2) simulate the mouse input
4) disable the window

then the cursor is not repositioned.

I have not used Spy. I've used it in the past for other things. To me, this seems more like a "timing" or "understanding the mechanics" issue.
0
 
LVL 22

Accepted Solution

by:
nietod earned 100 total points
ID: 1412746
I missunderstood the timer part.

I believe I know what the problem is.  The window is part from a different thread and the mouse input is being posted (keyboard and mouse input is posted, not sent) to the window's queue.  but it is not removed and handled at that time.  
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412747
What happens is that the window is enabled, the message is posted, the window is disabled and your program continues.  Latter on the other window's thread resumes and gets the message, but it is now disabled.

There are a bunch of approaches to fix this.  The one I would use is to use SendMessage to send WM_LBUTTONDOWN and other mouse messages to the window  (Other people have used this technique and it is known to work pretty well).  SendMessage() will cause the thread of the window that gets the message to resume and process the message before SendMessage() returns to your process.  

Let me know if you need more details.
0
 

Author Comment

by:flfmdll
ID: 1412748
I've increased the points a bit. I think you are onto the right solution. However, my very original approach was to use PostMessage. This didn't work (probably for the reasons your stated). I tried SendMessage and still that didn't work. So I decided to get a little pushy and use mouse_event. This seems to work better than my other approaches. But it still has the flaw I've mentioned. A lot of people think that what I am doing is purely wrong. i.e: controlling one app by another. Whatever.

So if you have more specific ideas on what I can do with my current code or even very specifically how I can change it to do what you are talking about, please let me know.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412749
What you are doing is wrong.  If the Microsoft police come in the night an take you away, well, you've been warned.  

Alot of questions deal with "breaking the rules" because Microsoft has done a good job of making it difficult to break the rules and has not done a good job eliminating the NEED to break the rules.

Just a guess, but the problem with SendMessage() was probably a coordinate issue.  The coordinates for a button down (or up, or double-click) message should be in client coordinates, not screen coordinates.  Make sure you convert the coordinates t the target window's client area before sending the message.  If not, let me know.  It can be made to work.
0
 

Author Comment

by:flfmdll
ID: 1412750
I've changed the code listed above. I commented out the mouse_event function calls (everything else remains the same). I then added the following code in place of the first mouse_event. It doesn't work. What am I doing wrong?

-------------------------------------------------------------------------------------------------------------------------
        {
            POINT cpt;
            LPARAM lparam;
            WPARAM wparam;

            cpt.x = pt.x;
            cpt.y = pt.y;

            ScreenToClient(&cpt);

            SendMessage(WM_MOUSEMOVE, wparam, MAKELPARAM(cpt.x, cpt.y));

            SendMessage(WM_LBUTTONDOWN, wparam, MAKELPARAM(cpt.x, cpt.y));

            SendMessage(WM_MOUSEMOVE, wparam, MAKELPARAM(cpt.x, cpt.y));

            SendMessage(WM_LBUTTONUP, wparam, MAKELPARAM(cpt.x, cpt.y));
        }

0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 
LVL 22

Expert Comment

by:nietod
ID: 1412751
Sorry I didn't respond.  I didn't get notification of your comment.  I just looked in now to see what was going on.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412752
I don't see anything wrong.  But there are a couple of things I'm not seeig that could be wrong.  You do not show how you set the wparam that is sent with mouse messages.  It should be set to MK_LBUTTON for the messagees where the button is down.  0 otherwise.  Also I don't see you enabling/disabling the window.  If you aren't doing that, you still need to.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412753
If that doesn't help.  Use Spy++ or a similar tool to compare the messages that the window gets when you simulate the click to the messages the window gets when it actually gets the click.
0
 

Author Comment

by:flfmdll
ID: 1412754
I don't know. I do have the EnableWindow. I use it when my MDI catches the left mouse down. So, I catch left mouse down in the MDI, run through the previously mentioned code, set the timer, timer fires, and then disable the window. This works fine with the mouse_event, but not with the SendMessage.

The code below is based on your suggested changes. I tried using Spy++. It doesn't see the messages either with mouse_event or SendMessage. It does see right mouse down messages.

I raised the points again. This is getting silly. I'm about to change over to trying to inject a mouse procedure into the other process.

          {
              POINT cpt;
              LPARAM lparam;
              WPARAM wparam;

              cpt.x = pt.x;
              cpt.y = pt.y;

              ScreenToClient(&cpt);

              wparam = MK_LBUTTON;

              SendMessage(WM_MOUSEMOVE, wparam, MAKELPARAM(cpt.x, cpt.y));

              SendMessage(WM_LBUTTONDOWN, wparam, MAKELPARAM(cpt.x, cpt.y));

              SendMessage(WM_MOUSEMOVE, wparam, MAKELPARAM(cpt.x, cpt.y));

              wparam = 0;

              SendMessage(WM_LBUTTONUP, wparam, MAKELPARAM(cpt.x, cpt.y));
          }
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412755
Wait a second.  Is this MFC?  I don't know how I missed it befure but you do not specify a window handle to SendMessage or ScreenToClient.  I assume that means this is for MFC (not my earea of expertise).  However, you need to specify a window object for the target window otherwise it is using the current window object.  That means it is sending the message to itself.
0
 

Author Comment

by:flfmdll
ID: 1412756
I tried that. Still nothing. I appreciate your help. I'm sure if you saw this monster of a code you would say, "Oh, that's the problem." It's difficult.

If I set my timer to 1000 milliseconds, i can quickly click a second time and the other app does get the mouse click. It misses the first one though.

DLL INJECTION!
0
 
LVL 22

Expert Comment

by:nietod
ID: 1412757
I'm not sure what you are saying about clicking a second time.  Is this a 2nd simulated click?  

Can you post the code the way it is now or, if it is too big, email it to me. (nietod@theshop.net).  If it is big try to point me to the right area/areas.

Finally what are you hopping to acheive?  Although it should be possible to get this to work, there might be a way all around this.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

This article shows a few slightly more advanced techniques for Windows 7 gadget programming, including how to save and restore user settings for your gadget and how to populate the "details" panel that is displayed in the Windows 7 gadget gallery.  …
This article describes how to programmatically preset the "Pages per Sheet" option that's available with most printer drivers.   This setting lets you do "n-Up" printing, where two, four, or more pages are printed on each sheet of paper. If your …
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now