Jump to content

Problems with mouse events in Windows 7


R0cc0
 Share

Recommended Posts

Hello people,

VERY long time lurker and first time poster here. I have a problem that has finally forced me to start asking questions where ever I can get answers. Since I am using AutoIt then I figured this was the best place to start.

I am attempting to clone mouse events to multiple windows which have no controls. But it seems that no matter what I try I cannot get mouse events to register on an inactive window.

Here is an example of code that does not work on my system:

#include <Misc.au3> ; needed for _IsPressed()
#include <WindowsConstants.au3> ; needed for $WS_POPUP
#region "my code"
$x = (@DesktopWidth/2)-50
$y = (@DesktopHeight/2)-50
;create 4 identical guis 50 pixels apart with a button
$gui1 = GUICreate("gui1", 50, 50, $x, $y, $WS_POPUP)
GUISetBkColor(0xFF0000)
$btn1 = GUICtrlCreateButton("btn1", 5, 15, 40, 20)
GUISetState()
$gui2 = GUICreate("gui2", 50, 50, $x + 50, $y, $WS_POPUP)
GUISetBkColor(0xFF0000)
$btn2 = GUICtrlCreateButton("btn2", 5, 15, 40, 20)
GUISetState()
$gui3 = GUICreate("gui3", 50, 50, $x, $y + 50, $WS_POPUP)
GUISetBkColor(0xFF0000)
$btn3 = GUICtrlCreateButton("btn3", 5, 15, 40, 20)
GUISetState()
$gui4 = GUICreate("gui4", 50, 50, $x + 50, $y + 50, $WS_POPUP)
GUISetBkColor(0xFF0000)
$btn4 = GUICtrlCreateButton("btn4", 5, 15, 40, 20)
GUISetState()
; press esc to quit
HotKeySet("{ESC}", "Quit")
; set mouse coords relative to the active window
Opt("MouseCoordMode", 0)
; set mouse coords relative to the client area of the active window
; Opt("MouseCoordMode", 2)
While 1

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  _MouseClickPlus(WinGetTitle($gui2), "left", $pos[0], $pos[1])
  ; _MouseClickPlus(WinGetTitle($gui3), "left", $pos[0], $pos[1])
  ; _MouseClickPlus(WinGetTitle($gui4), "left", $pos[0], $pos[1])

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf

; check for gui events
$msg = GUIGetMsg()

Select
Case $msg = $btn4
  ConsoleWrite("btn4 pressed" & @LF)
Case $msg = $btn3
  ConsoleWrite("btn3 pressed" & @LF)
Case $msg = $btn2
  ConsoleWrite("btn2 pressed" & @LF)
Case $msg = $btn1
  ConsoleWrite("btn1 pressed" & @LF)
EndSelect

Sleep(10)
WEnd
Func Quit()
Exit
EndFunc
#endregion "my code"
#region "code by others"
;===============================================================================
;
; Function Name:  _MouseClickPlus()
; Version added:  0.1
; Description:  Sends a click to window, not entirely accurate, but works
;               minimized.
; Parameter(s):   $Window   =  Title of the window to send click to
;               $Button  =  "left" or "right" mouse button
;               $X     =  X coordinate
;               $Y     =  Y coordinate
;               $Clicks  =  Number of clicks to send
; Remarks:    You MUST be in "MouseCoordMode" 0 to use this without bugs.
; Author(s):      Insolence <insolence_9@yahoo.com>
;
;===============================================================================
Func _MouseClickPlus($Window, $Button = "left", $X = "", $Y = "", $Clicks = 1)
  Local $MK_LBUTTON =  0x0001
  Local $WM_LBUTTONDOWN   =  0x0201
  Local $WM_LBUTTONUP   =  0x0202

  Local $MK_RBUTTON =  0x0002
  Local $WM_RBUTTONDOWN   =  0x0204
  Local $WM_RBUTTONUP   =  0x0205
  Local $WM_MOUSEMOVE   =  0x0200

  Local $i            = 0

  Select
  Case $Button = "left"
     $Button     =  $MK_LBUTTON
     $ButtonDown =  $WM_LBUTTONDOWN
     $ButtonUp   =  $WM_LBUTTONUP
  Case $Button = "right"
     $Button     =  $MK_RBUTTON
     $ButtonDown =  $WM_RBUTTONDOWN
     $ButtonUp   =  $WM_RBUTTONUP
  EndSelect

  If $X = "" OR $Y = "" Then
     $MouseCoord = MouseGetPos()
     $X = $MouseCoord[0]
     $Y = $MouseCoord[1]
  EndIf

  For $i = 1 to $Clicks
     DllCall("user32.dll", "int", "SendMessage", "hwnd", WinGetHandle($Window), "int", $WM_MOUSEMOVE, "int", 0, "long", _MakeLong($X, $Y))
      
     DllCall("user32.dll", "int", "SendMessage", "hwnd", WinGetHandle($Window), "int", $ButtonDown, "int", $Button, "long", _MakeLong($X, $Y))
      
     DllCall("user32.dll", "int", "SendMessage", "hwnd", WinGetHandle($Window), "int", $ButtonUp, "int", $Button, "long", _MakeLong($X, $Y))
  Next
EndFunc
Func _MakeLong($LoWord,$HiWord)
  Return BitOR($HiWord * 0x10000, BitAND($LoWord, 0xFFFF))
EndFunc
#endregion "code by others"

What should happen is when I attempt to click btn1 then a click should register on btn2 (or btn3, btn4, etc... depending on what I uncomment). The button controls only exist for this example, not in my actual script. So I have no controlID to work with, only a window handle. But if I can get this example to work then I can integrate the solution into my script. If you try this example and it works, please post here with your system specs. If it's working for you on Windows 7 then I will be surprised.

My system specs:

CPU: i7-2600k 4.5Ghz

OS: Windows 7 Ultimate x64

Things I have tried:

ControlClick (AutoIt Native)

SendInput (User32.dll) - This works like a charm. Although it took me a little bit of time to figure out the DllStruct. :D However, it only works if I activate each window which is to receive the mouse events. This can become quite a problem as the number of windows which will receive the events gets larger. Since the user will have to wait for the script to cycle through them all (takes about 50-60 ms per window in my script if I want the clicks to register on the window), this is not a viable option.

PostMessage (User32.dll) - No response and no errors generated (GetLastError). I've tried the old default DLL structure as well as an attempted modification for use on 64-bit systems as per a post I found in this forum.

SendMessage (User32.dll) - Same as PostMessage. (Also used in _MouseClickPlus)

I've also toyed around with mouse hooks a little. But I don't fully understand them yet. NOTE: I do not have a threadID for the windows receiving mouse events.

If anyone knows a solution to this issue it would be greatly appreciated. I have just about used up my Google-fu on this problem.

Edit: Oops, I found a mistake in my original example code. I have updated the code but the results have not changed. I was sending a window handle to _MouseClickPlus not the title. This happened because I grabbed that function for this example, but in my source script I am making a direct DllCall using the handle.

Edited by R0cc0
Link to comment
Share on other sites

Edit the code as follows:

If _IsPressed(Hex(1)) Then
 
  $pos = MouseGetPos()
  ; ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)
 
  ;_MouseClickPlus($gui2, "left", $pos[0], $pos[1]) <<<<< commented out from example
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])
 
  ControlClick($gui2, '', '', "left", 1, $pos[0], $pos[1]); added
 
  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd
 
EndIf

That doesn't work either. Maybe it does on other operating systems or architectures? But not on mine. (In fact, I'm sure it probably will work on 32-bit Windows XP for example)

I can't use the controlID. I could use it for this example, but it would be pointless since the windows I am working with have no controls. It would give me a false success.

Link to comment
Share on other sites

Even this does not work for me:

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  ; _MouseClickPlus($gui2, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])

  WinActivate($gui2)
  ControlClick($gui2, '', '', "left", 1, $pos[0], $pos[1])

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf

It works using the controlID, but I still have to activate the window like with SendInput:

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  ; _MouseClickPlus($gui2, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])

  WinActivate($gui2)
  ControlClick($gui2, '', $btn2, "left", 1)

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf
Edited by R0cc0
Link to comment
Share on other sites

As far as I can see you are not testing for a result from ControlClick, and how would you know if it was successful

or not if there is no control present on the target gui? Would it not just be clicking the body of it?

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

As far as I can see you are not testing for a result from ControlClick, and how would you know if it was successful

or not if there is no control present on the target gui? Would it not just be clicking the body of it?

Sorry for the delay. Kids...

My mistake. I forget that others haven't been working in-depth on my script and therefore don't have all of the insight which I do about it. I know where it's failing so I don't need to see the error. Here are 4 examples. 3 of them do not press btn2, and none of them return an error (just like GetLastError does not return an error from PostMessage or SendMessage and MSDN states that under certain circumstances it will not generate an error code.) All of them report success. Yet these are the only changes to the example script. Which means the failure is occurring where the changes are regardless of what the error code states.

Also, if the click is not interacting with a control then it most certainly is not interacting with a control-less window. Which is the entire point of the example.

Fails but returns success:

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  ; _MouseClickPlus($gui2, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])

  ;WinActivate($gui2)
  $ret = ControlClick($gui2, '', '', "left", 1, $pos[0], $pos[1])
  If $ret = 0 Then ConsoleWrite("ControlClick failed" & @LF)
  If $ret = 1 Then ConsoleWrite("ControlClick successful" & @LF)

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf

This also fails and returns success:

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  ; _MouseClickPlus($gui2, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])

  WinActivate($gui2)
  $ret = ControlClick($gui2, '', '', "left", 1, $pos[0], $pos[1])
  If $ret = 0 Then ConsoleWrite("ControlClick failed" & @LF)
  If $ret = 1 Then ConsoleWrite("ControlClick successful" & @LF)

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf

This also fails and returns success:

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  ; _MouseClickPlus($gui2, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])

  ;WinActivate($gui2)
  $ret = ControlClick($gui2, '', $btn2, "left", 1)
  If $ret = 0 Then ConsoleWrite("ControlClick failed" & @LF)
  If $ret = 1 Then ConsoleWrite("ControlClick successful" & @LF)

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf

However, this is successful since it shows btn2 as being pressed in the console. It also shows successful.

If _IsPressed(Hex(1)) Then

  $pos = MouseGetPos()
  ConsoleWrite("X: " & $pos[0] & @LF & "Y: " & $pos[1] & @LF)

  ; _MouseClickPlus($gui2, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui3, "left", $pos[0], $pos[1])
  ; _MouseClickPlus($gui4, "left", $pos[0], $pos[1])

  WinActivate($gui2)
  $ret = ControlClick($gui2, '', $btn2, "left", 1)
  If $ret = 0 Then ConsoleWrite("ControlClick failed" & @LF)
  If $ret = 1 Then ConsoleWrite("ControlClick successful" & @LF)

  While _IsPressed(Hex(1))
   Sleep(10)
  WEnd

EndIf
Link to comment
Share on other sites

Did you read this in the help file?

Some controls will resist clicking unless they are the active window. Use the WinActivate() function to force the control's window to the top before using ControlClick().

It would certainly explain the difference.

"$ret = ControlClick($gui2, '', '', "left", 1, $pos[0], $pos[1])"

The above is most likely clicking the gui behind the button when the window is active.

Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Did you read this in the help file?

It would certainly explain the difference.

"$ret = ControlClick($gui2, '', '', "left", 1, $pos[0], $pos[1])"

The above is most likely clicking the gui behind the button when the window is active.

Yes, I did. I'm only showing you what ControlClick is doing since you said that was the way to go. It will not work at all unless the window is active on my system, and still doesn't work as it's intended when it does work. I believe this is because ControlClick uses SendMessage or PostMessage to send the mouse events. W/e the case, the functions it is using will not work on my system unless the window is active and that's exactly what I am trying to get around.

I do not believe native Autoit to hold an answer for me at this time. I'll be lucky to find an answer in C++, which is what Autoit was coded with IIRC. But I am here to see if someone knows something which I don't.

Link to comment
Share on other sites

Well I'm unsure myself what method it uses, perhaps you could look at the last public source code which is

available to find out.

Maybe you are using a software someone has automated before, try searching for that on the forum

if you havent already, or post it here.

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Well I'm unsure myself what method it uses, perhaps you could look at the last public source code which is

available to find out.

Maybe you are using a software someone has automated before, try searching for that on the forum

if you havent already, or post it here.

I can't post the source for my script, it's proprietary. I've also done a search for much more than that, lol. It really doesn't matter to me why it's not working if there's nothing I can do about it. If there is an answer it's going to involve DllCalls no matter what. As I already said I can do this just fine with SendInput as well, but only after activating the window.

It's more about functionality than appearance. I need function over beauty right now. If I'm cloning to 20 windows it takes almost a full second to cycle through activating the windows and clicking what I need to. That's just not professional on top of making for an less than perfect appearance. Right now I'm just looking for clicks, I haven't even mentioned drags yet. :D

The bottom line is I will have to use whatever language I have to use to get everything I need. I haven't explored .Net yet, but I may have to go that way.

Link to comment
Share on other sites

You seem to think I'm missing some point, I am not.

I'm aware of what you want to do, and you are on an autoit forum so that automatically means you are attempting it in autoit.

Neither do I care about your source code, you are obviously automating third party software, so that's what I was suggesting you search for or name. There is no secret voodoo function.

If the apps are .net, or delphi etc.. then it's probably best to use the same to automate them

if autoit functions do not work.

Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

I don't know if it can be done with .Net or not. That is a potential road to get past my issues that I haven't explored yet. It seems to me that the UIPI is blocking the input from Send/PostMessage when the window is not active.

I am not in search of a "secret voodoo function."

I am looking for someone else who may have had this same issue but perhaps had different information to pool with mine. My operating system is blocking these functions. Perhaps I just need to adjust a token or something? It's that sort of feedback I'm looking for.

Link to comment
Share on other sites

There is plenty of examples on the forum of others have the same issue, which reduces to not being able

to interact with a gui or control while it is not active.

With all the searching you have done, I have to assume you noticed that the majority of those that

were resolved one way or another are those that named the software they were trying to interact

with, and the language in which it was developed.

Best of luck.

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

There is plenty of examples on the forum of others have the same issue, which reduces to not being able

to interact with a gui or control while it is not active.

With all the searching you have done, I have to assume you noticed that the majority of those that

were resolved one way or another are those that named the software they were trying to interact

with, and the language in which it was developed.

Best of luck.

If you can't get the example to work then how is naming a software of any kind going to change that? The example is using my own virtual property and it doesn't work.

Thanks for your help.

Link to comment
Share on other sites

What example are you referring to?

If it's that code in your first post where it's trying to use a click function to click a button from a gui created within itself

and from the same loop, is does not make sense to me.

Or do you mean the example in the helpfile for control click does not work?

But more importantly, the software does matter, as does the language it is written in.

If you have a problem naming it, then find another similar from the same language, and test with that.

I promise you that being all vague and cyber shifty wont help your cause my friend.

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

What example are you referring to?

If it's that code in your first post where it's trying to use a click function to click a button from a gui created within itself

and from the same loop, is does not make sense to me.

Or do you mean the example in the helpfile for control click does not work?

But more importantly, the software does matter, as does the language it is written in.

If you have a problem naming it, then find another similar from the same language, and test with that.

I promise you that being all vague and cyber shifty wont help your cause my friend.

I love these forums. Tell us everything, even proprietary knowledge or else you'll receive no help from us. No, an example is not good enough. If the knowledge leads to being of a topic that is against the rules: locked!

If you can't understand the example then you are not the person to help me anyway. Being all cyber shitty to me isn't going to change that either. I'll find the answer in C++, if there is one, and port it just like I've had to do for most other complex operations beyond the scope of the limited Autoit language. I come here seeking real help from professional people. Not kiddies looking to start a thread war.

Link to comment
Share on other sites

I love these forums. Tell us everything, even proprietary knowledge or else you'll receive no help from us. No, an example is not good enough. If the knowledge leads to being of a topic that is against the rules: locked!

If you can't understand the example then you are not the person to help me anyway. Being all cyber shitty to me isn't going to change that either. I'll find the answer in C++, if there is one, and port it just like I've had to do for most other complex operations beyond the scope of the limited Autoit language. I come here seeking real help from professional people. Not kiddies looking to start a thread war.

See, you're just making stuff up now, maybe because you got 'shifty' confused with 'shitty' and feel a little insulted.

Sorry you feel that way, I have been trying to help you, but you are correct that I am not an Autoit Pro.

I'll leave you to the pros now, sorry for the confusion.

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

See, you're just making stuff up now, maybe because you got 'shifty' confused with 'shitty' and feel a little insulted.

Sorry you feel that way, I have been trying to help you, but you are correct that I am not an Autoit Pro.

I'll leave you to the pros now, sorry for the confusion.

Yes, I am feeling insulted. Not only because of Shifty->Shitty. But because of my perception of your attitude towards me.

Want a program name? Fine, MSPaint. I want to clone "all" mouse events to 20 instances of MSPaint without having to activate each window. This includes drags. I await a "Professional" response but already know I'll not be getting an answer in such a response.

As for making stuff up: what are you referring to? If you mean this:

I love these forums. Tell us everything, even proprietary knowledge or else you'll receive no help from us. No, an example is not good enough. If the knowledge leads to being of a topic that is against the rules: locked!

I would be happy to start linking threads to support this statement.

As I stated, I will find the answer elsewhere. The answer is not in native Autoit anyhow. I'm going to have to port from C++ if there's even a solution to the issue.

Link to comment
Share on other sites

Stop your bleating, you are like a little baby.

This works perfectly fine for me on windows 7

For $i = 100 To 200
ControlClick("Untitled - Paint","", "[CLASS:Afx:bb0000:8; INSTANCE:1]", "Primary",1,$i,100)
Next

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Stop your bleating, you are like a little baby.

This works perfectly fine for me on windows 7

For $i = 100 To 200
ControlClick("Untitled - Paint","", "[CLASS:Afx:bb0000:8; INSTANCE:1]", "Primary",1,$i,100)
Next

Thank you for proving my point. I have been using Autoit at the expert level since way before you even joined here. Just try and send a "mouse drag" operation using that function... It doesn't hold down clicks. It sends both the up and down clicks in one shot.

NEXT!

Edited by R0cc0
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...