Detect keyup on winform without any control - c#

As stated in title, I have a form that doesn't have any control on itself (so I can't focus it!!! damn).
I keep it controlless because I need to show images on background and I need to move it by keeping mouse clicked.
Are there any way to detect the keyup event when this is the foreground window?should I use a global hook (and check which is the foreground image obviusly)?
Any simplier workaround?I tested with an hidden control but it's not working.
The problem of putting a control with opacity = 0 brings the possibility to "miss" the MouseDown and MouseUp events (because they could happen over the control instead of the form, but I can still redirect them)
Any suggestion?
Here is the question where I picked some resources:
Fire Form KeyPress event

Can't you just set the Form's KeyPreview to true and use the Form's KeyUp Event? (or am i missing something?)

I would override OnKeyUp as it seems to be exactly what you are asking for. Here is an example of popping up a Message Box when the Escape key is released.
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
MessageBox.Show("Escape was pressed");
e.Handled = true;
}
base.OnKeyUp(e);
}

It looks that you are seeking for GlobalHook. Please have a look at SetWindowsHookEx Native Api. You can easily write your Pinvoke statements.
Here is an example from pinvoke.net
using System.Windows.Forms;
public class MyClass
{
private HookProc myCallbackDelegate = null;
public MyClass()
{
// initialize our delegate
this.myCallbackDelegate = new HookProc(this.MyCallbackFunction);
// setup a keyboard hook
SetWindowsHookEx(HookType.WH_KEYBOARD, this.myCallbackDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
}
[DllImport("user32.dll")]
protected static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadID);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private int MyCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0) {
//you need to call CallNextHookEx without further processing
//and return the value returned by CallNextHookEx
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
// we can convert the 2nd parameter (the key code) to a System.Windows.Forms.Keys enum constant
Keys keyPressed = (Keys)wParam.ToInt32();
Console.WriteLine(keyPressed);
//return the value returned by CallNextHookEx
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}

Related

EnumWindows not finding Touch-Keyboard

I am trying to get the HWND of the On-Screen/Touch keyboard - the one that appears on the bottom of the screen. For that I first look for the Process ID according to the name, and then do EnumWindows to look through all windows and find one whose Process ID corresponds to the one I know:
doTheThing(int pid)
{
EnumWindows(new EnumWindowsProc(Report), (IntPtr)pid);
}
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
protected static bool Report(IntPtr hwnd, IntPtr lParam)
{
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hwnd, out lpdwProcessId);
if (lpdwProcessId == lParam)
{
MessageBox.Show("True: " + hwnd.ToString());
return false;
}
return true;
}
Now Report is called several times, but the thing never actually matches. If I take another process, it works, but it seems like the OnScreenkeyboard "System.Diagnostics.Process (WindowsInternal.ComposableShell.Experiences.TextInput.InputApp)" does not show up with EnumWindows.
Using FindWindow does not work as the window does not have a Title and its class is the generic ApplicationFrameHost.
In AHK I was able to get the HWND by hovering over the keyboard with MouseGetPos,,, WinUMID, so a HWND definitely exists.
Is there a possibility that some windows are ignored by the EnumWindows? If so, how can I prevent that? What other solutions are there?
As a sidenote, it also does not show up in the UI Automation verify tool.
Note: Windows now has two keyboards apparently. I mean the one you can open via the taskbar.
To clarify why I need this:
I have the following AHK-Script that can make the window I point my mouse over window semi-transparent:
MouseGetPos,,, WinUMID
WinSet, Transparent, 100, ahk_id %WinUMID%
I noticed that when I point it over the Touch-Keyboard, it will make it transparent and that effect stays even if the keyboard disappears, until I restart my system. If I save the WinUMID variable I can even change the transparency while the keyboard is not used.
Now I want to make the Keyboard semi-transparent in C#, and that works as long as I provide the HWND. But I can't figure out a way to get the HWND inside of C#, without the help of AHK.
process.MainWindowHandle returns 0.
I have also tried
ProcessThreadCollection threads = process.Threads;
foreach (ProcessThread thread in threads)
{
EnumThreadWindows((uint)thread.Id, new EnumThreadDelegate(EnumThreadCallback), IntPtr.Zero);
}
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
protected delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
protected static bool EnumThreadCallback(IntPtr hwnd, IntPtr lParam)
{
MessageBox.Show("Try");
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hwnd, out lpdwProcessId);
if (lpdwProcessId == lParam)
{
MessageBox.Show("True: " + hwnd.ToString());
makeTransparent(lParam);
return false;
} else
MessageBox.Show("False: " + hwnd.ToString());
return true;
}
EnumThreadCallback was never called though, even though it works for any other process.
Edit:
I found out that the Keyboard process was not actually creating the Keyboard. Instead, it seems like the keyboard-UI is created by explorer.
The following code does work:
Process[] processes = Process.GetProcesses();
IntPtr hWnd = IntPtr.Zero;
while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "ApplicationFrameWindow", null)) != IntPtr.Zero)
{
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hWnd, out lpdwProcessId);
foreach (Process process in processes)
{
if (process.ToString() == "System.Diagnostics.Process (explorer)")
{
if (process.Id == (int)lpdwProcessId)
{
doThing(hWnd);
}
}
}
}
However, multiple hWnds are used, and only one of them belongs to the keyboard. Now I need to find out how to filter out this specific one. There do not seem to be downsides to this method right now, but I don't feel comfortable releasing this without knowing if it can have any adverse effects because of this. Victim 1: The new Paste window.
Try this alternate solution:
var process = Process.GetProcessById(pid);
IntPtr hwnd = process.MainWindowHandle;

c# Virtual mouse movement and click. Without moving the in use real cursor [duplicate]

I couldn't find any solution except moving the cursor by Cursor class, clicking with mouse_event then moving the cursor to its old position. I am playing with SendInput function right now but still no chance for a good solution. Any advice?
You should use Win32 API.
Use pInvoked SendMessage from user32.dll
pInvoked Function
Then read about mouse events:
Mouse Input on msdn
And then read about: System events and Mouse Mess.......
Also there is lots of info:
Info
Here's an example following the approach Hooch suggested.
I created a form which contains 2 buttons. When you click upon the first button, the position of the second button is resolved (screen coördinates). Then a handle for this button is retrieved. Finally the SendMessage(...) (PInvoke) function is used to send a click event without moving the mouse.
public partial class Form1 : Form
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "WindowFromPoint",
CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr WindowFromPoint(Point point);
private const int BM_CLICK = 0x00F5;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Specify the point you want to click
var screenPoint = this.PointToScreen(new Point(button2.Left,
button2.Top));
// Get a handle
var handle = WindowFromPoint(screenPoint);
// Send the click message
if (handle != IntPtr.Zero)
{
SendMessage( handle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("Hi", "There");
}
}

Performing a mouse click without moving cursor

I couldn't find any solution except moving the cursor by Cursor class, clicking with mouse_event then moving the cursor to its old position. I am playing with SendInput function right now but still no chance for a good solution. Any advice?
You should use Win32 API.
Use pInvoked SendMessage from user32.dll
pInvoked Function
Then read about mouse events:
Mouse Input on msdn
And then read about: System events and Mouse Mess.......
Also there is lots of info:
Info
Here's an example following the approach Hooch suggested.
I created a form which contains 2 buttons. When you click upon the first button, the position of the second button is resolved (screen coördinates). Then a handle for this button is retrieved. Finally the SendMessage(...) (PInvoke) function is used to send a click event without moving the mouse.
public partial class Form1 : Form
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "WindowFromPoint",
CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr WindowFromPoint(Point point);
private const int BM_CLICK = 0x00F5;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Specify the point you want to click
var screenPoint = this.PointToScreen(new Point(button2.Left,
button2.Top));
// Get a handle
var handle = WindowFromPoint(screenPoint);
// Send the click message
if (handle != IntPtr.Zero)
{
SendMessage( handle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("Hi", "There");
}
}

PostMessage does not seem to be working

I am trying to use PostMessage to send a tab key.
Here is my code:
// This class allows us to send a tab key when the the enter key
// is pressed for the mooseworks mask control.
public class MaskKeyControl : MaskedEdit
{
// [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
// static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
[return: MarshalAs(UnmanagedType.Bool)]
// I am calling this on a Windows Mobile device so the dll is coredll.dll
[DllImport("coredll.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, Int32 wParam, Int32 lParam);
public const Int32 VK_TAB = 0x09;
public const Int32 WM_KEYDOWN = 0x100;
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
PostMessage(this.Handle, WM_KEYDOWN, VK_TAB, 0);
return;
}
base.OnKeyDown(e);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (e.KeyChar == '\r')
e.Handled = true;
base.OnKeyPress(e);
}
}
When I press enter the code gets called, but nothing happens. Then I press TAB and it works fine. (So there is something wrong with my sending of the Tab Message.)
You really shouldn't post windows messages related to user input directly to windows controls. Rather, if you want to simulate input, you should rely on the SendInput API function instead to send the key presses.
Also, as Chris Taylor mentions in his comment, the SendKeys class can be used to send key inputs to an application in the event that you want to use an existing managed wrapper (instead of calling SendInput function yourself through the P/Invoke layer).
PostMessage on key events does really really odd things.
In this case, maybe SendMessage with KEYDOWN, KEYPRESS, KEYUP (three calls) might work better.
An alternative to sending input messages to the control, you could be more explicit and do the following.
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (Parent != null)
{
Control nextControl = Parent.GetNextControl(this, true);
if (nextControl != null)
{
nextControl.Focus();
return;
}
}
}
base.OnKeyDown(e);
}
This will set the focus to the next control on the parent when the enter key is pressed.

C# ListView mouse wheel scroll without focus

I'm making a WinForms app with a ListView set to detail so that several columns can be displayed.
I'd like for this list to scroll when the mouse is over the control and the user uses the mouse scroll wheel. Right now, scrolling only happens when the ListView has focus.
How can I make the ListView scroll even when it doesn't have focus?
"Simple" and working solution:
public class FormContainingListView : Form, IMessageFilter
{
public FormContainingListView()
{
// ...
Application.AddMessageFilter(this);
}
#region mouse wheel without focus
// P/Invoke declarations
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point pt);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x20a)
{
// WM_MOUSEWHEEL, find the control at screen position m.LParam
Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
IntPtr hWnd = WindowFromPoint(pos);
if (hWnd != IntPtr.Zero && hWnd != m.HWnd && System.Windows.Forms.Control.FromHandle(hWnd) != null)
{
SendMessage(hWnd, m.Msg, m.WParam, m.LParam);
return true;
}
}
return false;
}
#endregion
}
You'll normally only get mouse/keyboard events to a window or control when it has focus. If you want to see them without focus then you're going to have to put in place a lower-level hook.
Here is an example low level mouse hook

Categories

Resources