I have a laptop with a very sensitive touch pad, and wanted to code a small program that could block the mouse input when I was typing a paper or something.
I didn't think it would be hard to do, considering everything I've seen on low-level hooks, but I was wrong (astounding, right?).
I looked at a few examples, but the examples I've seen either block both keyboard and mouse, or just hide the mouse.
Any help with this would be great.
As you mentioned, you can do this using a low-level mouse hook (WH_MOUSE_LL), albeit somewhat incorrectly. What happens when you set a hook is that you'll receive notifications on each mouse input event (WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN, WM_XBUTTONDOWN, WM_NCXBUTTONDOWN, the equivalent up events for each of those, WM_MOUSEWHEEL, and WM_MOUSEHWHEEL). Once you've finished processing each event, you're supposed to call the CallNextHookEx function, which passes the event information on to the next application in the hook chain. However, if you want to prevent any other program from getting mouse input information, you can just skip calling that function at the end of your hook procedure. The "Remarks" section of the above-linked documentation explains it thusly:
Calling CallNextHookEx is optional,
but it is highly recommended;
otherwise, other applications that
have installed hooks will not receive
hook notifications and may behave
incorrectly as a result. You should
call CallNextHookEx unless you
absolutely need to prevent the
notification from being seen by other
applications.
And as it turns out, low-level mouse hooks aren't actually that difficult in C#. I just coded one up myself, actually. But rather than posting that monstrosity of a library, I'll refer you to the simpler code snippet posted on Stephen Toub's blog, which I've reprinted here with syntax highlighting for convenience:
class InterceptMouse
{
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
As I explained above, you'll want to modify his HookCallback method not to call CallNextHookEx once you've finished processing the mouse event, but return something like new IntPtr(1) instead.
EDIT: And yeah, as others mentioned, there are probably other easier, cleaner solutions to this problem. Your trackpad drivers are a great place to look for an option like "Ignore accidental mouse input while typing". If you don't have this option, you're probably using the standard Windows mouse drivers. Try to download the drivers from your trackpad's manufacturer from the laptop manufacturer's website (for what it's worth, most of the non-Apple trackpads I've seen are Synaptics).
A lot of touchpad drivers have this as an option. I.e. When you are typing, it ignores touchpad input. You could also turn off the tap-click, relying on the actual touchpad buttons to click.
First try the driver's configuration utility, before you try to write your own.
Related
I'm currently working on a personal project where I'm trying to create an APM calculator like. In order to realize it I need to be able to detect any key pressed and any mouse click. I'm using hooks to catch keyboard events and mouse events but I can't find any way to capture special mouse button click...
When I'm talking about special mouse button, I mean additional buttons like on gaming mice. Example : Logitech G600.
I would like to know if there is any way to detect mouse click from any mouse button or special mouse button ?
I already found how to do it for conventional buttons and even for the navigation buttons, but I can't seem to find anything about additional buttons.
Here is my code where I removed all the keyboard part :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Windows.Input;
namespace MouseHook
{
static class Program
{
private const int WH_MOUSE_LL = 14;
private static LowLevelMouseProc _procMouse = HookCallbackMouse;
private static IntPtr _hookIDMouse = IntPtr.Zero;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200,
WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_XBUTTONDOWN = 0x020B
// MISSING ADDITIONAL BUTTONS
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
_hookIDMouse = SetHookMouse(_procMouse);
Application.Run(new Form1());
UnhookWindowsHookEx(_hookIDMouse);
}
private static IntPtr SetHookMouse(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private static IntPtr HookCallbackMouse(int nCode, IntPtr wParam, IntPtr lParam)
{
// DEAL WITH ANY BUTTON OR ADDITIONAL BUTTON MOUSE
return CallNextHookEx(_hookIDMouse, nCode, wParam, lParam);
}
}
}
Thanks.
If I understood you correct you want to find the WindowsMessages that are being send once you click your additional mouse buttons. In order to detect those I suggest you to create a custom MessageFilter, which implements the interface IMessageFilter.
class MessageFilter : IMessageFilter
{
int[] msgToIgnore = new int[] {512, 799, 49632, 49446, 1847, 15, 96, 275, 160, 1848, 674 , 513, 675, 514, 280, 161, 274, 673, 515, 516, 517, 518, 519, 520, 521, 522, 163, 164, 167, 168, 169, 165, 166};
public bool PreFilterMessage(ref Message m)
{
bool match = false;
foreach (var msg in msgToIgnore)
{
if (m.Msg == msg)
match = true;
}
if (!match)
{
MessageBox.Show(m.Msg.ToString());
return true;
}
else
return false;
}
}
A MessageFilter filters every single WindowsMessage that is being send to your application. So I basically added the msgToIgnore array to ignore all the other basic WindowsMessages, like any normal mouse click, move, ... or anything that happens on the application start. (I tested it with an empty plain Form and clicked, doubleClicked, dragged, ... on every part of the Form) This ignores all those WindowsMessages, but once you send another WindowsMessage, so e.g. your click on the additional mouse button, the WindowsMessage will be shown in a MessageBox.
All you have to do to activate this MessageFilter is to add this one to your application, I suggest to do it in the constructor after InitializeComponent().
MessageFilter msgFilter = new MessageFilter();
Application.AddMessageFilter(msgFilter);
so, I've been developing a class to handle Kwyboard input in a VSTO add-in, so far I've been using Windows hooks to do so with relative success.
Having this code:
//.....
private const int WH_KEYBOARD = 2;
private const int WH_MOUSE = 7;
private enum WM : uint {
KEYDOWN = 0x0100,
KEYFIRST = 0x0100,
KEYLAST = 0x0108,
KEYUP = 0x0101,
MOUSELEFTDBLCLICK = 0x0203,
MOUSELEFTBTNDOWN = 0x0201,
MOUSELEFTBTNUP = 0x0202,
MOUSEMIDDBLCLICK = 0x0209,
MOUSEMIDBTNDOWN = 0x0207,
MOUSEMIDBTNUP = 0x0208,
MOUSERIGHTDBLCLK = 0x0206,
MOUSERIGHTBTNDOWN = 0x0204,
MOUSERIGHTBTNUP = 0x0205
}
private hookProcedure proc;
private static IntPtr hookID = IntPtr.Zero;
//Enganches
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(int hookId, hookProcedure proc, IntPtr hInstance, uint thread);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool unHookWindowsHookEx(int hookId);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CallNextHookEx(IntPtr hookId, int ncode, IntPtr wparam, IntPtr lparam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string name);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetCurrentThreadId();
public CPInputListener() {
proc = keyBoardCallback;
hookID = setHook(proc);
}
private IntPtr setHook(hookProcedure procedure){
ProcessModule module = Process.GetCurrentProcess().MainModule;
uint threadId = (uint)GetCurrentThreadId();
return SetWindowsHookEx(WH_KEYBOARD, procedure, IntPtr.Zero, threadId);
}
public void stopListeningAll() {
unHookWindowsHookEx(WH_KEYBOARD);//For now
}
private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam) {
if (ncode >= 0) {
//LPARAM pretty useless
Keys key = (Keys)wParam;
KeyEventArgs args = new KeyEventArgs(key);
onKeyDown(args);//for now
}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}
//....
I do successfully receive keyboard input, but here is the big mistery; each time a key is pressed, no matter how fast it was, the event (onKeyDown) is called 10 times exactly, no more no less.
If the key is long pressed, the event keep being called but 10 by 10 times instead of calling just once.
So far I've tried
Using wParam to call the required event on Key Up: Doesn't seem to work, in all codes I've seen dealing with Key down and up events, IntPtr wParam is used, but from that variable I can only retrieve the keycode which doesn't help.
Using lParam or nCode: These vars are giving unconsistent values between those 10 calls, ncode tends to retrieve 0's and 3's and lParam some values which seem to be unmanaged memory adresses...
What do I expect
I do expect for onKeyDown to be called just once when the key is pressed or in the other hand being able to call the method by on key up which i do expect to be called just once per key releasing.
How to bypass this
If I can't find a reasonable answer, I was thinking on using a custom made timer to discard all those callings and use only the last one, would you recommend this if everything else fails?
Thanks a lot! Be happy and be kind! :D
First you have to filter for the correct ncode to get only the keystrokes you are supposed to process. (For example, you are not supposed to process HC_NOREMOVE.)
Then you have to check if it was a KeyDown or KeyUp event using a flag in lParam.
If the key was long-pressed, multiple KeyDown events are already combined to one call by Win32, so you don't have to do anything special here. But if you want to get only the last KeyUp event then you have to check another flag in lParam as well.
So, here's the code you need to change:
private IntPtr keyBoardCallback(int ncode, IntPtr wParam, IntPtr lParam)
{
// Feel free to move the const to a private field.
const int HC_ACTION = 0;
if (ncode == HC_ACTION)
{
Keys key = (Keys)wParam;
KeyEventArgs args = new KeyEventArgs(key);
bool isKeyDown = ((ulong)lParam & 0x40000000) == 0;
if (isKeyDown)
onKeyDown(args);
else
{
bool isLastKeyUp = ((ulong)lParam & 0x80000000) == 0x80000000;
if (isLastKeyUp)
onKeyUp(args);
}
}
return CallNextHookEx(hookID, ncode, wParam, lParam);
}
Edit as requested in the comment:
Unfortunately the documentation of these parameters is pretty sparse.
One "hint" not to process anything other then HC_ACTION can be found here, stating:
if (nCode < 0) // do not process message
return ...;
// ...
switch (nCode)
{
case HC_ACTION:
// ... do something ...
break;
default:
break;
}
// ...
return CallNextHookEx(...);
Another supporting statement is made here:
Why does my keyboard hook receive the same key-up and key-down events multiple times?
The content of the lParam is defined as follows:
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode;
DWORD scanCode;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
}
(Just as a reminder: DWORD here is 4 bytes in size on x86 as well as on x64 platforms.)
The documentation of the lParam flags can be found here and here.
In this links it's described that
bit 30 (=0x40000000) is the previous key state
(1 if the key was down and 0 if the key was up before the new key state that caused this call)
bit 31 (=0x80000000) is the transition state
(0 on key press and 1 on key release now)
The term "previous key state" is rather confusing but effectively it's just the opposite of the current state (because there's only up or down and no third state).
The transition state is especially relevant when the "keyboard's automatic repeat feature" is activated, i.e. when the key is pressed down long enough.
Another sample (using VC7) can be found here:
if (HIWORD (lParam) & 0xC000)
// Key up without autorepeat
else
// Key down
Where 0xC000 just is 0x4000 || 0x8000 and defines that the key was released and has created a key up event.
All in all pretty confusing but nonetheless true.
Maybe there are other links out there that can describe this situation better, but I guess in times like these where new app development "should be done" in tiny sandboxes (like UWP) and VSTO is on its sure way to die to make way for newer Office add-ins that are written in HTML and JavaScript, nobody cares all that much about low-level hooks anymore.
Well, first off, what I'm trying to do is click a specific point inside a flash object, inside of a webbrowser control. I'm not sure why it isn't working, but I cannot seem to click any window, be it notepad, or the actual program.
Here is my code that I'm using.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(String sClassName, String sAppName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
public IntPtr find()
{
return this.Handle;//FindWindow("", "Form1");
}
public enum WMessages : int
{
WM_LBUTTONDOWN = 0x201, //Left mousebutton down
WM_LBUTTONUP = 0x202, //Left mousebutton up
WM_LBUTTONDBLCLK = 0x203, //Left mousebutton doubleclick
WM_RBUTTONDOWN = 0x204, //Right mousebutton down
WM_RBUTTONUP = 0x205, //Right mousebutton up
WM_RBUTTONDBLCLK = 0x206, //Right mousebutton do
}
private int MAKELPARAM(int p, int p_2)
{
return ((p_2 << 16) | (p & 0xFFFF));
}
/** This is the non-working code **/
public void DoMouseLeftClick(IntPtr handle, Point x)
{
SendMessage(handle, (int)WMessages.WM_LBUTTONDOWN, 0, MAKELPARAM(x.X, x.Y));
SendMessage(handle, (int)WMessages.WM_LBUTTONUP, 0, MAKELPARAM(x.X, x.Y));
return;
//I have tried PostMessage, and SendMessage, and both of them at the same time, and neither works.
PostMessage(handle, (uint)WMessages.WM_LBUTTONDOWN, 0, MAKELPARAM(x.X, x.Y));
PostMessage(handle, (uint)WMessages.WM_LBUTTONUP, 0, MAKELPARAM(x.X, x.Y));
}
private void timer2_Tick(object sender, EventArgs e)
{
//I try hovering my mouse over a button I added to the form, and nothing happens.
DoMouseLeftClick(find(), Cursor.Position);
}
So, I have tried using PostMessage, and SendMessage, and neither of those seem to work.
All I need it to do is to click on a specific point.
Also, I need to mention that I can't use mouse_event, because from what I know, the window needs to be active, plus the cursor needs to be over the point you are clicking. I am making a bot that automatically does a process in a flash object, so that's why I can't use mouse_event.
Thanks for your help guys.
I had the same problem.
I tried to draw something in the MS Paint. It turned out that I was clicking on the main window but it turned out that MS Paint (and most of the applications) consist of many child windows and you actually want to click on the child. So I had to change my code from:
IntPtr handle = FindWindow(null, "Untitled - Paint");
PostMessage(handle, (uint)MOUSE_BUTTONS.LEFT_DOWN, 0, lparam);
to
IntPtr handle = FindWindow(null, "Untitled - Paint");
handle = FindWindowEx(handle, IntPtr.Zero, "MSPaintView", null);
canvasHandle = FindWindowEx(handle, IntPtr.Zero, "Afx:00007FF676DD0000:8", null);
PostMessage(canvasHandle, (uint)MOUSE_BUTTONS.LEFT_DOWN, 0, lparam);
You need to use tools like Spy++ for Windows that comes with C++ Visual Studio package if you want to debug these kind of things (even if you program in C#)
Hope it helps someone.
For the WM_LBUTTONDOWN, you may need to specify which button. Take reference for: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645607(v=vs.85).aspx
I used:
SendMessage(hWnd, (int)WMessages.WM_RBUTTONDOWN, (int)KeyDownMessage.MK_LBUTTON, MAKELPARAM(x, y));
I am using the code from this article :
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
//Application.Run();
//UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
***custom code***
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
with a little modification but sometimes when i press a couple of keystrokes too fast then the computer "slows" down a bit and gets a little "laggy".
So when it hits asyncronosly the event HookCallback it sometimes get a little laggy, but i guess its the code in this method "HookCallback" that makes it laggy or is it the hooking it self? I had an idea about creating a new thread everytime i enter "HookCallback" , that maybe will help but do i want to start a new thread everytime i press a key? I am getting an asyncronosly call already so i dont know if i should start another thread besides that.
So my question is simple, where and why does it slow down the computer, is it the hooking it self or is it the custom code ? Or should i put the custom code in a different thread?
And i have another question, sometimes after a few keypresses "HookCallback" doesnt get called, as if i have "unhooked" the event, and it wont capture any keystrokes at all. How can i make sure that it never unhooks the event unless i do it in manually?
Thank you.
I've spent a fair amount of time doing keyboard hooks, so the following is just my own experience. Hooks need to be efficient. I wrote a small POS app a few years ago that had to capture keystrokes. It was written in C#, and slowed the input to the point of becoming noticeable. After a lot of headaches I finally realized that the big lag was coming from the translation from native code to managed code. I re-wrote small pieces of the hook code in C++ (native) and saw improvements that I would deem "good enough".
Just remember that speed is fully determined by how long your code takes to process it. Try your very best to make it efficient.
There is another factor about "unhook" problem. If your low level global hook codes can not finish in certain amount of time, the OS will remove your hook automatically without notifying you. So make your codes run as fast as possible
Cheers
If you want to capture keystrokes within your application only, things can be faster then; use WH_KEYBOARD instead of WH_KEYBOARD_LL, this will make much more noticeable performance increase.
private const int WH_KEYBOARD = 2;
I had the same symptoms, but it turned out my issue was due to Thread.Sleep();
I had recently changed my application from a Console to Windows Application. My code had been:
static void Main( string[] args )
{
hook_keys();
AppDomain.CurrentDomain.ProcessExit += CurrentDomainOnProcessExit;
while (true) { System.Threading.Thread.Sleep( new TimeSpan(0,10,0) ); }
}
private static void CurrentDomainOnProcessExit(object sender, EventArgs eventArgs )
{
unhook_keys();
}
This made windows slow down incredibly, even though all my hook does is increment a global variable whenever the keyboard is used. When I changed it to the following, my lag issue was resolved:
static void Main( string[] args )
{
hook_keys();
var application = new Application();
application.Run();
unhook_keys();
}
How to read the highlighted/Selected Text from any window using c#.
i tried 2 approaches.
Send "^c" whenever user selects some thing. But in this case my clipboard is flooded with lots of unnecessary data. Sometime it copied passwords also.
so i switched my approach to 2nd method, send message method.
see this sample code
[DllImport("user32.dll")]
static extern int GetFocus();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(int hWnd, int ProcessId);
[DllImport("user32.dll") ]
static extern int GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern int SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
// second overload of SendMessage
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, out int wParam, out int lParam);
const int WM_SETTEXT = 12;
const int WM_GETTEXT = 13;
private string PerformCopy()
{
try
{
//Wait 5 seconds to give us a chance to give focus to some edit window,
//notepad for example
System.Threading.Thread.Sleep(5000);
StringBuilder builder = new StringBuilder(500);
int foregroundWindowHandle = GetForegroundWindow();
uint remoteThreadId = GetWindowThreadProcessId(foregroundWindowHandle, 0);
uint currentThreadId = GetCurrentThreadId();
//AttachTrheadInput is needed so we can get the handle of a focused window in another app
AttachThreadInput(remoteThreadId, currentThreadId, true);
//Get the handle of a focused window
int focused = GetFocus();
//Now detach since we got the focused handle
AttachThreadInput(remoteThreadId, currentThreadId, false);
//Get the text from the active window into the stringbuilder
SendMessage(focused, WM_GETTEXT, builder.Capacity, builder);
return builder.ToString();
}
catch (System.Exception oException)
{
throw oException;
}
}
this code working fine in Notepad. But if i try to capture from another applications like Mozilla firefox, or Visual Studio IDE, it's not returning the text.
Can anybody please help me, where i am doing wrong? First of all, i have chosen the right approach?
That's because both Firefox and Visual Studio don't use the built-in Win32 controls for displaying/editing text.
It is not possible in general to be able to get the value of "any" selected text, because of the fact that programs can re-implement their own version of the Win32 controls any way they see fit, and your program cannot possibly expect to work with all of them.
However, you can use the UI Automation APIs which will allow you to interact with the majority of 3rd-party controls (at least, all the good ones - such as Visual Studio and Firefox - will likely work with the UI Automation APIs since it's a requirement for accessibility)