I try to redirect the XButton 1 and 2 (Side Buttons of the mouse) to specific Visual Studio actions.
When I press the XButton1 I want to compile the project / build it.
This action is bound to F6 by default.
When I press the XButton2 I want to switch between code and design view (WinForms). This is bound to F7.
After several attempts using using Visual Studio built-in tools, I created the following script using AutoHotKey:
XButton2::
IfWinActive Microsoft Visual Studio
{
Send {F7}
return
}
XButton1::
IfWinActive Microsoft Visual Studio
{
Send {F6}
return
}
However I am wondering if someone knows a native way of achieving the same with Visual Studio 2015?
Solution
The main idea is registering a global mouse hook and handling desired mouse event and run a visual studio command. To do so:
Start by creating a Visual Studio Package project.
Register a global mouse hook using SetWindowsHookEx by passing WH_MOUSE_LL and handle desired mouse event, for example WM_XBUTTONDOWN. Perform registration when the solution loads.
Run desired Visual Studio Command using DTE.ExecuteCommand passing suitable command, for example Build.BuildSolution:
var dte = (EnvDTE.DTE)this.GetService(typeof(EnvDTE.DTE));
dte.ExecuteCommand("Build.BuildSolution");
Don't forget to unhook using UnhookWindowsHookEx when the solution closed.
Note:
To find the command that you need, go to Tools → Options → Environment → KeyBoard and find the command which you need.
You will find lots of resources about how to register a global mouse hook like this which I changed a bit and used for test. At the end of the post you can find full source code.
In Visual Studio 2013 Add ins are deprecated, so while you can do the same using a Visual Studio Add-in project but it's better to do it using VSPackages.
Implementaion
Start by creating a Visual Studio Package project and change the code of package to the code which I post here. Also add the classes which I used for global mouse hook and windows API to the solution.
Package
Here is the full code for my Package:
using Microsoft.VisualStudio.Shell;
using System;
using System.Runtime.InteropServices;
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[Guid(GuidList.guidVSPackage1PkgString)]
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids80.SolutionExists)]
public sealed class VSPackage1Package : Package
{
public VSPackage1Package() { }
EnvDTE.DTE dte;
protected override void Initialize()
{
base.Initialize();
dte = (EnvDTE.DTE)this.GetService(typeof(EnvDTE.DTE));
dte.Events.SolutionEvents.Opened += SolutionEvents_Opened;
dte.Events.SolutionEvents.AfterClosing += SolutionEvents_AfterClosing;
}
void SolutionEvents_AfterClosing() { MouseHook.Stop(); }
void SolutionEvents_Opened()
{
MouseHook.Start();
MouseHook.MouseAction += MouseHook_MouseAction;
}
void MouseHook_MouseAction(object sender, EventArgs e)
{
dte.ExecuteCommand("Build.BuildSolution");
}
}
Windows API messages, structures and methods
using System;
using System.Runtime.InteropServices;
public class Win32
{
public delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
public const int WH_MOUSE_LL = 14;
public enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201, WM_LBUTTONUP = 0x0202,
WM_MOUSEMOVE = 0x0200, WM_MOUSEWHEEL = 0x020A,
WM_RBUTTONDOWN = 0x0204, WM_RBUTTONUP = 0x0205
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT { public int x; public int y; }
[StructLayout(LayoutKind.Sequential)]
public 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)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn,
IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
Global Mouse Hook
Since I don't have XButton in my mouse, I handled WM_RBUTTONDOWN event.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
public static class MouseHook
{
public static event EventHandler MouseAction = delegate { };
private static Win32.LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Start() { _hookID = SetHook(_proc); }
public static void Stop() { Win32.UnhookWindowsHookEx(_hookID); }
private static IntPtr SetHook(Win32.LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
var handle = Win32.GetModuleHandle(curModule.ModuleName);
return Win32.SetWindowsHookEx(Win32.WH_MOUSE_LL, proc, handle, 0);
}
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
Win32.MouseMessages.WM_RBUTTONDOWN == (Win32.MouseMessages)wParam)
{
Win32.MSLLHOOKSTRUCT hookStruct =
(Win32.MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam,
typeof(Win32.MSLLHOOKSTRUCT));
MouseAction(null, new EventArgs());
}
return Win32.CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
Related
I am building an application using UIAutomation which labels certain widgets on a typical Microsoft Windows user interface, e.g. Windows Explorer, control panel. I am using appropriate threading with UIA based on a Microsoft blog example and everything is working well. The labelling of the widgets is triggered by certain events and one of these events is a mouse click using a low level mousehook code shown below. The problem I have is that when the UI is updated on a mouseclick the mouse movement becomes juddery (as in not smooth) while the UI is updating. So what springs to mind is that the mouse action event needs to run in a different thread. Although I have been programming for quite a few years it has been off and on so I am far from confident when it comes to such threading issues. I have tried the code below in the mouse auction event but it makes no difference. Any help greatly appreciated. I would particularly appreciate example code with an explanation in particular.
// Setup global mouse hook to react to mouse clicks under certain conditions, see event handler
MouseHook.Start();
MouseHook.MouseAction += new EventHandler(MouseHook_MouseAction);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
namespace myapplication
{
public static class MouseHook
{
[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);
public static event EventHandler MouseAction = delegate { };
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;
}
public static void Start()
{
_hookID = SetHook(_proc);
}
public static void stop()
{
UnhookWindowsHookEx(_hookID);
}
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
return hook;
}
}
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 || MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam ||
MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam))
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
MouseAction(null, new EventArgs());
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
}
private void MouseHook_MouseAction(object sender, EventArgs e)
{
//my attempt at threading solution
Thread th = new Thread(() =>
{
UpdateUI();
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
I have a C# application which uses multiple event handlers which are triggered by various events. As an example:
Global mouse hook event
Global Key hook event which is filtered (the handler only triggers with certain keypresses)
Active window change global hook
Structure Changed application hook (this is an UIAAutomation event where different structure changed event/handlers are created for each application window when it becomes the active window (so, for example, if the application is Internet Explorer then paging down the browser, clicking on another website link are examples of the structure changing on the Internet Explorer application instance)
All these events (running on background threads, e.g. MTA) have the effect of updating the UI window of my application. All of them are working as they should.
My issue is that with certain circumstances multiple events are triggered at the same time. So for example it is feasible that the following events are all triggered within 1 second (such a scenario would occur if I clicked on a different active window):
Global Change of active window
Global Mouse Hook
Application structure changed.
In different circumstances (e.g. different active applications) one event is preferred above the other as the defining event which is ruled by a set of conditions (Booleans) . I do not want to act on more than 1 event at a particular period of time.
What is the best programming mechanism for considering the events triggered and deciding, by a set of conditions, which one to act on? Of course this all has to be done within a very quick period of time (e.g. one second or less). The events if triggered will all occur within this period of time.
I hope this makes sense and if not please ask for clarification. Incidentally the reason I would like to update my UI of my application by a certain defining event is that the information that my UI presents will be slightly different depending on which event is fired (mainly due to the slight difference in timing when the differing events are triggered). Incidentally the time taken to trigger a particular event will vary depending on the action taken (e.g. clicking on a different active window). Sometimes one event type is quicker than another but then in different circumstances a different event type can be the quickest event (or the slowest event triggered)
Thank you for both the answers below very much appreciated. I will check out the System.Reactive library in the first place as it sounds purpose-built for the task.
Microsoft's Reactive Framework (NuGet "System.Reactive") can do this kind of ting superbly and very powerfully. You can do some things very simply, but some queries - especially those dealing with time - can be quite complicated.
Here's a sample of what you might do:
var event1 = new Subject<int>();
var event2 = new Subject<int>();
var query =
event1.Merge(event2).Buffer(TimeSpan.FromSeconds(1.0));
query.Subscribe(xs => Console.WriteLine($"\"{String.Join(", ", xs)}\""));
event1.OnNext(42);
Thread.Sleep(3000);
event2.OnNext(43);
Thread.Sleep(500);
event1.OnNext(44);
That outputs:
"42"
""
""
"43, 44"
""
""
Note that it produces the "43, 44" at the same time even though the events fire 500ms apart.
I've been using code like below to prevent conflicts in event handling :
class Program
{
enum EVENTS
{
EVENT1,
EVENT2,
EVENT3,
EVENT4,
}
static void Main(string[] args)
{
}
static void EventHandler(EVENTS myEvent)
{
Object thisLock = new Object();
lock (thisLock)
{
switch (myEvent)
{
case EVENTS.EVENT1 :
break;
case EVENTS.EVENT2:
break;
case EVENTS.EVENT3:
break;
case EVENTS.EVENT4:
break;
}
}
}
}
Here is a simplified version of my code with several global hooks which are triggered by change of active window and a left mouse click. If you left click on a different active window both the mouse click event and change of active window event will be triggered. If you could show me example code of using the Reactive namespace to deal with the 2 events when they are triggered within milliseconds of each other I would greatly appreciate it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Testing_multiple_events
{
public partial class Form1 : Form
{
int activeWindowCount = 1;
int activeMouseClickCount = 1;
public Form1()
{
InitializeComponent();
// set up the global hook event for change of active window
GlobalEventHook.Start();
GlobalEventHook.WinEventActive += new EventHandler(GlobalEventHook_WinEventActive);
// Setup global mouse hook to react to mouse clicks under certain conditions, see event handler
MouseHook.Start();
MouseHook.MouseAction += new EventHandler(MouseHook_MouseAction);
}
private void GlobalEventHook_WinEventActive(object sender, EventArgs e)
{
richTextBox1.AppendText("Active Window Change Global Hook Triggered: " + activeWindowCount + "\r\n");
activeWindowCount++;
}
private void MouseHook_MouseAction(object sender, EventArgs e)
{
richTextBox1.AppendText("Global MouseHook Triggered: " + activeMouseClickCount + "\r\n");
activeMouseClickCount++;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Testing_multiple_events
{
public static class GlobalEventHook
{
[DllImport("user32.dll")]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
public static event EventHandler WinEventActive = delegate { };
public static event EventHandler WinEventContentScrolled = delegate { };
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject,
int idChild, uint dwEventThread, uint dwmsEventTime);
private static WinEventDelegate dele = null;
private static IntPtr _hookID = IntPtr.Zero;
public static void Start()
{
dele = new WinEventDelegate(WinEventProc);
_hookID = SetWinEventHook(Win32API.EVENT_SYSTEM_FOREGROUND, Win32API.EVENT_OBJECT_CONTENTSCROLLED, IntPtr.Zero, dele, 0, 0, Win32API.WINEVENT_OUTOFCONTEXT);
}
public static void stop()
{
UnhookWinEvent(_hookID);
}
public static void restart()
{
_hookID = SetWinEventHook(Win32API.EVENT_SYSTEM_FOREGROUND, Win32API.EVENT_OBJECT_CONTENTSCROLLED, IntPtr.Zero, dele, 0, 0, Win32API.WINEVENT_OUTOFCONTEXT);
}
public static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == Win32API.EVENT_SYSTEM_FOREGROUND)
{
WinEventActive(null, new EventArgs());
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Testing_multiple_events
{
public static class MouseHook
{
[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);
public static event EventHandler MouseAction = delegate { };
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;
}
public static void Start()
{
_hookID = SetHook(_proc);
}
public static void stop()
{
UnhookWindowsHookEx(_hookID);
}
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static IntPtr SetHook(LowLevelMouseProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
return hook;
}
}
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 || MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam ||
MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam))
{
MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
MouseAction(null, new EventArgs());
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
}
I want to use mono to write a simple CL tool that registers every click around the system. I understand that I can access this from Windows Forms? Which is like the wrapper around the internal Windows API?
Sorry I this is a real stupid question but coming from a JS background where its just AddEventListener this is kind of confusing, or badly documented. Thanks
What you're looking for is in user32.dll
Here're some links about it:
http://pinvoke.net/default.aspx/user32.GetAsyncKeyState
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx
looking up that the user press a key or not?
The first link contains examples of how to use the dll.
You can do multiple things with this dll. For example, what you're after is
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Int32 vKey);
For this, you'll need to check the key every time you want to check whether the key is down. You can either use the virtual key code or use the Keys class.
If you also want to simulate mouse events, e.g send a left click to the system, the following code is what you're after. (more info here)
[DllImport("user32.dll")]
static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, int dwExtraInfo);
I did a similar thing not long ago, however I was hooking into the keyboard and not the mouse. The process is similar, however it is a lot easier to hook into a specific program. The code is below on how I solved my problem.
In the following code, I created an event which triggered whenever a key was pressed and sent the Key Code as the event argument.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KeyHook {
public class KeyHook {
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
LowLevelKeyboardProc _proc { get; set; }
IntPtr _hookID { get; set; }
public delegate void KeyHandler(Keys k);
public event KeyHandler OnKeyDown;
public event KeyHandler OnKeyUp;
public KeyHook() {
Initialize();
_hookID = SetHook(_proc);
}
void Initialize() {
this._proc = HookCallback;
this._hookID = IntPtr.Zero;
Application.ApplicationExit += Application_ApplicationExit;
}
void Application_ApplicationExit(object sender, EventArgs e) {
UnhookWindowsHookEx(_hookID);
}
IntPtr SetHook(LowLevelKeyboardProc proc) {
using (Process curProcess = Process.GetCurrentProcess()) {
using (ProcessModule curModule = curProcess.MainModule) {
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle
(curModule.ModuleName), 0);
}
}
}
IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
if (this.OnKeyDown != null) {
this.OnKeyDown((Keys)Marshal.ReadInt32(lParam));
}
} else if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP) {
if (this.OnKeyUp != null) {
this.OnKeyUp((Keys)Marshal.ReadInt32(lParam));
}
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
#region dll Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod,
uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
}
}
As beginner lesson I want to port this tutorial http://null-byte.wonderhowto.com/how-to/create-simple-hidden-console-keylogger-c-sharp-0132757/ to a Windows Form Application.
This should show a user what keys are pressed inside a label called 'lblMessage'.
I have separeted the code now into two pieces.
Form1.cs
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public GlobalKeyHook hook = new GlobalKeyHook();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Origin
// _hookID = SetHook(_proc);
hook._hookID = hook.SetHook(_proc);
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// Origin
// UnhookWindowsHookEx(_hookID);
hook.UnhookWindowsEx(_hookID);
}
}
}
GlobalKeyHook.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
namespace WindowsFormsApplication3
{
class GlobalKeyHook : Form
{
#region DLLs
[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)]
public 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);
#endregion
#region Fields and delegation
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
public static IntPtr _hookID = IntPtr.Zero;
public delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
#endregion
public GlobalKeyHook()
{
IntPtr hookID = _hookID;
LowLevelKeyboardProc proc = _proc;
}
static Form form = new Form();
#region Methods
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
form.Text = vkCode.ToString();
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
public 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);
}
}
#endregion
}
}
But I am not able to use the public static IntPtr _hookID, the method SetHook() or the DLL UnhookWindowsHookEx in the Form1 class. Is it not possible to use this "types" from another class?
Not sure this will solve all your problems but may help you avoid a few of them down the line.
If this is .net 4.0 or higher your SetWindowsHookEX() call will likely return 0 (it failed) because .net no longer emulates a native module for managed dlls. To fix this you can add a DllImport for LoadLibrary() like this:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
Then somewhere before you call SetWindowsHookEX() do this:
IntPtr hinstDLL = UnsafeMethods.LoadLibrary("user32.dll");
and call SetWindowsHookEX() like this:
SetWindowsHookEx(WH_KEYBOARD_LL, proc,
hinstDLL, 0);
Basically SetWindowsHookEX() needs a valid module handle which it verifies but never actually uses it. the reason to load user32 is that since you are p/invoking functions from it you def have it.
Also, in your constructor just set it to IntPtr.Zero. No need make _hookID if you always are setting it to IntPtr.Zero. It is also worth noting that if the computer you are running this on has less than win7sp1 using IntPtr.Zero won't work (99% sure at least.)
I would strongly suggest you check out This set of hooks They have a decent implementation of a hook library that you can easily extend however you want.
You need to set your GlobalKeyHook class to public class GlobalKeyHook.
I have a Microsoft Word Add-in that find the similar words in a text (But When I click a button !)
My question is : how to call a function when user typed words ?
In other word , i want an event like "TextChange" or "Keypress" when user typing to get the current word and process it and get it's similar words.
Somethings Like this :
private void TextChangeEventOfCurrentActiveDocument(object sender, System.EventArgs e)
{
...
}
Any Other idea that i can get new words that user typed ?
Thanks.
Finally after a long time, i create this add-in by using Windows hooks.
(Special thanks to #Reg Edit)
Here the entire code that i wrote and this work nice for me. (some optional section of code was removed.)
ThisAddIn.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Word;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace PersianWords
{
public partial class ThisAddIn
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr hookId = IntPtr.Zero;
private delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
private static HookProcedure procedure = HookCallback;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr SetHook(HookProcedure procedure)
{
using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
return SetWindowsHookEx(WH_KEYBOARD_LL, procedure, GetModuleHandle(module.ModuleName), 0);
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int pointerCode = Marshal.ReadInt32(lParam);
if (pointerCode == 162 || pointerCode == 160)
{
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
string pressedKey = ((Keys)pointerCode).ToString();
//Do some sort of processing on key press
var thread = new Thread(() =>
{
MyClass.WrdApp.CustomizationContext = MyClass.WrdApp.ActiveDocument;
//do something with current document
});
thread.Start();
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
hookId = SetHook(procedure);
MyClass.WrdApp = Application;
MyClass.WrdApp.CustomizationContext = MyClass.WrdApp.ActiveDocument;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
UnhookWindowsHookEx(hookId);
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
Sorry to say, but there is no such event. Nothing is getting close to it either.
So you are stuck with a button, or check the contents every once in a while using some sort of timer (the Timer class might be an option).
You could use Windows hooks to intercept keystrokes from another window (Word in this case).
Alternatively, the Word Application has a WindowSelectionChange event, which won't fire on typing, but will fire if the user moves the cursor with an arrow key or clicks a word. This would allow you to react to a word being clicked, rather than the user having to move somewhere else on the screen to click a button.