Home All Groups Group Topic Archive Search About

Trouble with mouse click

Author
10 Oct 2006 1:22 PM
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

Author
10 Oct 2006 5:38 PM
Chris Dunaway
Morten Snedker wrote:
Show quoteHide quote
> 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.
Author
11 Oct 2006 6:02 PM
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
Author
12 Oct 2006 8:30 PM
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