Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 553
  • Last Modified:

Simulating a mouse click

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
flfmdll
Asked:
flfmdll
  • 9
  • 5
1 Solution
 
nietodCommented:
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
 
flfmdllAuthor Commented:
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
 
nietodCommented:
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
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
nietodCommented:
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
 
flfmdllAuthor Commented:
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
 
nietodCommented:
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
 
flfmdllAuthor Commented:
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
 
nietodCommented:
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
 
nietodCommented:
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
 
nietodCommented:
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
 
flfmdllAuthor Commented:
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
 
nietodCommented:
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
 
flfmdllAuthor Commented:
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
 
nietodCommented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 9
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now