/InputSystem/Windows/WindowsKeyboard.cs
C# | 955 lines | 494 code | 107 blank | 354 comment | 41 complexity | be7b9a678e5bfc7bc1c84f9f33867c6b MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Windows.Forms;
- using System.Windows.Input;
- using System.Windows.Interop;
- using Delta.InputSystem.Devices;
- using Delta.Platforms.Windows;
- using Delta.Utilities;
- using DeltaApplication = Delta.Engine.Application;
- using KeyEventArgs = System.Windows.Forms.KeyEventArgs;
- using WPFKeyEventArgs = System.Windows.Input.KeyEventArgs;
- using WPFVisual = System.Windows.Media.Visual;
- using WPFWindow = System.Windows.Window;
-
- namespace Delta.InputSystem.Windows
- {
- /// <summary>
- /// Windows keyboard support is based on the Windows Forms or WPF events
- /// we receive for its main application window. These are collected each
- /// frame and then processed in the Update method. This implementation is
- /// better for typing text because we will not miss any states and the
- /// HandleInput method for text boxes is getting all the keyboard input
- /// while the XnaKeyboard implementation might miss some keys when the
- /// frame rate is too low (due the way it gets input via polling, nothing
- /// we can do about it, but this event driven implementation is fixing
- /// this problem on Windows).
- /// On platforms that can use WindowsKeyboard you should always use it, it
- /// even suppresses the Windows Key and handles keys and text input way
- /// better, XnaKeyboard should only be a fallback for platforms this
- /// </summary>
- public class WindowsKeyboard : BaseKeyboard, IDisposable
- {
- #region SP_DEVINFO_DATA Struct
- /// <summary>
- /// An SP_DEVINFO_DATA structure defines a device instance that is a
- /// member of a device information set.
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- public struct SP_DEVINFO_DATA
- {
- #region cbSize (Public)
- /// <summary>
- /// The size, in bytes, of the SP_DEVINFO_DATA structure. For more
- /// information, see the following Remarks section.
- /// </summary>
- public int cbSize;
- #endregion
-
- #region ClassGuid (Public)
- /// <summary>
- /// The GUID of the device's setup class.
- /// </summary>
- public Guid ClassGuid;
- #endregion
-
- #region DevInst (Public)
- /// <summary>
- /// An opaque handle to the device instance (also known as a handle to
- /// the devnode).
- /// </summary>
- public int DevInst;
- #endregion
-
- #region Reserved (Public)
- /// <summary>
- /// Reserved. For internal use only.
- /// </summary>
- public int Reserved;
- #endregion
- }
- #endregion
-
- #region KBDLLHOOKSTRUCT Struct
- /// <summary>
- /// Structure contain information about low-level keyboard input event.
- /// More information can be found here: http://geekswithblogs.net/aghausman/archive/2009/04/26/disable-special-keys-in-win-app-c.aspx
- /// and http://msdn.microsoft.com/en-us/library/ms644967%28VS.85%29.aspx
- /// </summary>
- [StructLayout(LayoutKind.Sequential)]
- private struct KBDLLHOOKSTRUCT
- {
- #region key (Public)
- public readonly Keys key;
- #endregion
-
- #region scanCode (Public)
- public readonly int scanCode;
- #endregion
-
- #region flags (Public)
- public readonly int flags;
- #endregion
-
- #region time (Public)
- public readonly int time;
- #endregion
-
- #region extra (Public)
- public readonly IntPtr extra;
- #endregion
- }
- #endregion
-
- #region Constants
- /// <summary>
- /// Return the devices that are currently present in the system
- /// </summary>
- /// <remarks>
- /// see: http://msdn.microsoft.com/en-us/library/ff551069%28v=vs.85%29.aspx
- /// </remarks>
- protected const int DIGCF_PRESENT = 2;
-
- /// <summary>
- /// Used to define private messages for use by private window classes,
- /// usually of the form WM_USER+X, where X is an integer value.
- /// </summary>
- public const Int32 WM_USER = 1024;
-
- /// <summary>
- /// Windows Message to call On-screen-Keyboard window
- /// </summary>
- public const Int32 WM_CSKEYBOARD = WM_USER + 192;
-
- /// <summary>
- /// The title name of the On-Screen keyboard process.
- /// </summary>
- private const string oskProcessTitle = "On-Screen Keyboard";
- #endregion
-
- #region Delegates
- /// <summary>
- /// System level functions to be used for hook and unhook keyboard input
- /// </summary>
- /// <param name="nCode">Code</param>
- /// <param name="wParam">Options</param>
- /// <param name="lParam">Options</param>
- /// <returns>delegate</returns>
- private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam,
- IntPtr lParam);
- #endregion
-
- #region IsConnected (Public)
- /// <summary>
- /// Is a keyboard connected? On windows we can always assume yes of course,
- /// but it is checked anyway in IsKeyboardConnected to make sure we really
- /// have one and do not need on-screen keyboards in Windows (also possible).
- /// </summary>
- public override bool IsConnected
- {
- get
- {
- return isConnected;
- }
- }
- #endregion
-
- #region MarkAllKeysAsHandled (Public)
- /// <summary>
- /// Helper property for marking all keys as handled when we receive key
- /// events. By default this is on and this will prevent windows from
- /// further handling these keys. An exception is made for editors, which
- /// will still allow keyboard input and not mark them as handled here.
- /// Some additional exceptions apply like WindowsKey, Ctrl+Alt+Del, etc.
- /// Since we don't want the WindowsKey working on non-editor applications
- /// we will create a windows hook and disable that key explicitly!
- /// Note: This also disables Alt+F4 to close the window, so if you want
- /// Escape and/or Alt+F4 to close the game or application, add it as a
- /// global input command for the Exit event and it will work.
- /// </summary>
- /// <value>
- /// <c>true</c> if [mark all keys as handled]; otherwise, <c>false</c>.
- /// </value>
- public bool MarkAllKeysAsHandled
- {
- get;
- set;
- }
- #endregion
-
- #region Protected
-
- #region hWnd (Protected)
- /// <summary>
- /// Window handle for IsConnected
- /// </summary>
- protected Int32 hWnd;
- #endregion
-
- #endregion
-
- #region Private
-
- #region GUID_DEVCLASS_KEYBOARD (Private)
- /// <summary>
- /// Keyboard GUID
- /// </summary>
- private static Guid GUID_DEVCLASS_KEYBOARD =
- new Guid("4D36E96B-E325-11CE-BFC1-08002BE10318");
- #endregion
-
- #region oskProcessFilePath (Private)
- /// <summary>
- /// The absolute path of the located On-Screen keyboard process file which
- /// will be started every time the On-Screen keyboard window is shown.
- /// <para />
- /// Note: This value will be set the first time when the showing is needed.
- /// </summary>
- private static string oskProcessFilePath;
- #endregion
-
- #region keysUp (Private)
- /// <summary>
- /// Helper array to handle all key up presses from the events in Update!
- /// </summary>
- private readonly List<InputButton> keysUp = new List<InputButton>();
- #endregion
-
- #region keysDown (Private)
- /// <summary>
- /// Helper array to handle all key down presses from the events in Update!
- /// </summary>
- private readonly List<InputButton> keysDown = new List<InputButton>();
- #endregion
-
- #region inputTextBuffer (Private)
- /// <summary>
- /// Helper to keep track of the input text received in KeyPress events.
- /// </summary>
- private string inputTextBuffer = "";
- #endregion
-
- #region numberOfTimesBackspaceWasDown (Private)
- /// <summary>
- /// A little helper to keep track how many times backspace was pressed.
- /// This is not using the Released state because we want repeated key
- /// presses too (to allow quickly erasing text).
- /// </summary>
- private int numberOfTimesBackspaceWasDown;
- #endregion
-
- #region isConnected (Private)
- /// <summary>
- /// Sets IsConnected
- /// </summary>
- private bool isConnected;
- #endregion
-
- #region oskProcess (Private)
- private Process oskProcess;
- #endregion
-
- #region lowLevelKeyboardHook (Private)
- private IntPtr lowLevelKeyboardHook;
- #endregion
-
- #region keyboardProcessObject (Private)
- private readonly LowLevelKeyboardProc keyboardProcessObject;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create windows keyboard
- /// </summary>
- public WindowsKeyboard()
- {
- // Check if there are any keyboards Connected in the system.
- IsKeyboardConnected();
-
- // Retrieve the window handle for OSK
- hWnd = FindWindow("TFirstForm", "hvkFirstForm");
-
- // Windows Presentation Foundation
- IntPtr nativeWindowHande = DeltaApplication.Window.Handle;
- bool isWpfWindow =
- DeltaApplication.Window.GetType() == typeof(WindowWPF);
-
- if (isWpfWindow)
- {
- WPFVisual visual = HwndSource.FromHwnd(
- nativeWindowHande).CompositionTarget.RootVisual;
- WPFWindow wpfWindow = WPFWindow.GetWindow(visual);
- wpfWindow.KeyDown += OnWPFWindowKeyDown;
- wpfWindow.KeyUp += OnWPFWindowKeyUp;
- wpfWindow.PreviewTextInput += OnWPFWindowPreviewTextInput;
- }
- // Windows Forms
- else
- {
- Control formsWindow = Control.FromHandle(nativeWindowHande);
- formsWindow.KeyDown += OnFormsWindowKeyDown;
- formsWindow.KeyUp += OnFormsWindowKeyUp;
- formsWindow.KeyPress += OnFormsWindowKeyPress;
- }
-
- // Is this an editor window? Then still don't mark keys as handled.
- if (isWpfWindow ||
- DeltaApplication.Window.Title.Contains("Editor") ||
- DeltaApplication.Window.Title.Contains("Tool"))
- {
- MarkAllKeysAsHandled = false;
- }
- else
- {
- // This will prevent Alt, Control, etc. being send to windows, thus
- // disabling menus and Alt stealing the focus.
- MarkAllKeysAsHandled = true;
- // And also disable the WindowsKey (which is just plain annoying,
- // especially for games, we can also use it as an working input key)!
- // Get Current Module
- ProcessModule currentModule =
- Process.GetCurrentProcess().MainModule;
- // Assign callback function each time keyboard process
- keyboardProcessObject = LowLevelCaptureKey;
- // Set Hook of Keyboard Process for current module
- lowLevelKeyboardHook = SetWindowsHookEx(13, keyboardProcessObject,
- GetModuleHandle(currentModule.ModuleName), 0);
- } // else
- }
- #endregion
-
- #region IDisposable Members
- /// <summary>
- /// Dispose, will just get rid of the keyboard hook if it was created.
- /// </summary>
- public void Dispose()
- {
- if (lowLevelKeyboardHook != IntPtr.Zero)
- {
- UnhookWindowsHookEx(lowLevelKeyboardHook);
- lowLevelKeyboardHook = IntPtr.Zero;
- }
- }
- #endregion
-
- #region OnWPFWindowPreviewTextInput (Public)
- /// <summary>
- /// On WPF window preview text input
- /// </summary>
- /// <param name="sender">Sender</param>
- /// <param name="e">Event</param>
- public void OnWPFWindowPreviewTextInput(object sender,
- TextCompositionEventArgs e)
- {
- inputTextBuffer += e.Text;
- }
- #endregion
-
- #region HandleInput (Public)
- /// <summary>
- /// Handle input
- /// </summary>
- /// <param name="inputText">Input text</param>
- public override void HandleInput(ref string inputText)
- {
- // This is pretty easy, we just need to append the text :)
- if (inputTextBuffer.Length > 0)
- {
- inputText += inputTextBuffer;
- inputTextBuffer = "";
- }
-
- // Was backspace pressed. Use the Pressed state here to get the repeated
- // key presses too!
- if (numberOfTimesBackspaceWasDown > 0 &&
- inputText.Length > 0)
- {
- // Completely kill all the input text?
- if (numberOfTimesBackspaceWasDown >= inputText.Length)
- {
- inputText = "";
- }
- else
- {
- inputText = inputText.Remove(
- inputText.Length - numberOfTimesBackspaceWasDown,
- numberOfTimesBackspaceWasDown);
- }
- }
-
- // Was enter pressed? Then add a newline
- if (GetState(InputButton.Enter) == InputState.Released)
- {
- inputText += "\n";
- }
- }
- #endregion
-
- #region ForceShowOnScreenKeyboard (Public)
- /// <summary>
- /// Runs OSK.exe to simulate an on screen keyboard
- /// </summary>
- /// <remarks>
- /// Check this article for more details
- /// http://social.msdn.microsoft.com/Forums/da-DK/csharplanguage/thread/4e4511f8-ad50-4788-b5d7-2dd825b45665
- /// </remarks>
- public override void ForceShowOnScreenKeyboard()
- {
- if (oskProcessFilePath == null)
- {
- string winDirectory = Environment.GetEnvironmentVariable("WINDIR");
-
- // Start with the default path of Windows7 (and Vista ? too)
- string onScreenKeyboard = Path.Combine(winDirectory, "sysnative",
- "osk.exe");
- onScreenKeyboard = "";
- // if the osk file does't exists at this loaction then we have probably
- // an older Windows OS where the the file should be located in the
- // 'system32' folder
- if (File.Exists(onScreenKeyboard) == false)
- {
- onScreenKeyboard = Path.Combine(winDirectory, "system32", "osk.exe");
- } // if
-
- // if even this isn't the case then just ask the OS directly for this
- // file
- if (File.Exists(onScreenKeyboard) == false)
- {
- onScreenKeyboard = "osk.exe";
- } // if
-
- // Last remember the found path for the next time we need it
- oskProcessFilePath = onScreenKeyboard;
- } // if
-
- try
- {
- //oskProcess = new Process();
- //oskProcess.StartInfo.FileName = oskProcessFilePath;
- //oskProcess.StartInfo.UseShellExecute = false;
- //oskProcess.Start();
- oskProcess = Process.Start(oskProcessFilePath);
-
- // Some time the window is running but only in the back ground, this
- // happens in case we call HideOnScreenKeyboard, therefore we have
- // to bring it to the front again.
- //IntPtr windowHandle = (IntPtr)FindWindow(null, oskProcessTitle);
- //ShowWindow(windowHandle, 1);
- //ShowWindow(oskProcess.MainWindowHandle, 1);
- } // try
- catch (Exception ex)
- {
- Log.Warning("The OnScreenKeyboard couldn't be started because of:" +
- ex);
- } // catch
- }
- #endregion
-
- #region HideOnScreenKeyboard (Public)
- /// <summary>
- /// Kills the OSK.exe process thus hiding/ closing the On Screen Keyboard
- /// </summary>
- public override void HideOnScreenKeyboard()
- {
- if (oskProcess != null)
- {
- oskProcess.Kill();
- oskProcess = null;
- } // if
-
- //IntPtr windowHandle = (IntPtr)FindWindow(null, oskProcessTitle);
- //// Set the window handle to 0, it will disappear
- //ShowWindow(windowHandle, 0);
- // Set the window handle to 0, it will disappear
- //ShowWindow(oskProcess.MainWindowHandle, 0);
- }
- #endregion
-
- #region Methods (Private)
-
- #region GetInputButtonFromKeyCode
- /// <summary>
- /// Get input button from key code
- /// </summary>
- private static InputButton GetInputButtonFromKeyCode(Keys keyCode)
- {
- // Basically we can just convert the key code straight away (see default)
- // Some keys need special handling, they have different Xna key codes!
- switch (keyCode)
- {
- case Keys.Control:
- case Keys.ControlKey:
- return InputButton.Control;
-
- case Keys.LControlKey:
- return InputButton.LeftControl;
-
- case Keys.RControlKey:
- return InputButton.RightControl;
-
- case Keys.Alt:
- case Keys.Menu:
- case Keys.LMenu:
- case Keys.RMenu:
- return InputButton.Alt;
-
- case Keys.Shift:
- case Keys.ShiftKey:
- return InputButton.Shift;
-
- case Keys.LShiftKey:
- return InputButton.LeftShift;
-
- case Keys.RShiftKey:
- return InputButton.RightShift;
-
- case Keys.LWin:
- return InputButton.LeftWindows;
-
- case Keys.RWin:
- return InputButton.RightWindows;
-
- case Keys.Back:
- return InputButton.BackSpace;
-
- default:
- return (InputButton)(int)keyCode;
- }
- }
- #endregion
-
- #region OnFormsWindowKeyDown
- /// <summary>
- /// On forms window key down
- /// </summary>
- /// <param name="sender">Sender</param>
- /// <param name="e">Event args</param>
- private void OnFormsWindowKeyDown(object sender,
- KeyEventArgs e)
- {
- // Mark that we have data for the Update method.
- HasData = true;
-
- // Grab and convert the key code, which maps with our InputButton
- InputButton key = GetInputButtonFromKeyCode(e.KeyCode);
- keysDown.Add(key);
-
- // Always mark this key as being used already, this way we can
- // prevent windows from doing stuff for Alt, WindowsKey, etc.
- // This works fine for alt, but seems to have no effect for WindowsKey
- e.Handled = MarkAllKeysAsHandled;
-
- //Log.Info("OnFormsWindowKeyDown e.KeyCode=" + e.KeyCode +
- // ", e.KeyData=" + e.KeyData +
- // ", e.KeyValue=" + e.KeyValue +
- // ", key=" + key);
- }
- #endregion
-
- #region OnFormsWindowKeyUp
- /// <summary>
- /// On forms window key up
- /// </summary>
- /// <param name="sender">Sender</param>
- /// <param name="e">Event args</param>
- private void OnFormsWindowKeyUp(object sender,
- KeyEventArgs e)
- {
- // Mark that we have data for the Update method.
- HasData = true;
-
- // Grab and convert the key code, which maps with our InputButton
- InputButton key = GetInputButtonFromKeyCode(e.KeyCode);
- keysUp.Add(key);
-
- // Always mark this key as being used already, this way we can
- // prevent windows from doing stuff for Alt, WindowsKey, etc.
- // This works fine for alt, but seems to have no effect for WindowsKey
- e.Handled = MarkAllKeysAsHandled;
-
- //Log.Info("OnFormsWindowKeyUp e.KeyCode=" + e.KeyCode +
- // ", e.KeyData=" + e.KeyData +
- // ", e.KeyValue=" + e.KeyValue +
- // ", key=" + key);
- }
- #endregion
-
- #region OnFormsWindowKeyPress
- /// <summary>
- /// On forms window key press
- /// </summary>
- /// <param name="sender">Event sender</param>
- /// <param name="e">Event args</param>
- private void OnFormsWindowKeyPress(object sender, KeyPressEventArgs e)
- {
- //Log.Info("OnFormsWindowKeyPress e.KeyChar='" + e.KeyChar + "'");
- // Only handle real keys (above space)
- if (e.KeyChar >= ' ')
- {
- inputTextBuffer += e.KeyChar;
- }
- }
- #endregion
-
- #region OnWPFWindowKeyDown
- /// <summary>
- /// On WPF window key down
- /// </summary>
- /// <param name="sender">Sender</param>
- /// <param name="e">Event</param>
- private void OnWPFWindowKeyDown(object sender, WPFKeyEventArgs e)
- {
- // Mark that we have data for the Update method.
- HasData = true;
- // We need to convert the WPF button to a virtual key, which then
- // can be mapped to our InputButton enum.
- InputButton key = GetInputButtonFromKeyCode(
- (Keys)KeyInterop.VirtualKeyFromKey(e.Key));
- keysDown.Add(key);
- // Also add this key to the usedKeyIndices list
- AddKeyIndex((int)key);
- }
- #endregion
-
- #region OnWPFWindowKeyUp
- /// <summary>
- /// On WPF window key up
- /// </summary>
- /// <param name="sender">Sender</param>
- /// <param name="e">Event</param>
- private void OnWPFWindowKeyUp(object sender, WPFKeyEventArgs e)
- {
- // Mark that we have data for the Update method.
- HasData = true;
- // We need to convert the WPF button to a virtual key, which then
- // can be mapped to our InputButton enum.
- InputButton key = GetInputButtonFromKeyCode(
- (Keys)KeyInterop.VirtualKeyFromKey(e.Key));
- keysUp.Add(key);
- // Also add this key to the usedKeyIndices list
- AddKeyIndex((int)key);
-
- // If this was the space key, also add it to the inputTextBuffer because
- // OnWPFWindowPreviewTextInput does not seem to handle spaces:
- // http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/446ec083-04c8-43f2-89dc-1e2521a31f6b
- if (key == InputButton.Space)
- {
- inputTextBuffer += ' ';
- }
- }
- #endregion
-
- #region Update
- /// <summary>
- /// Update
- /// </summary>
- protected override void Update()
- {
- // No need to update if we have nothing connected!
- if ( //can never be true here: IsConnected == false ||
- // And skip we had no data before and still got nothing new
- HasData == false)
- {
- return;
- }
-
- // First update all key states (so we can overwrite it easily below)
- //for (int keyIndex = 0; keyIndex < NumberOfKeyboardKeys; keyIndex++)
- int numberOfNotPressedKeys = 0;
-
- // Time is stamped only when the key state is changed to Pressed
- for (int index = 0; index < numberOfKeyIndices; index++)
- {
- #region IsPressed
- int keyIndex = usedKeyIndices[index];
- // If button was pressed before? Then just set to pressing.
- if (keys[keyIndex] >= InputState.Pressed)
- {
- keys[keyIndex] = InputState.IsPressed;
- }
- #endregion
-
- #region NoPressed
- // Else set to NotPressed anymore
- else
- {
- keys[keyIndex] = InputState.NotPressed;
- numberOfNotPressedKeys++;
- }
- #endregion
-
- // We need to handle combined keys too (Control makes LeftControl and
- // RightControl key states to also be set, which is important for
- // Windows input, but then again LeftControl from Xna needs also to
- // set the Control key state in XnaKeyboard).
- HandleCombinedKeys(keyIndex);
- } // for
-
- #region Pressed
- // Now check if anything new was pressed
- numberOfTimesBackspaceWasDown = 0;
- foreach (InputButton newKey in keysDown)
- {
- int keyIndex = (int)newKey;
- // 'IsPressed' state and will get the 'Pressed' all the time
- // keysUp list for state checking, so only remove or add keys to the
- // lists at the events
- if (keys[keyIndex] != InputState.IsPressed)
- {
- keys[keyIndex] = InputState.Pressed;
- } // if
-
- // We need to handle combined keys too (Control makes LeftControl and
- // RightControl key states to also be set, which is important for
- // Windows input, but then again LeftControl from Xna needs also to
- // set the Control key state in XnaKeyboard).
- HandleCombinedKeys(keyIndex);
-
- // Also add this key to the usedKeyIndices list
- AddKeyIndex(keyIndex);
-
- // Also check how many times we pressed BackSpace, which is important
- // for HandleInput for textbox strings.
- if (newKey == InputButton.BackSpace)
- {
- numberOfTimesBackspaceWasDown++;
- }
- }
- #endregion
-
- #region Released
- // And finally go through all newly not anymore pressed keys
- foreach (InputButton keyNotPressedAnymore in keysUp)
- {
- int keyIndex = (int)keyNotPressedAnymore;
- keys[keyIndex] = InputState.Released;
-
- // We need to handle combined keys too (Control makes LeftControl and
- // RightControl key states to also be set, which is important for
- // Windows input, but then again LeftControl from Xna needs also to
- // set the Control key state in XnaKeyboard).
- HandleCombinedKeys(keyIndex);
-
- // Also add this key to the usedKeyIndices list
- AddKeyIndex(keyIndex);
- }
- #endregion
-
- // And handle the extra logic in the base class (e.g. virtual cursor)
- base.Update();
-
- // Check if we have no more pressed keys, then reset!
- if (numberOfNotPressedKeys == numberOfKeyIndices &&
- keysDown.Count == 0 &&
- keysUp.Count == 0)
- {
- numberOfNotPressedKeys = 0;
- // If we still have no data, no key is in any pressed state,
- // then we can clear the usedKeyIndices to skip all checks
- // for the next frame
- HasData = false;
- }
- else
- {
- keysDown.Clear();
- keysUp.Clear();
- }
- }
- #endregion
-
- #region IsKeyboardConnected
- /// <summary>
- /// This method will detect all the HIDs and record the keyboards that
- /// exists in the system
- /// </summary>
- /// <returns>
- /// true if any keyboard was present, otherwise false.
- /// </returns>
- private void IsKeyboardConnected()
- {
- isConnected = false;
- // Set the information you want to retrieve about the device you need
- // to detect through adding the GUID for the device.
- IntPtr devinfo = SetupDiGetClassDevs(ref GUID_DEVCLASS_KEYBOARD,
- IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT);
-
- // Defines the class instance that sets the device information
- SP_DEVINFO_DATA devInfoSet = new SP_DEVINFO_DATA
- {
- cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA))
- };
-
- // Check now if a keyboard is attached.
- if (SetupDiEnumDeviceInfo(devinfo, 0, ref devInfoSet))
- {
- isConnected = true;
- }
- }
- #endregion
-
- #region SetWindowsHookEx
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr SetWindowsHookEx(int id,
- LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
- #endregion
-
- #region UnhookWindowsHookEx
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern bool UnhookWindowsHookEx(IntPtr hook);
- #endregion
-
- #region CallNextHookEx
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode,
- IntPtr wp, IntPtr lp);
- #endregion
-
- #region GetModuleHandle
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- private static extern IntPtr GetModuleHandle(string name);
- #endregion
-
- #region GetAsyncKeyState
- [DllImport("user32.dll", CharSet = CharSet.Auto)]
- private static extern short GetAsyncKeyState(Keys key);
- #endregion
-
- // Global objects for the keyboard hook
-
- #region LowLevelCaptureKey
- /// <summary>
- /// Low level capture key
- /// </summary>
- /// <param name="nCode">Capture code to handle (we handle all above 0).
- /// </param>
- /// <param name="wp">Pointer to the first pointer (unused here)</param>
- /// <param name="lp">Pointer to the KBDLLHOOKSTRUCT</param>
- /// <returns>Pointer, but basically 0 if not successful, otherwise this
- /// method handled the capture key.</returns>
- private IntPtr LowLevelCaptureKey(int nCode, IntPtr wp, IntPtr lp)
- {
- if (nCode >= 0)
- {
- KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(
- lp, typeof(KBDLLHOOKSTRUCT));
-
- // Disable the Windows keys from stealing our app focus
- if (objKeyInfo.key == Keys.RWin ||
- objKeyInfo.key == Keys.LWin)
- {
- // Mark that we have data for the Update method.
- HasData = true;
- // Grab and convert the key code, which maps with our InputButton
- InputButton key = GetInputButtonFromKeyCode(objKeyInfo.key);
- // Is the key up or down?
- if ((objKeyInfo.flags & 0x80) != 0)
- {
- keysUp.Add(key);
- }
- else
- {
- keysDown.Add(key);
- }
-
- // We need to handle combined keys too (Control makes LeftControl and
- // RightControl key states to also be set, which is important for
- // Windows input, but then again LeftControl from Xna needs also to
- // set the Control key state in XnaKeyboard).
- HandleCombinedKeys((int)key);
-
- // Also add this key to the usedKeyIndices list
- AddKeyIndex((int)key);
-
- // And don't handle it any further (will not be handled by Windows
- // and also won't get to the keyboard events below).
- return (IntPtr)1;
- }
- } // if
-
- // Else use the normal keyboard handling hook
- return CallNextHookEx(lowLevelKeyboardHook, nCode, wp, lp);
- }
- #endregion
-
- #region SetupDiGetClassDevs
- /// <summary>
- /// Returns a handle to a device information set that contains requested
- /// device information elements for a local computer.
- /// </summary>
- /// <param name="classGuid">KeyboardGUID</param>
- /// <param name="enumerator">An identifier (ID) of a Plug and Play (PnP)
- /// enumerator.</param>
- /// <param name="hWndParent">A handle to the top-level window to be used
- /// for a user interface that is associated with installing a device
- /// instance in the device information set. This handle is optional and
- /// can be NULL.</param>
- /// <param name="flags">device information elements that are added to the
- /// device information set. </param>
- /// <returns>Pointer to the device information handle.</returns>
- [DllImport("setupapi.dll")]
- private static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid,
- IntPtr enumerator, IntPtr hWndParent, int flags);
- #endregion
-
- #region SetupDiEnumDeviceInfo
- /// <summary>
- /// The SetupDiEnumDeviceInfo function returns a SP_DEVINFO_DATA
- /// structure that specifies a device information element in a device
- /// information set.
- /// </summary>
- /// <param name="deviceInfoSet">A handle to the device information set
- /// for which to return an SP_DEVINFO_DATA structure that represents a
- /// device information element.</param>
- /// <param name="supplies">A zero-based index of the device information
- /// element to retrieve.</param>
- /// <param name="deviceInfoData">A pointer to an SP_DEVINFO_DATA structure
- /// to receive information about an enumerated device information
- /// element. The caller must set DeviceInfoData.cbSize to
- /// sizeof(SP_DEVINFO_DATA).</param>
- /// <returns>True if this call succeeded, false otherwise.</returns>
- [DllImport("setupapi.dll")]
- private static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,
- int supplies, ref SP_DEVINFO_DATA deviceInfoData);
- #endregion
-
- #region FindWindow
- /// <summary>
- /// Retrieves a handle to the top-level window whose class name and window
- /// name match the specified strings. This function does not search child
- /// windows. This function does not perform a case-sensitive search.
- /// </summary>
- /// <param name="_ClassName">
- /// The class name or a class atom created by a previous call to the
- /// RegisterClass or RegisterClassEx function.
- /// </param>
- /// <param name="_WindowName">
- /// The window name (the window's title)
- /// </param>
- /// <returns>
- /// Handle to the window that has the specified class name and
- /// window name.</returns>
- [DllImport("user32.dll", EntryPoint = "FindWindow")]
- private static extern Int32 FindWindow(string _ClassName,
- string _WindowName);
- #endregion
-
- #region ShowWindow
- /// <summary>
- /// Finds a specific runnin window for a given process
- /// </summary>
- /// <param name="hWnd">The window handler</param>
- /// <param name="nCmdShow">Specifies the view options, 1 = view,
- /// 0 = hide</param>
- /// <returns>true if successfully invoked</returns>
- [DllImport("user32.dll")]
- private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
- #endregion
-
- #endregion
- }
- }