Send click to background window relative position in panel with C# - c#

I use C#, i have an app with a button1, button2 and a panel1, if i click to button1 it open an external program to panel1, then if i click to button2 it send a background click to the window, works fine.
But the problem is this click position is not relative to the panel and not go to right place. It send the click where are the cursor actually.
Code pieces what i use for that:
PerformRightClick(proc.MainWindowHandle, new Point(54, 42));
void PerformRightClick(IntPtr hwnd, Point point)
{
var pointPtr = MakeLParam(point.X, point.Y);
SendMessage(hwnd, WM_MOUSEMOVE, IntPtr.Zero, pointPtr);
SendMessage(hwnd, WM_RBUTTONDOWN, IntPtr.Zero, pointPtr);
SendMessage(hwnd, WM_RBUTTONUP, IntPtr.Zero, pointPtr);
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));

Based on my test, we need to use panel.Handle when we want to send click to panel.
Here is a code example you can refer to.
private const Int32 WM_MOUSEMOVE = 0x0200;
private const Int32 WM_RBUTTONDOWN = 0x0204;
private const Int32 WM_RBUTTONUP = 0x0205;
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
IntPtr MakeLParam(int x, int y) => (IntPtr)((y << 16) | (x & 0xFFFF));
private void button1_Click(object sender, EventArgs e)
{
PerformRightClick(panel1.Handle, new Point(20, 30));
}
void PerformRightClick(IntPtr hwnd, Point point)
{
var pointPtr = MakeLParam(point.X, point.Y);
SendMessage(hwnd, WM_MOUSEMOVE, IntPtr.Zero, pointPtr);
SendMessage(hwnd, WM_RBUTTONDOWN, IntPtr.Zero, pointPtr);
SendMessage(hwnd, WM_RBUTTONUP, IntPtr.Zero, pointPtr);
}
private void panel1_Click(object sender, EventArgs e)
{
MessageBox.Show("Panel is clicked");
}
private void button2_Click(object sender, EventArgs e)
{
PerformRightClick(this.Handle, new Point(20, 30));
}
private void Form1_Click(object sender, EventArgs e)
{
MessageBox.Show("Form is clicked");
}
Pic:
Like the above picture, when we click button1, the panel is clicked and the Form is clicked when we click button2.

Related

How to send mouse events to a minimized form in c#?

I try to write a simple tester tool, it testing web site (win form, using WebBrowser control).
I need to send mouse click and keystrokes to the site.
It works when the form is on top, but i would like to run the tester in the background. How can i send mouse click, keystrokes to a minimized/background form?
Current mouse event code:
[DllImport( "user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall )]
public static extern void mouse_event( uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo );
[Flags]
public enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004,
MIDDLEDOWN = 0x00000020,
MIDDLEUP = 0x00000040,
MOVE = 0x00000001,
ABSOLUTE = 0x00008000,
RIGHTDOWN = 0x00000008,
RIGHTUP = 0x00000010
}
void mouseEvent( uint flag, Point p )
{
p = caller.PointToScreen( p );
Cursor.Position = p;
mouse_event( flag, (uint) 0, (uint) 0, (uint) 0, (UIntPtr) 0 );
}
public void sendMouseClick( Point p )
{
uint flag = (uint) MouseEventFlags.LEFTDOWN + (uint) MouseEventFlags.LEFTUP;
mouseEvent( flag, p );
}
-- Edited:
I tried the SendMessage but didn't works :(
Currently i try to use a simple from with 2 buttons, no web browser, just normal windows.Form and buttons. i try to click button1 from code when i push the button2. :)
// On the form, when i press the button 2 then minimize, wait, and try to press the button1
private void button2_Click( object sender, EventArgs e)
{
// this.RaiseMouseEvent();
MouseHelper mh = new MouseHelper(this.Text);
this.WindowState = FormWindowState.Minimized;
Thread.Sleep( 2000 );
this.Refresh();
Thread.Sleep(2000);
mh.SendMouseClick( 25,25 );
}
private void button1_Click( object sender, EventArgs e )
{
throw new Exception( "BUTTON 1 CLICKED" );
}
// In the MouseHelper I call the left click
public void SendMouseClick( int p_x, int p_y )
{
Int32 l_parm1 = (p_y << 16) | (p_x & 0xffff);
SendMessage( windowPtr, WM_LBUTTONDOWN, 0, l_parm1 );
SendMessage( windowPtr, WM_LBUTTONUP, 0, l_parm1 );
}
public MouseHelper( String windowTitle )
{
windowPtr = FindWindowByCaption( IntPtr.Zero, windowTitle );
}
// defintions
public const uint WM_LBUTTONDOWN = 0x0201;
public const uint WM_LBUTTONUP = 0x0202;
[DllImport( "user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall )]
public static extern int SendMessage(
IntPtr hWnd,
uint Msg,
Int32 wParam,
Int32 lParam
);
What I did wrong?
It doesn't work even the window is not minimezed:( The 1st solution works when window is active, but the 2nd not :(
I tried with 25,25 and 147,47 (result of PoinToScreen of 25,25)
Probably SendMessage would work.
See SendMessage and System Defined Messages (more specifically here)
Something like:
SendMessage(hwnd, WM_LBUTTONDOWN, 0, (123<<16)|(456));
SendMessage(hwnd, WM_LBUTTONUP, 0, (123<<16)|(456));

Send mouse click to minimized window. I don't want to maximize or move the mouse

I want to simulate a mouseclick at a specific position inside a window that is hidden. So no maximizing the window or moving the mouse. I just want to send the right message.
Now I tried something like this
DllImport("user32.dll")]
public static extern int PostMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
static int WM_LBUTTONDOWN = 0x0201;
static int WM_LBUTTONUP = 0x0202;
private static IntPtr MakeLParam(int LoWord, int HiWord)
{
return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
}
public static void test()
{
int windowPtr = FindWindow(null, "Calculator");
Console.WriteLine("pointer: " + windowPtr);
int x = 260;
int y = 180;
IntPtr lParam = MakeLParam(x, y);
IntPtr windowPointer = new IntPtr(windowPtr);
PostMessage(windowPointer, WM_LBUTTONDOWN, IntPtr.Zero, lParam);
PostMessage(windowPointer, WM_LBUTTONUP, IntPtr.Zero, lParam);
}
I expected this program to click the 8 on the calculator ( if you set it to scientific ), but it doesn't do that.
How can I simulate that click? Please don't suggest finding the windows-control-element for the "8"-Button, this is not what I am trying to do here.

Send mouse click to another window c#

I want to simulate a left-click at the window (background) x/y coordinates.
I have searched and found a lot of stuff to simulate mouseclicks to other windows but it does not work 100% well.
My code:
private const int WM_LBUTTONDOWN = 0x201; //Left mousebutton down
private const int WM_LBUTTONUP = 0x202; //Left mousebutton up
[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
public static extern bool PostMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
public void SimulateMouseLeft(IntPtr hwnd, int x,int y)
{
int coordinates = x | (y << 16);
//PostMessage(hwnd, WM_LBUTTONDOWN, IntPtr.Zero, (IntPtr)coordinates);
//PostMessage(hwnd, WM_LBUTTONUP, IntPtr.Zero, (IntPtr)coordinates);
PostMessage(hwnd, WM_LBUTTONDOWN, (IntPtr)0x1, (IntPtr)coordinates);
PostMessage(hwnd, WM_LBUTTONUP, (IntPtr)0x1, (IntPtr)coordinates);
//send left button up
//PostMessage(hwnd, 0x202, (IntPtr)0x1, (IntPtr)coordinates);
}
If I start this code, and simulate a click to another window, then the other window gets a left click but not at x/y. The click is at the same position then my mouse is and after the click my mouse jumps to another position on the screen.
Thanks for your help!

Send Win API paste cmd from background C# app

Goal: write a C# app that runs in the background, listens for the key combination Win-V, and when that occurs, pastes the clipboard contents into the current active window (some arbitrary app). Essentially I'm trying to mimic PureText, but I'm not bothering to convert the text to plain text first.
Problem: pasting into the currently active windows is not working.
Details: To listen in the background for key presses I'm using the globalKeyboardHook class from A Simple C# Global Low Level Keyboard Hook. I'm able to catch Win-V events, but I'm not able to send the paste command properly. I can send the paste by using the functions SendKeys.Send or keybd_event. However, they send another "V" press down the pipeline which gets caught by the gkh_KeyDown event and causes multiple paste events to fire.
I'm expecting that I need to use SendMessage or PostMessage, but all my attempts to do that have failed so far. Below is the full code with the last function, SendCtrlV, being the one of interest. The comments explain everything I've tried so far. Can you see what I'm missing?
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Utilities;
namespace KeyHookTest
{
public partial class Form1 : Form
{
private bool LWin_down;
private bool V_down;
globalKeyboardHook gkh = new globalKeyboardHook();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static public extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
gkh.HookedKeys.Add(Keys.V);
gkh.HookedKeys.Add(Keys.LWin);
gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
}
void gkh_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.LWin)
LWin_down = false;
else
V_down = false;
}
void gkh_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.LWin)
LWin_down = true;
else
V_down = true;
if (LWin_down && V_down)
{
LogDebug("Enter Win+V");
try
{
SendCtrlV();
}
catch { }
}
}
private void SendCtrlV()
{
uint KEYEVENTF_KEYUP = 2;
int KEYDOWN = 0x0100;
int KEYUP = 0x0101;
byte KEY_LCONTROL1 = 0x11;
IntPtr KEY_LCONTROL2 = new IntPtr(0x11);
byte KEY_V1 = 0x56;
IntPtr KEY_V2 = new IntPtr(0x56);
int WM_PASTE1 = 0x302;
uint WM_PASTE2 = 0x302;
IntPtr hWnd = GetForegroundWindow();
// Works, but causes multiple gkh_KeyDown to fire so it's slow and buggy
/*keybd_event(KEY_LCONTROL1, 0, 0, 0);
keybd_event(KEY_V1, 0, 0, 0);
keybd_event(KEY_V1, 0, KEYEVENTF_KEYUP, 0);
keybd_event(KEY_LCONTROL1, 0, KEYEVENTF_KEYUP, 0);*/
// Works, but causes multiple gkh_KeyDown to fire so it's slow and buggy
//SendKeys.Send("^v");
// Doesn't work, causes UAC prompt
//SendKeys.Send("{^}v");
// Doesn't work, nothing gets pasted to the foregroundwindow
//SendMessage(hWnd, WM_PASTE1, 0, 0);
// Doesn't work, nothing gets pasted to the foregroundwindow
//PostMessage(hWnd, WM_PASTE2, IntPtr.Zero, IntPtr.Zero);
// Doesn't work, nothing gets pasted to the foregroundwindow
/*SendMessage(hWnd, KEYDOWN, KEY_LCONTROL1, 0);
SendMessage(hWnd, KEYDOWN, KEY_V1, 0);
SendMessage(hWnd, KEYUP, KEY_V1, 0);
SendMessage(hWnd, KEYUP, KEY_LCONTROL1, 0);*/
// Doesn't work, nothing gets pasted to the foregroundwindow
/*PostMessage(hWnd, 0x0100, KEY_LCONTROL2, IntPtr.Zero);
PostMessage(hWnd, 0x0100, KEY_V2, IntPtr.Zero);
PostMessage(hWnd, 0x0101, KEY_V2, IntPtr.Zero);
PostMessage(hWnd, 0x0101, KEY_LCONTROL2, IntPtr.Zero);*/
}
private void LogDebug(string msg)
{
string logpath = Environment.GetEnvironmentVariable("USERPROFILE") + #"\Desktop\KeyHookTest.txt";
File.AppendAllText(logpath, DateTime.Now.ToString("HH:mm:ss:fff") + ": " + msg + "\r\n");
}
}
}
These additional links helped lead me to the answer:
How to get active child window
How can I find the active child window?
Here's what's working for me:
private void SendCtrlV()
{
IntPtr hWnd = GetFocusedHandle();
PostMessage(hWnd, WM_PASTE, IntPtr.Zero, IntPtr.Zero);
}
static IntPtr GetFocusedHandle()
{
var info = new GuiThreadInfo();
info.cbSize = Marshal.SizeOf(info);
if (!GetGUIThreadInfo(0, ref info))
throw new Win32Exception();
return info.hwndFocus;
}
It works, but you must use the TextBox's native window handle if you want it to be effective

Synchronize Scroll Position of two RichTextBoxes?

In my application's form, I have two RichTextBox objects. They will both always have the same number of lines of text. I would like to "synchronize" the vertical scrolling between these two, so that when the user changes the vertical scroll position on one, the other scrolls the same amount. How might I go about doing this?
Thanks Jay for your answer; after some more searching I also found the method described here. I'll outline it below for anyone else interested.
First, declare the following enums:
public enum ScrollBarType : uint {
SbHorz = 0,
SbVert = 1,
SbCtl = 2,
SbBoth = 3
}
public enum Message : uint {
WM_VSCROLL = 0x0115
}
public enum ScrollBarCommands : uint {
SB_THUMBPOSITION = 4
}
Next, add external references to GetScrollPos and SendMessage.
[DllImport( "User32.dll" )]
public extern static int GetScrollPos( IntPtr hWnd, int nBar );
[DllImport( "User32.dll" )]
public extern static int SendMessage( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam );
Finally, add an event handler for the VScroll event of the appropriate RichTextBox:
private void myRichTextBox1_VScroll( object sender, EventArgs e )
{
int nPos = GetScrollPos( richTextBox1.Handle, (int)ScrollBarType.SbVert );
nPos <<= 16;
uint wParam = (uint)ScrollBarCommands.SB_THUMBPOSITION | (uint)nPos;
SendMessage( richTextBox2.Handle, (int)Message.WM_VSCROLL, new IntPtr( wParam ), new IntPtr( 0 ) );
}
In this case, richTextBox2's vertical scroll position will be synchronized with richTextBox1.
I did this for a small project a while ago, and here's the simplist solution I found.
Create a new control by subclassing RichTextBox:
public class SynchronizedScrollRichTextBox : System.Windows.Forms.RichTextBox
{
public event vScrollEventHandler vScroll;
public delegate void vScrollEventHandler(System.Windows.Forms.Message message);
public const int WM_VSCROLL = 0x115;
protected override void WndProc(ref System.Windows.Forms.Message msg) {
if (msg.Msg == WM_VSCROLL) {
if (vScroll != null) {
vScroll(msg);
}
}
base.WndProc(ref msg);
}
public void PubWndProc(ref System.Windows.Forms.Message msg) {
base.WndProc(ref msg);
}
}
Add the new control to your form and for each control explicitly notify the other instances of the control that its vScroll position has changed. Somthing like this:
private void scrollSyncTxtBox1_vScroll(Message msg) {
msg.HWnd = scrollSyncTxtBox2.Handle;
scrollSyncTxtBox2.PubWndProc(ref msg);
}
I think this code has problems if all the 'linked' controls don't have the same number of displayable lines.
[Visual Studio C# 2010 Express, v10.0.30319 on a Windows 7 64bit installation]
I've used Donut's solution posted above, but found a problem when scrolling to the end of RichTextBoxes that contain many lines.
If the result of GetScrollPos() is >0x7FFF then when nPos is shifted, the top bit is set. The creation of the IntPtr with the resulting wParam variable will then fail with an OverflowException. You can easily test this with the following (the second line will fail):
IntPtr ip = new IntPtr(0x7FFF0000);
IntPtr ip2 = new IntPtr(0x80000000);
A version of SendMessage() that uses UIntPtr would appear to be a solution, but I couldn't get that to work. So, I've use the following:
[DllImport("User32.dll")]
public extern static int SendMessage(IntPtr hWnd, uint msg, UInt32 wParam, UInt32 lParam);
This should be good up to 0xffff, but would fail after that. I've not yet experienced a >0xffff result from GetScrollPos(), and assume that User32.dll is unlikely to have a 64bit version of SendCommand(), but any solutions to that problem would be greatly appreciated.
const int WM_USER = 0x400;
const int EM_GETSCROLLPOS = WM_USER + 221;
const int EM_SETSCROLLPOS = WM_USER + 222;
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref Point lParam);
private void RichTextBox1_VScroll(object sender, EventArgs e)
{
Point pt;
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
private void RichTextBox2_VScroll(object sender, EventArgs e)
{
Point pt;
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
A variation of Jay's subclass approach can be found in Joseph Kingry's answer here: Synchronizing Multiline Textbox Positions in C#.
Joseph's approach also subclasses but doesn't require a _VScroll event handler. I used that approach to do a 3-way bind between 3 boxes and added WM_HSCROLL.
#Sudhakar MuthuKrishnan's answer needs some fixes, but works. Thanks!
First GetScrollPos which rised event and then set scroll position for others.
private void RichTextBox1_VScroll(object sender, EventArgs e)
{
Point pt = new Point();
SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}
private void RichTextBox2_VScroll(object sender, EventArgs e)
{
Point pt = new Point();
SendMessage(RichTextBox2.Handle, EM_GETSCROLLPOS, 0, ref pt);
SendMessage(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, ref pt);
}

Categories

Resources