2
votes

I am trying to automatically draw in a program similar to paint by simulating mouse movement and clicks with user32.dll in Windows 7.

Here's what I have and how I use it:

Setup

    [DllImport("user32.dll")]
    static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);


    [Flags]
    public enum MouseEventFlags
    {
        LEFTDOWN = 0x00000002,
        LEFTUP = 0x00000004,
        MIDDLEDOWN = 0x00000020,
        MIDDLEUP = 0x00000040,
        MOVE = 0x00000001,
        ABSOLUTE = 0x00008000,
        RIGHTDOWN = 0x00000008,
        RIGHTUP = 0x00000010
    }

    public void LeftMouseDown()
    {
        mouse_event((int)(MouseEventFlags.LEFTDOWN), Cursor.Position.X, Cursor.Position.Y, 0, 0);
    }

    public void LeftMouseUp()
    {
        mouse_event((int)(MouseEventFlags.LEFTUP), Cursor.Position.X, Cursor.Position.Y, 0, 0);
    }

When Drawing

            foreach (var contour in contours)
            {
                LeftMouseDown();
                foreach (var point in contour)
                {
                    var x = point.X + offsetX;
                    var y = point.Y + offsetY;
                    Cursor.Position = new Point(x, y);
                    //LeftMouseDown();
                    System.Threading.Thread.Sleep(2);
                }
                LeftMouseUp();
            }

What I'm trying to simulate is the mouse being clicked and held, moved around to a bunch of points in each contour and then let up before moving on to the next contour.

The problem, is that this just holds the mouse button down for the first movement and then lets it up.

TL;DR

How can I keep the mouse left click down when the mouse is being moved around programatically?

I am trying to simulate drawing in a third party app. (Microsoft LINQ's Whiteboard, the IM client.)

2
Don't you expect it to let up when LeftMouseUp is called? Your problem doesn't seem very well defined. - M.Babcock
Is the intent to simulate drawing in your own app or control a different one? If it's your own, why fake the mouse at all - just draw? - John Arlen
In a different one. Microsoft LINQ's Whiteboard. If I just press the mouse and move 1 px and then repeat, it generates too many lines and LINQ gets slow. If it is all in one continuous left click it registers it as one line. - Jason
You are only simulating clicks, not moves. Add a MouseMove() method. - Hans Passant
@Blankasaurus, don't you mean LIN[C]'s Whiteboard? :) - Bruno Brant

2 Answers

2
votes

Summarizing the comment trail: this has not the expected result because the code only simulates mouse clicks, not mouse motion. Moving the cursor with Cursor.Position directly changes the cursor position, bypassing the Windows input event queue. So doesn't generate any notifications (WM_MOUSEMOVE messages) to the window that has the focus.

Fix by adding a MouseMove() method to the helper class that uses mouse_event() with MouseEventFlags.MOVE

1
votes

A late response, but I think the mistake you have is in calling mouse_event. Basically, mouse_event reports relative mouse movement (when without MOUSEEVENTF_ABSOLUTE) but you pass absolute position thus making it act wrongly. Also, you should move the cursor to the position to press down the mouse button before performing mouse down.

So here's my fix:

// I changed a bit of the method signature, but that doesn't really matter
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
static extern void mouse_event(MouseEventFlags flags, uint dx, uint dy, uint delta, IntPtr extraInfo);

[Flags]
enum MouseEventFlags : uint
{
    Absolute = 0x8000,
    LeftDown = 0x0002,
    LeftUp = 0x0004,
    MiddleDown = 0x0020,
    MiddleUp = 0x0040,
    Move = 0x0001,
    RightDown = 0x0008,
    RightUp = 0x0010,
    Wheel = 0x0800,
    XDown = 0x0080,
    XUp = 0x0100,
    HWheel = 0x1000,
}

public void LeftMouseDown()
{
    // Simulate left down, notice that RELATIVE movement is 0
    mouse_event(MouseEventFlags.LeftDown, 0, 0, 0, IntPtr.Zero);
}

public void LeftMouseUp()
{
    // Simulate left up, notice that RELATIVE movement is 0 too
    mouse_event(MouseEventFlags.LeftUp, 0, 0, 0, IntPtr.Zero);
}

And when moving mouse...

foreach (var contour in contours)
{
    // simulate mouse down AFTER cursor is moved to the first point (IMPORTANT!)
    var x = contour[0].X + offsetX;
    var y = contour[0].Y + offsetY;
    Cursor.Position = new Point(x, y);
    LeftMouseDown();
    foreach (var point in contour)
    {
        x = point.X + offsetX;
        y = point.Y + offsetY;
        Cursor.Position = new Point(x, y);
        System.Threading.Thread.Sleep(2);
    }
    // cursor is already at the final position
    LeftMouseUp();
}

And a side-note, via some simple testing, setting Cursor.Position does generate WM_MOUSEMOVE message.