views:

136

answers:

3

I have multiple cursors (which are actually forms) that can be controlled by their respective mouse. (1 cursor for 1 user).

I use SetCursorPos to position the default cursor (the original system cursor) in a position that will not take away the focus from my application, and use ShowCursor(false) to hide it.

I have a class that gets the handle of the mouse and the coordinates.

When the user clicks I use the SetCursorPos and the mouse_event to simulate the clicks in that particular position.

My simulated mouse clicks only work on certain components' OnClick event (It was supposed to be only buttons and labels, but I experimented with the stuff on my project just to know what will or won't work):

It works on:

  • Buttons (TButton, TBitBtn, TAdvSmoothButton)
  • TAdvGrid
  • TMenuItem (but the direct child of the TMainMenu only)
  • TRadioButton

It doesn't work on:

  • TLabel
  • Panels (TPanel, TAdvSmoothPanel)
  • TCoolBar
  • TMenuItem (not direct child of TMainMenu)

This is my code:

 SetCursorPos(currentX , currentY);
 mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
 mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

Why doesn't it work on some components? Is there a workaround (because I would like to be able to click labels using mouse_event).

EDIT: I tried checking if the clicking function was really called, so I put ShowMessage('clicked'); before the SetCursorPos and mouse_event...but strangely everything (minor edit: everything except MenuItems) works fine now (except for the fact that I have a Message popping out everytime I try to click something). Does anybody have an idea why this behaves that way?

+2  A: 

mouse_event is actually deprecated, you should use SendInput instead, see if that fixes anything (I would also suggest making the mouse move an input message, over using SetCursorPos), also, if your doing this for a specific application, PostMessage might be a much better and simpler alternative

Necrolis
Is there a tutorial or sample code for using SendInput? (I'm so terrible at using winapi stuff)I don't know how to use PostMessage to click at a specific place (via coordinates), I only know how to click a certain component.
Dian
this should be very helpful (although game related, code is code): http://forum.codecall.net/classes-code-snippets/13649-c-keyboard-mouse-emulation.html
Necrolis
Thank you for your help. :D
Dian
+1  A: 

It works by accident right now, those components have probably captured the mouse. You need to pass the mouse pointer coordinates in the 2nd and 3rd arguments. Thus:

 //SetCursorPos(currentX , currentY);
 mouse_event(MOUSEEVENTF_LEFTDOWN, currentX, currentY, 0, 0);
 mouse_event(MOUSEEVENTF_LEFTUP, currentX, currentY, 0, 0);
Hans Passant
I tried it, but now I can't click anything. :(
Dian
Use a tool like Spy++ to see where those clicks are going.
Hans Passant
You _are_ passing _screen_ coordinates, right?
Paul-Jan
Yes, screen coordinates.
Dian
+1  A: 

Seems to work here;

procedure TForm1.Panel1Click(Sender: TObject);
begin
  ShowMessage('Click');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Pt: TPoint;
begin
  Pt := Panel1.ClientToScreen(Point(0, 0));
  SetCursorPos(Pt.x, Pt.y);
//  SetCursorPos(Panel1.ClientOrigin.x, Panel1.ClientOrigin.y);
  mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
  mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
end;

or, without SetCursorPos;

procedure TForm1.Button1Click(Sender: TObject);
var
  Pt: TPoint;
begin
  Pt := Panel1.ClientToScreen(Point(0, 0));
  Pt.x := Round(((Pt.x + 1) * 65535) / Screen.Width);
  Pt.y := Round(((Pt.y + 1) * 65535) / Screen.Height);
  mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_MOVE or MOUSEEVENTF_LEFTDOWN,
      Pt.x, Pt.y, 0, 0);
  mouse_event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_MOVE or MOUSEEVENTF_LEFTUP,
      Pt.x, Pt.y, 0, 0);
end;
Sertac Akyuz
Thanks for answering but I tried to implement both of your suggestions and it still didn't work. Now it was getting really fishy, so I tried to check with a ShowMessage in the function and now I think the problem may lie elsewhere (see my question edit).
Dian
Oh and thanks for the code btw, I will keep it for future reference. :D
Dian
@Dian - I've read your edit, it's difficult for anyone to make a guess without knowing/seeing a broader picture... Don't know if it's a far fetch but try with replacing your `ShowMessage` with a `ReleaseCapture;`, if it works you can find out who's capturing the mouse with `GetCaptureControl`/`GetCapture`.
Sertac Akyuz
Sadly, it still doesn't work... I discovered that the `ShowMessage` worked for everything except MenuItems. Yeah, I know, it's like guesswork... I added a little more description (first few lines) to the question (if that matters?).
Dian
@Dian - how does not the sprite-forms do not process the simulated clicks, but the controls beneath them does? The forms and the hardware cursor are not at the same coordinates? Or are you hiding the forms just before a click?
Sertac Akyuz
Hmmmm...never really thought about that since it worked on every button (because I did lots of buttons tests before I moved on to other components). No I don't move the forms before the click, should I? Does `SWP_NOACTIVATE` have anything to do with it not processing the click?
Dian
Tried the Hide and Show thing. It worked more or less the same without the Hide and Show, though I think I'm keeping the code because it makes sense that way. :D
Dian
@Dian >..SWP_NOACTIVATE.. - No, it only causes the window not to be activated when `SetWindowPos` is called, a click on the window still activates it, even if it wasn't the case it's not a reason for ignoring clicks... I'm actually surprised the way it worked before, maybe some of your clicks were going through some transparent parts of your forms. But if it were the case hiding the forms would straight it out...
Sertac Akyuz
The weird thing is I still can't trigger the onclick event of the Panels, Labels, MenuItems.. I even resorted to setting the cursor on the exact coordinates of the label. Even more weird is that the code works fine when I place it in other methods, but I really need it on the method which differentiates the mousehandles. :(
Dian