Trouble with mouse click

M

Morten Snedker

If I have a number of random applications open, move the mouse cursor
to a given position and do a click, the application gets the focus.

That is what this simple code should illustrate:

Dim pt As Point
Dim wnd As IntPtr

Const WM_LBUTTONUP = &H202 '//LButton up
Const WM_LBUTTONDOWN = &H201 '//LButton down

pt.X = 450
pt.Y = 200

Windows.Forms.Cursor.Position = pt

wnd = WindowFromPoint(pt.X, pt.Y)
Call SendMessage(wnd, WM_LBUTTONDOWN, 0, 0&)
Call SendMessage(wnd, WM_LBUTTONUP, 0, 0&)


It moves the cursor, but the application beneith doesn't get focus, as
it would had i clicked on that position manually. What am I missing
out on?

Regards /Snedker
 
C

Chris Dunaway

Morten said:
If I have a number of random applications open, move the mouse cursor
to a given position and do a click, the application gets the focus.

That is what this simple code should illustrate:

Dim pt As Point
Dim wnd As IntPtr

Const WM_LBUTTONUP = &H202 '//LButton up
Const WM_LBUTTONDOWN = &H201 '//LButton down

pt.X = 450
pt.Y = 200

Windows.Forms.Cursor.Position = pt

wnd = WindowFromPoint(pt.X, pt.Y)
Call SendMessage(wnd, WM_LBUTTONDOWN, 0, 0&)
Call SendMessage(wnd, WM_LBUTTONUP, 0, 0&)


It moves the cursor, but the application beneith doesn't get focus, as
it would had i clicked on that position manually. What am I missing
out on?

I suggest you fire up Spy++ and then test your scenario again. When
you click on an application, besides the mouse messages, there are
other messages sent, such as WM_ACTIVATE.
 
T

teslar91

If this is a continuation of our other thread - you didn't mention you
wanted it to receive focus too, just that you wanted it clicked and
without moving the mouse. :)

Chris' reply is correct.

But if the mouse pointer is in, or will be moved to, the proper
location, this is much easier:

Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Int32, ByVal dX
As Int32, ByVal dy As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo
As Int32)

mouse_event MOUSEEVENTF_LEFTDOWN + MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
 
R

rvalstar

As Tesla... suggests indirectly, there are limited ways to simulate
mouse events in Windows. We have both mouse_events (deprecated) and
SendInput -- neither of which are managed code.

I have searched the net at length and attacked this "simple" problem
for many hours so I'm hoping my efforts will pay off for you.

This is a VB.NET 2005 test bed solution that allows you to play with
mouse_event, SendInput, and OnMouse... techniques. The OnMouse...
approach has yet to yield fruits. Either other approach (comment out
the other 2; uncomment the METHOD desired) work quite well.

This example expects a form called frmMain, a picture box called
picClick, three buttons: (btnClickStart, btnClick, btnScreen), and a
multi-line text box called txtResults.

This is an amalgam primarily based on
http://vb-helper.com/howto_move_click_mouse.html and
http://www.vbforums.com/showthread.php?t=398899 -- I'm trying to update
http://www.pinvoke.net/ to share this information.

Also, this code shows how to simulate a "union struct" in VB.NET -- I
was pleased to figure this out.

Rick Valstar
Star Consulting
r + last name + at + gmail + dot + com

Here's the code:

Option Explicit On

Imports System.Runtime.InteropServices

Public Class frmMain
'Const METHOD As String = "mouse_event"
'Const METHOD As String = "OnMouse"
Const METHOD As String = "SendInput"

Const INPUT_MOUSE As Int32 = 0
Const INPUT_KEYBOARD As Int32 = 1
Const INPUT_HARDWARE As Int32 = 2

Const MOUSEEVENTF_MOVE As Int32 = &H1 ' mouse move
Const MOUSEEVENTF_LEFTDOWN As Int32 = &H2 ' left button down
Const MOUSEEVENTF_LEFTUP As Int32 = &H4 ' left button up
Const MOUSEEVENTF_RIGHTDOWN As Int32 = &H8 ' right button down
Const MOUSEEVENTF_RIGHTUP As Int32 = &H10 ' right button up
Const MOUSEEVENTF_MIDDLEDOWN As Int32 = &H20 ' middle button down
Const MOUSEEVENTF_MIDDLEUP As Int32 = &H40 ' middle button up
Const MOUSEEVENTF_ABSOLUTE As Int32 = &H8000 ' absolute move
Const MOUSEEVENTF_WHEEL As Int32 = &H800 ' wheel button rolled

Private Structure MOUSEINPUT
Dim dx As Int32
Dim dy As Int32
Dim mouseData As Int32
Dim dwFlags As Int32
Dim time As Int32
Dim dwExtraInfo As IntPtr
End Structure

Private Structure KEYBDINPUT
Dim wVk As Int16
Dim wScan As Int16
Dim dwFlags As Int32
Dim time As Int32
Dim dwExtraInfo As IntPtr
End Structure

Private Structure HARDWAREINPUT
Dim uMsg As Int32
Dim wParamL As Int16
Dim wParamH As Int16
End Structure

<StructLayout(LayoutKind.Explicit)> _
Private Structure INPUT
<FieldOffset(0)> Dim dwType As Int32
' simulate a union struct (C) or a variant record (Pascal)
<FieldOffset(4)> Dim mi As MOUSEINPUT
<FieldOffset(4)> Dim ki As KEYBDINPUT
<FieldOffset(4)> Dim hi As HARDWAREINPUT
End Structure

Private Declare Auto Sub mouse_event Lib "user32.dll" (ByVal
dwFlags As Int32, ByVal dx As Int32, ByVal dy As Int32, ByVal cButtons
As Int32, ByVal dwExtraInfo As IntPtr)

<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SendInput(ByVal nInputs As Int32, ByRef
pInputs As INPUT, ByVal cbSize As Int32) As Int32
End Function

Private Declare Auto Sub SetLastError Lib "kernel32.dll" (ByVal
dwErrCode As Int32)

Sub ClickMouse(ByVal MouseButton As Integer)
Dim inputevents As New INPUT

inputevents.mi.dx = 0
inputevents.mi.dy = 0
inputevents.mi.mouseData = 0
inputevents.mi.dwFlags = MouseButton
inputevents.mi.time = 0
inputevents.dwType = INPUT_MOUSE

SetLastError(0)
Dim result As Integer = SendInput(1, inputevents,
Marshal.SizeOf(GetType(INPUT)))
Dim lasterror As Integer = Marshal.GetLastWin32Error

Debug.WriteLine("Result: " & result)
Debug.WriteLine("LastError: " & lasterror & " " & New
System.ComponentModel.Win32Exception(lasterror).Message)
End Sub

Sub btnClickStart_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btnClickStart.Click
txtResults.AppendText("***** btnClickStart *****" & vbCrLf)

'move mouse to the start button
Dim pt As New System.Drawing.Point(20,
Screen.PrimaryScreen.Bounds.Height - 20)
'Dim pt As Point = picClicker.PointToScreen(New
Point(picClicker.ClientRectangle.Width / 2,
picClicker.ClientRectangle.Height / 2))

Debug.WriteLine("pt " & pt.X & " " & pt.Y)

System.Windows.Forms.Cursor.Position = pt
Windows.Forms.Application.DoEvents()

ClickMouse(MOUSEEVENTF_LEFTDOWN) 'press left button
ClickMouse(MOUSEEVENTF_LEFTUP) 'release left button
End Sub

Private Sub btnClick_Click(ByVal eventSender As System.Object,
ByVal eventArgs As System.EventArgs) Handles btnClick.Click
Dim cur_x As Single
Dim cur_y As Single
Dim dest_x As Single
Dim dest_y As Single

txtResults.AppendText("***** btnClick *****" & vbCrLf)

' mouse_event moves in a coordinate system where (0, 0) is in
the upper left corner and (65535,65535) is in the lower right corner.

' Get the current mouse coordinates and convert them into this
new system.
cur_x = System.Windows.Forms.Cursor.Position.X * 65535 /
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width
cur_y = System.Windows.Forms.Cursor.Position.Y * 65535 /
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height

' Convert the coordinates of the center of the picClicker
PictureBox into this new system.
Dim pt As Point = picClicker.PointToScreen(New
Point(picClicker.ClientRectangle.Width / 2,
picClicker.ClientRectangle.Height / 2))
'Dim pt As New System.Drawing.Point(20,
Screen.PrimaryScreen.Bounds.Height - 20)

dest_x = pt.X * 65535 /
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width
dest_y = pt.Y * 65535 /
System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height

txtResults.AppendText("From " &
System.Windows.Forms.Cursor.Position.X & " " &
System.Windows.Forms.Cursor.Position.Y & " to " & pt.X & " " & pt.Y &
vbCrLf)
txtResults.AppendText("From " & cur_x & " " & cur_y & " to " &
dest_x & " " & dest_y & vbCrLf)

' Move the mouse to its final destination and click it.
Select Case METHOD
Case "OnMouse"
System.Windows.Forms.Cursor.Position = pt
Windows.Forms.Application.DoEvents()

'Dim eMouseMove As New
System.Windows.Forms.MouseEventArgs(Windows.Forms.MouseButtons.None, 0,
pt.X, pt.Y, 0)

'Me.OnMouseMove(eMouseMove)

Dim eMouseButton As New
System.Windows.Forms.MouseEventArgs(Windows.Forms.MouseButtons.Left, 1,
0, 0, 0)

Me.OnMouseDown(eMouseButton)
Me.OnMouseUp(eMouseButton)

Case "mouse_event"
mouse_event(MOUSEEVENTF_ABSOLUTE + MOUSEEVENTF_MOVE +
MOUSEEVENTF_LEFTDOWN + MOUSEEVENTF_LEFTUP, dest_x, dest_y, 0, 0)

Case "SendInput"
Dim pInputs(0 To 2) As INPUT

With pInputs(0)
.dwType = INPUT_MOUSE
.mi.dx = dest_x
.mi.dy = dest_y
.mi.mouseData = 0
.mi.dwFlags = MOUSEEVENTF_ABSOLUTE +
MOUSEEVENTF_MOVE
.mi.time = 0
.mi.dwExtraInfo = IntPtr.Zero
End With

With pInputs(1)
.dwType = INPUT_MOUSE
.mi.dx = 0
.mi.dy = 0
.mi.mouseData = 0
.mi.dwFlags = MOUSEEVENTF_LEFTDOWN
.mi.time = 0
.mi.dwExtraInfo = IntPtr.Zero
End With

With pInputs(2)
.dwType = INPUT_MOUSE
.mi.dx = 0
.mi.dy = 0
.mi.mouseData = 0
.mi.dwFlags = MOUSEEVENTF_LEFTUP
.mi.time = 0
.mi.dwExtraInfo = IntPtr.Zero
End With

'System.Windows.Forms.Cursor.Position = pt
'Windows.Forms.Application.DoEvents()

'Debug.WriteLine("Size: " & Marshal.SizeOf(pInputs(0))
& " " & Marshal.SizeOf(GetType(INPUT)))

SetLastError(0)
Debug.WriteLine("Result: " & SendInput(3, pInputs(0),
Marshal.SizeOf(GetType(INPUT))))
Debug.WriteLine("LastError: " &
Marshal.GetLastWin32Error & " " & New
System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error).Message)

'SetLastError(0)
'Debug.WriteLine("Result: " & SendInput(1, pInputs(1),
Marshal.SizeOf(GetType(INPUT))))
'Debug.WriteLine("LastError: " &
Marshal.GetLastWin32Error & " " & New
System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error).Message)

'SetLastError(0)
'Debug.WriteLine("Result: " & SendInput(1, pInputs(2),
Marshal.SizeOf(GetType(INPUT))))
'Debug.WriteLine("LastError: " &
Marshal.GetLastWin32Error & " " & New
System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error).Message)

Case Else
Beep()
End Select

'Windows.Forms.Application.DoEvents()
'My.Application.DoEvents()
End Sub

Private Sub btnScreen_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnScreen.Click
Dim I As Integer
Dim Screens() As System.Windows.Forms.Screen =
System.Windows.Forms.Screen.AllScreens

txtResults.AppendText("***** btnScreen *****" & vbCrLf)

For I = 0 To Screens.GetUpperBound(0)
txtResults.AppendText("Device Name: " +
Screens(I).DeviceName & vbCrLf)
txtResults.AppendText("Bounds: " +
Screens(I).Bounds.ToString() & vbCrLf)
txtResults.AppendText("Type: " +
Screens(I).GetType().ToString() & vbCrLf)
txtResults.AppendText("Working Area: " +
Screens(I).WorkingArea.ToString() & vbCrLf)
txtResults.AppendText("Primary Screen: " +
Screens(I).Primary.ToString() & vbCrLf)
Next I
End Sub

Private Sub picClicker_Click(ByVal eventSender As System.Object,
ByVal eventArgs As System.EventArgs) Handles picClicker.Click
txtResults.AppendText("picClicker_Click" & vbCrLf)
End Sub

Private Sub picClicker_MouseDown(ByVal sender As System.Object,
ByVal e As System.Windows.Forms.MouseEventArgs) Handles
picClicker.MouseDown
txtResults.AppendText("picClicker_MouseDown (" & e.X & ", " &
e.Y & ")" & vbCrLf)
End Sub

Private Sub picClicker_MouseUp(ByVal sender As System.Object, ByVal
e As System.Windows.Forms.MouseEventArgs) Handles picClicker.MouseUp
txtResults.AppendText("picClicker_MouseUp (" & e.X & ", " & e.Y
& ")" & vbCrLf)
End Sub

Private Sub frmMain_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Click
txtResults.AppendText("frmMain_Click" & vbCrLf)
End Sub

Private Sub frmMain_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
txtResults.AppendText("frmMain_MouseDown (" & e.X & ", " & e.Y
& ")" & vbCrLf)
End Sub

Private Sub frmMain_MouseUp(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
txtResults.AppendText("frmMain_MouseUp (" & e.X & ", " & e.Y &
")" & vbCrLf)
End Sub
End Class
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top