I am trying to open an application through C# using Process.Start() and then simulate a mouse click on the application.
When I run the application, the mouse click occurs in the left top corner, which is not what I want.
public partial class Form1 : Form
{
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, int dx, int dy,
int dwData, int dwExtraInfo);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private const int MOUSEEVENTF_ABSOLUTE = 0x8000;
private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
private const int MOUSEEVENTF_LEFTUP = 0x0004;
private const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
private const int MOUSEEVENTF_MIDDLEUP = 0x0040;
private const int MOUSEEVENTF_MOVE = 0x0001;
private const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
private const int MOUSEEVENTF_RIGHTUP = 0x0010;
private const int MOUSEEVENTF_WHEEL = 0x0800;
private const int MOUSEEVENTF_XDOWN = 0x0080;
private const int MOUSEEVENTF_XUP = 0x0100;
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
AddTextToListBox1(label1);
}).Start();
new Thread(() =>
{
// AddTextToListBox2(label2);
}).Start();
}
private void AddTextToListBox1(Label label)
{
if (label1.InvokeRequired)
{
stringDelegate sd = new stringDelegate(AddTextToListBox1);
this.Invoke(sd, new object[] { label1 });
}
else
{
label1.Text = this.ToString()+"Hi";
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "C://xyz//xyz.exe";
p.Start();
IntPtr hwnd = p.MainWindowHandle;
}
}
private void AddTextToListBox2(Label label)
{
if (label1.InvokeRequired)
{
stringDelegate sd = new stringDelegate(AddTextToListBox2);
this.Invoke(sd, new object[] { label1 });
}
else
{
label2.Text = "done";
Thread.Sleep(6000);
int X = 579;
int Y = 637;
mouse_event(MOUSEEVENTF_ABSOLUTE| MOUSEEVENTF_MOVE, X, Y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
}
}
I think mouse_event will only raise event in current app because it doesn't take hwnd on params. My experience in this area is really low, but I've done some research. Look at SendMessage function.
MSDN article:
Sends the specified message to a window or windows.
The SendMessage function calls the window procedure for the specified window
and does not return until the window procedure has processed the message.
You can send many messages with it, including mouse events
Related
I am making an AI and want it to function as a player. I use some code to make the mouse click and this works. Then I made some code to make the mouse click on random places but then it stopped working.
I changed
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
to
private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
private const int MOUSEEVENTF_LEFTUP = 0x0004;
but it did not seem to change anything except the location it pressed on.
I also made it wait 0,5 sec between mouseDown and mouseUp but it still did not work when I ran rndPlay();, when I tried to do just press1(); it did work.
public static void MoveTo(int x, int y)
{
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x, y, 0, 0);
}
public static void LeftClick()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, System.Windows.Forms.Control.MousePosition.X, System.Windows.Forms.Control.MousePosition.Y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, System.Windows.Forms.Control.MousePosition.X, System.Windows.Forms.Control.MousePosition.Y, 0, 0);
}
public static void LeftMouseClick(int x, int y)
{
MoveTo(x, y);
LeftClick();
}
public static void press1()
{
LeftMouseClick(28000, 25000);
last2Moves("1");
}
public static void press2()
{
LeftMouseClick(33000, 25000);
last2Moves("2");
}
//etc
public static void rndPlay()
{
for (int r = 0; moveSucces != true && r < 10000; r++)
{
Random rndP = new Random();
int rndPress = rndP.Next(1, 10);
if (rndPress == 1)
{
press1();
}
if (rndPress == 2)
{
press2();
}
//etc
}
}
When I run rndPlay(); I see the mouse move but it does not seem to click. Is there another way to make the mouse click in a location that would work better/faster?
Apart from using very large values for X and Y your code seems ok.
I whipped up a small example using a textbox to show when and where the mouse was clicked, connected to a simple Forms timer to simulate clicks.
public partial class Form1 : Form
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MouseLeftDown = 0x02;
private const int MouseLeftUp = 0x04;
private readonly Timer _timer = new Timer();
public Form1()
{
InitializeComponent();
_timer.Interval = 1000;
_timer.Tick += (s, a) => mouse_event(MouseLeftDown | MouseLeftUp, (uint)MousePosition.X, (uint)MousePosition.Y, 0, 0);
_timer.Enabled = true;
_textBox.Click += (s, a) => _textBox.AppendText($"Clicked at {PointToClient(MousePosition)}\n");
}
}
I want every time you start my window form, he set at the bottom of the screen (above the taskbar)
public void goBottomWindow(Form targetForm)
{
targetForm.WindowState = FormWindowState.Maximized;
targetForm.FormBorderStyle = FormBorderStyle.None;
targetForm.TopMost = true;
WinApi.SetWinFullScreen(targetForm.Handle);
}
public class WinApi
{
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which);
[DllImport("user32.dll")]
public static extern void
SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
int X, int Y, int width, int height, uint flags);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64; // 0x0040
public static int ScreenX
{
get { return GetSystemMetrics(SM_CXSCREEN); }
}
public static int ScreenY
{
get {return 60;}
}
public static void SetWinFullScreen(IntPtr hwnd)
{
SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}
}
Using this code, it leaves my window form at the top of the screen .... but what I need is the form window positioned below.
It is possible to do this?
Sorry, my English is very bad :(
Why targetForm.WindowState = FormWindowState.Maximized; ?
isn't FormWindowState.Normal better for you?
Just fetch the dimension of your desktop/screen
var rect = Screen.PrimaryScreen.WorkingArea;
targetForm.Witdh = rect.Width;
targetForm.Top = rect.Height - targetForm.Height;
I saw that in your first lines it says targetForm.TopMost = true;
That means that your form will be TOPMOST, if you want your form to be BottomMost, you should change that to false;
Hope this helps! - CCB
I'm developing an app that have to send some keys or mouse events to the active window.
I'm using this class:
Mouse
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Mouse
{
public static class VirtualMouse
{
// import the necessary API function so .NET can
// marshall parameters appropriately
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
// constants for the mouse_input() API function
private const int MOUSEEVENTF_MOVE = 0x0001;
private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
private const int MOUSEEVENTF_LEFTUP = 0x0004;
private const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
private const int MOUSEEVENTF_RIGHTUP = 0x0010;
private const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
private const int MOUSEEVENTF_MIDDLEUP = 0x0040;
private const int MOUSEEVENTF_ABSOLUTE = 0x8000;
// simulates movement of the mouse. parameters specify changes
// in relative position. positive values indicate movement
// right or down
public static void Move(int xDelta, int yDelta)
{
mouse_event(MOUSEEVENTF_MOVE, xDelta, yDelta, 0, 0);
}
// simulates movement of the mouse. parameters specify an
// absolute location, with the top left corner being the
// origin
public static void MoveTo(int x, int y)
{
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, x, y, 0, 0);
}
// simulates a click-and-release action of the left mouse
// button at its current position
public static void LeftClick()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
}
public static void RightClick()
{
mouse_event(MOUSEEVENTF_RIGHTDOWN, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
mouse_event(MOUSEEVENTF_RIGHTUP, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
}
}
}
Keyboard
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Mouse
{
public static class VirtualKeyboard
{
[DllImport("user32.dll")] static extern uint keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
public static void KeyDown(System.Windows.Forms.Keys key)
{
keybd_event((byte)key, 0, 0, 0);
}
public static void KeyUp(System.Windows.Forms.Keys key)
{
keybd_event((byte)key, 0, 0x7F, 0);
}
}
}
this is my testing code:
private void button1_Click(object sender, EventArgs e)
{
Thread.Sleep(2000);
VirtualMouse.Move(100, 100);
VirtualMouse.RightClick();
VirtualKeyboard.KeyDown(System.Windows.Forms.Keys.A);
VirtualKeyboard.KeyUp(System.Windows.Forms.Keys.A);
}
Mouse moves, but doesn't send click. Any idea?
How can I make a key continue pressed for some time? I tried using thread.sleep between KeyDown and KeyUp and it's not working.
There is an open source project on CodePlex (Microsoft's open source website)
Windows Input Simulator (C# SendInput Wrapper - Simulate Keyboard and Mouse)
http://inputsimulator.codeplex.com/
It has examples and real simple to use.
Your definitions should be Uint32:
private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x02;
private const UInt32 MOUSEEVENTF_ABSOLUTE = 0x8000;
private const UInt32 MOUSEEVENTF_LEFTUP = 0x04;
private const UInt32 MOUSEEVENTF_RIGHTDOWN = 0x08;
private const UInt32 MOUSEEVENTF_RIGHTUP = 0x10;
I’m programming one easy C# application, and i need onScroll event on Listview. So i created class ListviewEx witch inherits original ListView. I found how to detect scroll message from WinAPI and i modified WndProc method.
Now i have this WndProc:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_VSCROLL)
{
onScroll(this, new EventArgs());
}
}
But problem is, that I dont know how to detect information about scrolling. This data should be in WParam, but in C# is no LOWORD macro like in C++ and i need switch to detect parameters like SB_ BOTTOM, SB_ ENDSCROLL, SB_PAGEUP etc.
Is there any way how to replace LOWORD macro in C# ?
Or other way how to detect necessary parameters about scrolling?
Thanks for your answers. It really helped me :) Now I have what I wanted...
Here is code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Reflection;
namespace ControlsEx
{
public class ListViewEx : ListView
{
// Windows messages
private const int WM_PAINT = 0x000F;
private const int WM_HSCROLL = 0x0114;
private const int WM_VSCROLL = 0x0115;
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_KEYDOWN = 0x0100;
private const int WM_LBUTTONUP = 0x0202;
// ScrollBar types
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
// ScrollBar interfaces
private const int SIF_TRACKPOS = 0x10;
private const int SIF_RANGE = 0x01;
private const int SIF_POS = 0x04;
private const int SIF_PAGE = 0x02;
private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
// ListView messages
private const uint LVM_SCROLL = 0x1014;
private const int LVM_FIRST = 0x1000;
private const int LVM_SETGROUPINFO = (LVM_FIRST + 147);
public enum ScrollBarCommands : int
{
SB_LINEUP = 0,
SB_LINELEFT = 0,
SB_LINEDOWN = 1,
SB_LINERIGHT = 1,
SB_PAGEUP = 2,
SB_PAGELEFT = 2,
SB_PAGEDOWN = 3,
SB_PAGERIGHT = 3,
SB_THUMBPOSITION = 4,
SB_THUMBTRACK = 5,
SB_TOP = 6,
SB_LEFT = 6,
SB_BOTTOM = 7,
SB_RIGHT = 7,
SB_ENDSCROLL = 8
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch(m.Msg)
{
case WM_VSCROLL:
ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT));
onScroll(this, sargs);
break;
case WM_MOUSEWHEEL:
ScrollEventArgs sarg = new ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(this.Handle, SB_VERT));
onScroll(this, sarg);
break;
case WM_KEYDOWN:
switch (m.WParam.ToInt32())
{
case (int)Keys.Down:
onScroll(this, new ScrollEventArgs(ScrollEventType.SmallDecrement, GetScrollPos(this.Handle, SB_VERT)));
break;
case (int)Keys.Up:
onScroll(this, new ScrollEventArgs(ScrollEventType.SmallIncrement, GetScrollPos(this.Handle, SB_VERT)));
break;
case (int)Keys.PageDown:
onScroll(this, new ScrollEventArgs(ScrollEventType.LargeDecrement, GetScrollPos(this.Handle, SB_VERT)));
break;
case (int)Keys.PageUp:
onScroll(this, new ScrollEventArgs(ScrollEventType.LargeIncrement, GetScrollPos(this.Handle, SB_VERT)));
break;
case (int)Keys.Home:
onScroll(this, new ScrollEventArgs(ScrollEventType.First, GetScrollPos(this.Handle, SB_VERT)));
break;
case (int)Keys.End:
onScroll(this, new ScrollEventArgs(ScrollEventType.Last, GetScrollPos(this.Handle, SB_VERT)));
break;
}
break;
}
}
public int ScrollPosition
{
get
{
return GetScrollPos(this.Handle, SB_VERT);
}
set
{
int prevPos;
int scrollVal;
if (ShowGroups == true)
{
prevPos = GetScrollPos(this.Handle, SB_VERT);
scrollVal = -(prevPos - value);
}
else
{
// TODO: Add setScrollPosition if ShowGroups == false
}
SendMessage(this.Handle, LVM_SCROLL, (IntPtr)0, (IntPtr)scrollVal);
}
}
public event ScrollEventHandler onScroll;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
[DllImport("user32.dll")]
public static extern int SendMessage(
int hWnd, // handle to destination window
uint Msg, // message
long wParam, // first message parameter
long lParam // second message parameter
);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int wMsg,
int wParam, int lParam);
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, uint wMsg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int GetScrollPos(IntPtr hWnd, int nBar);
[StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
}
}
You can define WParam constants as followed:
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int SB_LINELEFT = 0;
private const int SB_LINERIGHT = 1;
private const int SB_PAGELEFT = 2;
private const int SB_PAGERIGHT = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_LEFT = 6;
private const int SB_RIGHT = 7;
private const int SB_ENDSCROLL = 8;
private const int SIF_TRACKPOS = 0x10;
private const int SIF_RANGE = 0x1;
private const int SIF_POS = 0x4;
private const int SIF_PAGE = 0x2;
private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
The actual code to inspect the WParam would be something like this:
if (m.Msg == WM_VSCROLL)
{
ScrollInfoStruct si = new ScrollInfoStruct();
si.fMask = SIF_ALL;
si.cbSize = (uint)Marshal.SizeOf(si);
GetScrollInfo(msg.HWnd, SB_VERT, ref si);
if (msg.WParam.ToInt32() == SB_ENDSCROLL)
{
ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, si.nPos);
onScroll(this, sargs);
}
}
pinvoke.net is a great site to get the constant values used in windows32 API without having to inspect header files yourself.
See this example
Martijn answer will work but will not catch all scrolling. The WM_VSCROLL message is only sent when the user manipulates the scroll bar directly. If the user scrolls using a mouse wheel, or uses the UpArrow/DownArrow/PageUp/PageDown keys, then the WM_VSCROLL will not be sent.
You can catch scrolling that is caused by the scroll bar and by the mouse wheel by listening for the LVN_BEGINSCROLL notification message.
Catching scrolling that occurs when using keys is harder. No message that is sent to the control when it scrolls in response to a PageUp key, for example. The best that can be done in that case is to listen for KeyPress events, and then check for changes to the scroll bar positions before and after the event.
This could, of course, be complete overkill for your purposes. The WM_VSCROLL message may be completely sufficient for what you want. But if you want to catch all possible scrolling, have a look at the code in ObjectListView which already has a Scroll event that catches all these possibilities.
#Klinki
Thank you for you cool contribution
I think you forgot to handle horizontal scroll
Case WM_HSCROLL
Dim sargs As New ScrollEventArgs(ScrollEventType.EndScroll, GetScrollPos(Me.Handle, SB_HORZ))
RaiseEvent OnScroll(Me, sargs)
Exit Select
I need to control other application by simulating mouse movement and keyboard input. How do I accomplish this in C#? Is it even possible?
Have you looked at White TestStack?
Sample code:
Application application = Application.Launch("foo.exe");
Window window = application.GetWindow("bar", InitializeOption.NoCache);
Button button = window.Get<Button>("save");
button.Click();
I don't think it can get better than that. The library is created by ThoughtWorks.
See "To send a keystroke to a different application" on this page:
http://msdn.microsoft.com/en-us/library/ms171548.aspx
You can use p/invoke, I stole the following code for mouse clicks in random spots on a button with a known handle:
[Flags]
public enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004,
MIDDLEDOWN = 0x00000020,
MIDDLEUP = 0x00000040,
MOVE = 0x00000001,
ABSOLUTE = 0x00008000,
RIGHTDOWN = 0x00000008,
RIGHTUP = 0x00000010
}
[StructLayout(LayoutKind.Sequential)]
public struct Rectangle
{
public int X;
public int Y;
public int Width;
public int Height;
}
private static void Click(IntPtr Handle)
{
lock (typeof(MouseAction))
{
Rectangle buttonDesign;
GetWindowRect(Handle, out buttonDesign);
Random r = new Random();
int curX = 10 + buttonDesign.X + r.Next(100 - 20);
int curY = 10 + buttonDesign.Y + r.Next(60 - 20);
SetCursorPos(curX, curY);
//Mouse Right Down and Mouse Right Up
mouse_event((uint)MouseEventFlags.LEFTDOWN, curX, curY, 0, 0);
mouse_event((uint)MouseEventFlags.LEFTUP, curX, curY, 0, 0);
}
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
private static extern void mouse_event(
long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out Rectangle rect);
you can use AutoIT, it's freeware, and it has a dll version that you can import into C# (DllImport). It allows you to click on controls, write strings to edit boxes, make combobox selections etc on another application from C#.
Use the SendMessage Native Win32 API. DllImport this method from the User32.dll. You can use this API to send both keyboard & mouse messages
I tried to do the same: to use the mouse i used this class
(you need to add using System.Runtime.InteropServices;)
public class MouseClick1 //public is important here**
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public int CoordX { get; set; }
public int CoordY { get; set; }
public void Click1()
{
Cursor.Position = new Point(CoordX, CoordY);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
and after on your form1 (presuming the name is form1) you can
public partial class form1 : Form
{
MouseClick1 button1 = new MouseClick1();
MouseClick1 button2 = new MouseClick1();
[...]
public void Simulate_button1and2_Click(object sender, EventArgs e)
{
button1.CoordX = 1652; //random coordinates
button1.CoordY = 225;
button2.CoordX = 1650;
button2.CoordY = 250;
button1.Click1();
button1.Click2();
}
}
To have the coordinates on your pointer, I use a timer and a label:
private void timer1_Tick(object sender, EventArgs e)
{
Coord.Text = Cursor.Position.X.ToString() + " : " + Cursor.Position.Y.ToString(); //Premet d'avoir les coord en direct
}
Work fine with me.