|
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace Keyboard
{
/// <summary>
/// Controls and reports changes in state of toggle keys such as
/// CapsLock, NumLock and ScrollLock.
/// </summary>
public class ToggleKeysController
{
#region Events and Delegates
private delegate void KeyCheckDelayHandler(AsyncOperation asyncOperation, object state);
/// <summary>
/// Raised when a change in the CapsLock state occurs.
/// </summary>
public event EventHandler CapsLockStateChanged;
/// <summary>
/// Raised when a change in the NumLock state occurs.
/// </summary>
public event EventHandler NumLockStateChanged;
/// <summary>
/// Raised when a change in the ScrollLock state occurs.
/// </summary>
public event EventHandler ScrollLockStateChanged;
/// <summary>
/// Raised when a change in state of a monitored key occurs.
/// </summary>
public event EventHandler<ToggleKeyStateChangedEventArgs> ToggleKeyStateChanged;
#endregion
#region Fields
private IntPtr hookHandle;
private LowLevelKeyboardProc hookProc;
private bool _CapsLockOn;
private bool _NumLockOn;
private bool _ScrollLockOn;
#endregion
#region Constructor and Destructor
/// <summary>
/// Creates a new instance of the Keyboard.ToggleKeysConstroller class.
/// </summary>
public ToggleKeysController()
{
hookProc = KeyHookCallback;
using (Process currentProcess = Process.GetCurrentProcess())
{
using (ProcessModule currentModule = currentProcess.MainModule)
{
hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, GetModuleHandle(currentModule.ModuleName), 0);
}
}
LoadToggleKeys();
}
~ToggleKeysController()
{
Dispose(false);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the state of the CapsLock key.
/// </summary>
public bool CapsLockOn
{
get { return _CapsLockOn; }
set
{
if (value != _CapsLockOn)
ToggleKey(ToggleKeys.CapsLock);
}
}
/// <summary>
/// Gets or sets the state of the NumLock key.
/// </summary>
public bool NumLockOn
{
get { return _NumLockOn; }
set
{
if (value != _NumLockOn)
ToggleKey(ToggleKeys.NumLock);
}
}
/// <summary>
/// Gets or sets the state of the ScrollLock key.
/// </summary>
public bool ScrollLockOn
{
get { return _ScrollLockOn; }
set
{
if (value != _ScrollLockOn)
ToggleKey(ToggleKeys.ScrollLock);
}
}
#endregion
#region Methods
/// <summary>
/// Disposes of all resources used by this Keyboard.ToggleKeysController instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes of unmanaged resources used by this Keyboard.ToggleKeysController instance
/// and optionally any managed resources.
/// </summary>
/// <param name="disposing">Whether to dispose of managed resources.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
if (hookHandle != IntPtr.Zero)
{
UnhookWindowsHookEx(hookHandle);
hookHandle = IntPtr.Zero;
}
}
private void LoadToggleKeys()
{
_CapsLockOn = GetCapsLock();
_NumLockOn = GetNumLock();
_ScrollLockOn = GetScrollLock();
}
private bool GetCapsLock()
{
return (GetKeyState(VK_CAPITAL) & 1) == 1;
}
private bool GetNumLock()
{
return (GetKeyState(VK_NUMLOCK) & 1) == 1;
}
private bool GetScrollLock()
{
return (GetKeyState(VK_SCROLL) & 1) == 1;
}
private void KeyCheckDelay(AsyncOperation asyncOperation, object state)
{
Thread.Sleep(5);
asyncOperation.PostOperationCompleted(
new SendOrPostCallback(KeyCheckDo),
state);
}
private void KeyCheckDo(object state)
{
byte key = (byte)state;
switch (key)
{
case VK_CAPITAL:
bool current = GetCapsLock();
if (current != _CapsLockOn)
{
_CapsLockOn = current;
OnCapsLockStateChanged(EventArgs.Empty);
OnToggleStateChanged(new ToggleKeyStateChangedEventArgs(ToggleKeys.CapsLock, current));
}
break;
case VK_NUMLOCK:
current = GetNumLock();
if (current != _NumLockOn)
{
_NumLockOn = current;
OnNumLockStateChanged(EventArgs.Empty);
OnToggleStateChanged(new ToggleKeyStateChangedEventArgs(ToggleKeys.NumLock, current));
}
break;
case VK_SCROLL:
current = GetScrollLock();
if (current != _ScrollLockOn)
{
_ScrollLockOn = current;
OnScrollLockStateChanged(EventArgs.Empty);
OnToggleStateChanged(new ToggleKeyStateChangedEventArgs(ToggleKeys.ScrollLock, current));
}
break;
}
}
private IntPtr KeyHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
byte key = Marshal.ReadByte(lParam);
if (key == VK_CAPITAL || key == VK_NUMLOCK || key == VK_SCROLL)
new KeyCheckDelayHandler(KeyCheckDelay).BeginInvoke(
AsyncOperationManager.CreateOperation(null), key, null, null);
}
return CallNextHookEx(hookHandle, nCode, wParam, lParam);
}
/// <summary>
/// Raises the CapsLockStateChanged event.
/// </summary>
/// <param name="e">A System.EventArgs instance.</param>
protected virtual void OnCapsLockStateChanged(EventArgs e)
{
EventHandler eh = CapsLockStateChanged;
if (eh != null)
eh(this, e);
}
/// <summary>
/// Raises the NumLockStateChanged event.
/// </summary>
/// <param name="e">A System.EventArgs instance.</param>
protected virtual void OnNumLockStateChanged(EventArgs e)
{
EventHandler eh = NumLockStateChanged;
if (eh != null)
eh(this, e);
}
/// <summary>
/// Raises the ScrollLockStateChanged event.
/// </summary>
/// <param name="e">A System.EventArgs instance.</param>
protected virtual void OnScrollLockStateChanged(EventArgs e)
{
EventHandler eh = ScrollLockStateChanged;
if (eh != null)
eh(this, e);
}
/// <summary>
/// Raises the ToggleStateChanged event.
/// </summary>
/// <param name="e">A Keyboard.ToggleKeyStateChangedEventArgs instance.</param>
protected virtual void OnToggleStateChanged(ToggleKeyStateChangedEventArgs e)
{
EventHandler<ToggleKeyStateChangedEventArgs> eh = ToggleKeyStateChanged;
if (eh != null)
eh(this, e);
}
/// <summary>
/// Toggles the state of the specified key.
/// </summary>
/// <param name="key">The ToggleKeys.Key to toggle.</param>
public void ToggleKey(ToggleKeys key)
{
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYDOWN, 0);
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
}
/// <summary>
/// Returns a string representation of this Keyboard.ToggleKeysController instance.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return string.Format("CapsLockOn: {0}, NumLockOn: {1}, ScrollLockOn: {2}",
CapsLockOn, NumLockOn, ScrollLockOn);
}
#endregion
#region Interop
#region Constants
private const int KEYEVENTF_EXTENDEDKEY = 0x0001;
private const int KEYEVENTF_KEYDOWN = 0x0000;
private const int KEYEVENTF_KEYUP = 0x0002;
internal const byte VK_CAPITAL = 0x14;
internal const byte VK_NUMLOCK = 0x90;
internal const byte VK_SCROLL = 0x91;
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
#endregion Constants
#region Delegates and Functions
// http://msdn.microsoft.com/en-us/library/ms644985(VS.85).aspx
/// <summary>
/// The system calls this function every time a new keyboard input event is about to be posted into a thread input queue.
/// </summary>
/// <param name="nCode">A code the hook procedure uses to determine how to process the message.</param>
/// <param name="wParam">Identifier of the keyboard message.</param>
/// <param name="lParam">Pointer to a KBDLLHOOKSTRUCT structure.
/// See http://msdn.microsoft.com/en-us/library/ms644967(VS.85).aspx</param>
/// <returns>The value returned by CallNextHookEx.</returns>
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
// http://msdn.microsoft.com/en-us/library/ms644974(VS.85).aspx
/// <summary>
/// Passes the hook information to the next hook procedure in the current hook chain.
/// </summary>
/// <param name="hhk">Handle to the current hook.</param>
/// <param name="nCode">The hook code passed to the current hook procedure.</param>
/// <param name="wParam">The wParam value passed to the current hook procedure.</param>
/// <param name="lParam">The lParam value passed to the current hook procedure.</param>
/// <returns>This value is returned by the next hook procedure in the chain.
/// The current hook procedure must also return this value.</returns>
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
// http://msdn.microsoft.com/en-us/library/ms646301(VS.85).aspx
/// <summary>
/// Retrieves the status of the specified virtual key.
/// </summary>
/// <param name="nVirtKey">A virtual key.</param>
/// <returns>The status of the specified virtual key/</returns>
[DllImport("user32.dll")]
private static extern short GetKeyState(byte nVirtKey);
// http://msdn.microsoft.com/en-us/library/ms885630.aspx
/// <summary>
/// returns a module handle for the specified module.
/// </summary>
/// <param name="lpModuleName">String that contains the name of the module.</param>
/// <returns>A handle to the specified module.</returns>
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
// http://msdn.microsoft.com/en-us/library/ms646304(VS.85).aspx
/// <summary>
/// Synthesizes a keystroke.
/// </summary>
/// <param name="bVk">A virtual-key code.</param>
/// <param name="bScan">A hardware scan code for the key.</param>
/// <param name="dwFlags">Various aspects of function operation.</param>
/// <param name="dwExtraInfo">An additional value associated with the key stroke.</param>
[DllImport("user32.dll")]
private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
// http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
/// <summary>
/// Installs an application-defined hook procedure into a hook chain.
/// </summary>
/// <param name="idHook">The type of hook procedure to be installed.</param>
/// <param name="lpfn">Pointer to the hook procedure.</param>
/// <param name="hMod">Handle to the module containing the hook procedure pointed to by the lpfn parameter.</param>
/// <param name="dwThreadId">The identifier of the thread with which the hook procedure is to be associated.</param>
/// <returns>The handle to the hook procedure.</returns>
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, int dwThreadId);
// http://msdn.microsoft.com/en-us/library/ms644993(VS.85).aspx
/// <summary>
/// Removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
/// </summary>
/// <param name="hhk">Handle to the hook to be removed.</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
#endregion Delegates and Functions
#endregion Interop
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.