I am getting the automation element on windows using mouse cordinates(x &y)
static Rect m_bRect = new Rect(0, 0, 0, 0);
AutomationElement element = AutomationElement.FromPoint(new Point((double)Cursor.Position.X, (double)Cursor.Position.Y));
m_bRect = element.Current.BoundingRectangle;
i want to achieve one functionality that if user cursor position is not inside element Boundingrectangle then Block the input calls using API user32.dll, for that i have written below code on mouse down operation.
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern void BlockInput([In, MarshalAs(UnmanagedType.Bool)]bool fBlockIt);
[DllImport("User32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref POINT p);
private void HookManager_MouseDown(object sender, MouseEventArgs e)
{
POINT l_objPoint = new POINT();
bool l_bretVal = GetCursorPos(ref l_objPoint);
while (!m_bRect .Contains(l_objPoint.X, l_objPoint.Y)
{
BlockInput(true);
break;
}
}
But above code is not working as expected, it takes user clicks even if user cursor is not inside automation element boundingrectangle.
I have also tried another approach using timer tick please check below code but here Automationelement is not captured correctly.
private void OnHovering()
{
System.Windows.Forms.Timer _timerOnHover = new System.Windows.Forms.Timer();
_timerOnHover.Tick += new EventHandler(_timerOnHovering_Tick);
_timerOnHover.Interval = 100;
_timerOnHover.Start();
}
private void _timerOnHovering_Tick(object sender, EventArgs e)
{
POINT currentPoint = new POINT();
bool l_bretVal = GetCursorPos(ref currentPoint);
if (!l_bretVal)
{
return;
}
if (!m_bRect.Contains(currentPoint.X, currentPoint.Y && !ISmouseMove)
{
BlockInput(true);
}
else
{
BlockInput(false);
}
}
}
Related
I am making a recorder that records a specific window the user chooses. While I am recording, the program forces the window to be active all the time. It does not let me minimize it or even move it. It forced me to use alt + f4(that kills the active window) because I was not able to stop the recording. Is there a way to fix that?
Combobox code:
private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
if (Handlebox.SelectedIndex == 0)
{
handle_name = Handlebox.Items[0].ToString();
}
else if (Handlebox.SelectedIndex == 1)
{
handle_name = Handlebox.Items[1].ToString();
}
else if (Handlebox.SelectedIndex == 2)
{
handle_name = Handlebox.Items[2].ToString();
}
else if (Handlebox.SelectedIndex == 3)
{
handle_name = Handlebox.Items[3].ToString();
}
else if (Handlebox.SelectedIndex == 4)
{
handle_name = Handlebox.Items[4].ToString();
}
else if (Handlebox.SelectedIndex == 5)
{
handle_name = Handlebox.Items[5].ToString();
}
}
The way I add all the processes in the combobox:
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle))
{
Handlebox.Items.Add(process.ProcessName);
}
}
The script that the whole recording takes place:
// Record video:
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
public Bitmap CaptureApplication(string procName)
{
Process proc;
// Cater for cases when the process can't be located.
try
{
proc = Process.GetProcessesByName(procName)[0];
}
catch (IndexOutOfRangeException e)
{
return null;
}
// You need to focus on the application
SetForegroundWindow(proc.MainWindowHandle);
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
// Delay
Thread.Sleep(1000);
Rect rect = new Rect();
IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);
// sometimes it gives error.
while (error == (IntPtr)0)
{
error = GetWindowRect(proc.MainWindowHandle, ref rect);
}
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics.FromImage(bmp).CopyFromScreen(rect.left,rect.top,0,0,new Size(width, height),CopyPixelOperation.SourceCopy);
return bmp;
}
public void RecordVideo()
{
// Keep track of time:
watch.Start();
// Save screenshot:
string name = tempPath + "//screenshot-" + fileCount + ".png";
CaptureApplication(handle_name).Save(name, ImageFormat.Png);
inputImageSequence.Add(name);
fileCount++;
// Dispose of bitmap:
CaptureApplication(handle_name).Dispose();
}
Apologies for the brevity and style of this reply; I'm currently on mobile.
Could you express your combobox event handler as:
private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
if (Handlebox.SelectedIndex == -1) return;
handle_name = Handlebox.SelectedItem.ToString();
}
I'm not sure what exactly you want this application to do eventually, but If all you want is to gracefully exit, I would look into registering an event handler that watches for a key combination, and stops and saves the recording when it is pressed. I can't easily type out how to do that, but some concerted googling should help you.
I have added a DateTimePicker in ContextMenuStrip using ToolStripControlHost. I am using below code.
DateTimePicker startDate = new DateTimePicker();
(cms.Items["mnuStartDate"] as ToolStripMenuItem).DropDownItems.Add(new ToolStripControlHost(startDate));
Selecting of date is working but when I clicked on the arrows to go to the previous or next month, or if I clicked on the Month, the ToolStrip is closing. Is there any workaround here? Thanks.
Note: I am showing the cms(ContextMenuStrip ) when Right Clicking on a control.
Looks like the ToolStrip behavior closes when an item is selected. So I juts used CalendarMonth Control as a replacement. Link here
It seems to be related to Application.EnableVisualStyles(); When not-enabled, the menu doesn't unexpectedly close.
When enabled, to prevent closing, detect if the CalendarMonth menu is currently visible, and if the mouse was clicked on it.
public class DatePicker : DateTimePicker {
private const int DtmFirst = 0x1000;
private const int DtmGetmonthcal = (DtmFirst + 8);
private bool isDroppedDown = false;
private IntPtr hwndCalendarWindow = IntPtr.Zero;
protected override void OnDropDown(EventArgs e) {
isDroppedDown = true;
hwndCalendarWindow = SendMessage(Handle, DtmGetmonthcal, 0, 0);
base.OnDropDown(e);
}
protected override void OnCloseUp(EventArgs eventargs) {
isDroppedDown = false;
base.OnCloseUp(eventargs);
}
public bool CalendarMonthClicked(Point pt) {
if (!isDroppedDown)
return false;
RECT r = new RECT();
GetWindowRect(hwndCalendarWindow, out r);
return pt.X >= r.Left && pt.X <= r.Right && pt.Y >= r.Top && pt.Y <= r.Bottom;
}
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("User32.dll")]
private static extern IntPtr SendMessage(IntPtr h, int msg, int param, int data);
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
public int Left, Top, Right, Bottom;
}
}
static class Program {
//[DllImport("uxtheme.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
//public extern static Int32 SetWindowTheme(IntPtr hWnd, String textSubAppName, String textSubIdList);
[STAThread]
static void Main() {
Application.EnableVisualStyles(); // if you disable visual styles then the menu won't close
DatePicker dpStart = new DatePicker();
dpStart.MinimumSize = new Size(100, 50);
Form ff = new Form();
ContextMenuStrip cms = new ContextMenuStrip();
ToolStripMenuItem menu = new ToolStripMenuItem("Menu");
menu.DropDown.Closing += (o, e) => {
e.Cancel = (e.CloseReason == ToolStripDropDownCloseReason.AppClicked && dpStart.CalendarMonthClicked(Cursor.Position));
};
// sub-menu doesn't close properly, probably related to the ToolStripControlHost
cms.Opening += delegate {
menu.DropDown.Close(ToolStripDropDownCloseReason.CloseCalled);
};
cms.Closed += delegate {
menu.DropDown.Close(ToolStripDropDownCloseReason.CloseCalled);
};
ff.ContextMenuStrip = cms;
cms.Items.Add(menu);
ToolStripControlHost item = new ToolStripControlHost(dpStart) { AutoSize = true };
menu.DropDownItems.Add(item);
Application.Run(ff);
}
}
im trying to get the name of each window i click on. I have working code but its using system timers instead of timers on the form. i will post code so its eaiser to see what im doing wrong. its also not letting me refer back to my text box, i think i need to bring it into the function.
heres the Dll imports and variables
private static string LastActiveWindow = "";
private static string ActiveWindowName = "";
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWind, StringBuilder lpString, int nMaxCount);
and then i have my timer running 10 times a second which will run the active window function:
private void TimerActiveWindow_Tick(object sender, EventArgs e)
{
ActiveWindow();
}
private static void ActiveWindow(object Obj, EventArgs e)
{
IntPtr hwnd = GetForegroundWindow();
const int Capacity = 512;
var text = new StringBuilder(Capacity);
try
{
if (GetWindowText(hwnd, text, Capacity) > 0)
{
if (ActiveWindowName != text.ToString())
{
if (!LastActiveWindow.Equals(text.ToString()))
{
// TxtBody.text += "<br><font color = purple> Current Window - [" + text.ToString() + "]</font><br>";
LastActiveWindow = text.ToString();
MessageBox.Show(text.ToString());
}
}
}
}
catch { }
}
The only difference is the timer which on my program which uses the system timer looks like this
System.Timers.Timer TimerActiveWindow = new System.Timers.Timer();
TimerActiveWindow.Elapsed += new ElapsedEventHandler(Program.ActiveWindow);
TimerActiveWindow.AutoReset = true;
TimerActiveWindow.Interval = 100;
what i need to do is that I need to control another application installed on the same machine using my custom application. For example if i needed to use the standard windows calculator I would simply send the input events to the calculator. I have used some code snippets to make this possible and I have now triggered both mouse and keyboard events. but the problem is that i can be sure that the keyboard event will hit the target application because it has the process handle. but i cannot be sure about the mouse. and also if the target application goes into background, i cannot initiate mouse clicks on it. I need help to find a way to make sure that the mouse click is done on the application only.
I need to send mouse co-ordinates and click as well. for example "sendMouseClick("Notepad", 100, 400); which will send a click to Notepad, even though it stays minimized.
IMPORTANT NOTE
A similar question is answered previously but that is in reference to first finding the state of the other application and then sending the inputs either keyboard or mouse, what i need to do is to send an application a set of instructions that must work whether the application is in foreground or not.
For "The other Guys":: if you dont want to help or cant help, thats okay but please do know that i havent stolen the question or anything. I simply want to achieve this task in C#.
The code I have to simulate keyboard key Press is:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
namespace SimulateKeyPress
{
partial class Form1 : Form
{
private Button button1 = new Button();
private Button button2 = new Button();
private Button button3 = new Button();
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
public Form1()
{
button1.Location = new Point(10, 10);
button1.TabIndex = 1;
button1.Text = "Click to automate Calculator";
button1.AutoSize = true;
button1.Click += new EventHandler(button1_Click);
button2.Location = new Point(150, 140);
button2.TabIndex = 0;
button2.Text = "Click to Exit Calculator";
button2.AutoSize = true;
button2.Location = new Point(80, 80);
button2.TabIndex = 2;
button2.Text = "Click to Run Calculator";
button2.AutoSize = true;
button2.Click += new EventHandler(button2_Click);
this.DoubleClick += new EventHandler(Form1_DoubleClick);
this.Controls.Add(button1);
this.Controls.Add(button2);
// this.Controls.Add(button3);
}
// Get a handle to an application window.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);
// Activate an application window.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
// Send a series of key presses to the Calculator application.
private void button1_Click(object sender, EventArgs e)
{
// Get a handle to the Calculator application. The window class
// and window name were obtained using the Spy++ tool.
IntPtr calculatorHandle = FindWindow("CalcFrame", "Calculator");
//Process firstProc = new Process();
//firstProc.StartInfo.FileName = "calc.exe";
//firstProc.EnableRaisingEvents = true;
//firstProc.Start();
// Verify that Calculator is a running process.
if (calculatorHandle == IntPtr.Zero)
{
MessageBox.Show("Calculator is not running.");
return;
}
// Make Calculator the foreground application and send it
// a set of calculations.
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("1024");
SendKeys.SendWait("*");
SendKeys.SendWait("32");
SendKeys.SendWait("=");
}
private void button2_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start("calc.exe");
}
private void button3_Click(object sender,EventArgs e)
{
Process [] proc =Process.GetProcessesByName("Calculator");
proc[0].Kill();
}
// Send a key to the button when the user double-clicks anywhere
// on the form.
private void Form1_DoubleClick(object sender, EventArgs e)
{
// Send the enter key to the button, which raises the click
// event for the button. This works because the tab stop of
// the button is 0.
SendKeys.Send("{ENTER}");
}
}
}
the previous help on stack overflow, msdn and other sites provides the code to simulate a mouse click in the same application. But i need to send mouse hits to another application.
Maybe this could help you
Code
The task
Getting the mouse's current position
Sending the mouse event
Windows forms
...
using System.Runtime.InteropServices;
namespace yournamespace
{
public partial class yourclassname
{
[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;
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
}
}
WPF
Things are a bit harder in WPF
double mousePointX;
double mousePointY;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
private void WritePoint(object sender, RoutedEventArgs e)
{
POINT p;
if (GetCursorPos(out p))
{
System.Console.WriteLine(Convert.ToString(p.X) + ";" + Convert.ToString(p.Y));
}
}
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
private Point ConvertPixelsToUnits(int x, int y)
{
// get the system DPI
IntPtr dDC = GetDC(IntPtr.Zero); // Get desktop DC
int dpi = GetDeviceCaps(dDC, 88);
bool rv = ReleaseDC(IntPtr.Zero, dDC);
// WPF's physical unit size is calculated by taking the
// "Device-Independant Unit Size" (always 1/96)
// and scaling it by the system DPI
double physicalUnitSize = (1d / 96d) * (double)dpi;
Point wpfUnits = new Point(physicalUnitSize * (double)x,
physicalUnitSize * (double)y);
return wpfUnits;
}
private void WriteMouseCoordinatesInWPFUnits()
{
POINT p;
if (GetCursorPos(out p))
{
Point wpfPoint = ConvertPixelsToUnits(p.X, p.Y);
System.Console.WriteLine(Convert.ToString(wpfPoint.X) + ";" + Convert.ToString(wpfPoint.Y));
mousePointY = wpfPoint.Y;
mousePointX = wpfPoint.X
}
}
Now the most important part of the code
[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;
...
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, Convert.ToUInt32(mousePointX), Convert.ToUInt32(mousePointY), 0, 0);
...
Warning
The code is tested
The code is not a "copy & paste code
MessageBox.Show shows a window, and until the MessageBox window is not closed, clicking the main app will still let the MessageBox window have focus and make the MessageBox window flash. Same behavior is with the TaskDialog API, Form.ShowDialog(IWin32Window) and Window.ShowDialog().
How can I do this for a different process like Process.Start("notepad.exe")?
This hack may do the trick. but be sure you question yourself twice before using this.
private void DoEvil()
{
var windowField = typeof(Control).GetField("window", BindingFlags.Instance |
BindingFlags.NonPublic);
Form notepad = new Form();
NativeWindow window = (NativeWindow)windowField.GetValue(notepad);
var process = Process.Start("notepad.exe");
process.EnableRaisingEvents = true;
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(1);
}
window.AssignHandle(process.MainWindowHandle);
Control.CheckForIllegalCrossThreadCalls = false;
EventHandler handler = (s, ev) => notepad.DialogResult = DialogResult.OK;
process.Exited += handler;
notepad.ShowDialog(this);
process.Exited -= handler;
Control.CheckForIllegalCrossThreadCalls = true;
}
Warning: Don't try this at home, office or anywhere :p
Ok, so this is what I have come up with, not the greatest or most elegant way to do it nor does it contains any error handling but I think it works,
What I do in my code is create a dummy form (Form2) with a Panel control and assign the parent of EXE to the handle (InPtr) of the panel. I have removed to the border of the form to make it look as flush as possible but there is still a noticeable flicker between the EXE loading and the SetParent method being called.
Form1 Code
private void button4_Click(object sender, EventArgs e)
{
using (var f2 = new Form2())
{
f2.ShowDialog(this);
}
}
Form2 Code
public partial class Form2 : Form
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
private readonly BackgroundWorker worker = new BackgroundWorker();
private IntPtr mainHandle;
private IntPtr processHandle;
private Panel panel;
public Form2()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.CenterParent;
AddPanel();
}
private void AddPanel()
{
panel = new Panel {Dock = DockStyle.Fill};
mainHandle = panel.Handle;
Controls.Add(panel);
}
private void Form2_Load(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Invoke((MethodInvoker) Close);
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
var process = Process.Start("cmd.exe", "/k echo echo");
if (process != null)
{
while (process.MainWindowHandle == IntPtr.Zero)
{
// Add some sort of timeout here, infintite loops are bad!!!
}
processHandle = process.MainWindowHandle;
// Get the size of the EXE window and apply it to this form.
var size = GetSize(processHandle);
Invoke((MethodInvoker) delegate { Size = new Size(size.Width, size.Height);});
// Hook the parent of the EXE window to this form
SetHandle(processHandle);
// Make sure the windows is positions at location x = 0, y = 0 of this form
SetWindowPos(processHandle, IntPtr.Zero, 0, 0, size.Width, size.Height, SetWindowPosFlags.SWP_ASYNCWINDOWPOS);
// wait for the EXE to terminate
process.WaitForExit();
// Unhook the closed process window
SetParent(processHandle, IntPtr.Zero);
}
}
private void SetHandle(IntPtr ptr)
{
if (ptr != IntPtr.Zero)
SetParent(processHandle, mainHandle);
}
private static Size GetSize(IntPtr hWnd)
{
RECT pRect;
var size = new Size();
GetWindowRect(hWnd, out pRect);
size.Width = pRect.Right - pRect.Left;
size.Height = pRect.Bottom - pRect.Top;
return size;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[Flags]
private enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
}
Look here: Wait till a process ends
In short, you can either use "process.WaitForExit();" to pause execution till the process exits, but I don't think this will flash the window or auto-focus to that process.
If you want the fancier UI stuff you're going to have to do something like this:
while (!process.HasExited)
{
//update UI
}
For switching the focus to another process, this should get you started.
EDIT:
Here's more info on flashing the window.
Ok, here's a solution that might work:
Start the process with the following code:
Process p = new Process("cmd");
p.Start();
p.WaitForExit();
Now, in the event handler for activating your application (active), set the focus on the process again. You can use process.HasExited to verify that you process is still running or not.
Note: I haven't tested it, but I think it should bring you close.