How to make a keyboard hook global across processes

Solution 1:

In order for your keyboard hook to be accessible from all processes, it has to be placed in a DLL which will then be loaded into each process' address space. One important thing to keep in mind is that since each instance of the DLL is loaded in a separate process, each will have its own copy of any global variables in the DLL. If you need to share data between these instances, the simplest way is to create a shared data segment in the DLL. The following code is from an RSI monitoring program I wrote.

//
// some data will be shared across all
// instances of the DLL
//
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
int iKeyCount = 0;
HHOOK hKeyboardHook = 0;
HHOOK hMouseHook = 0;
#pragma data_seg()

//
// instance specific data
//
HMODULE hInstance = 0;

//
// DLL load/unload entry point
//
BOOL APIENTRY DllMain(HANDLE hModule, 
                      DWORD  dwReason, 
                      LPVOID lpReserved)
{
   switch (dwReason)
   {
   case DLL_PROCESS_ATTACH :
      hInstance = (HINSTANCE) hModule;
      break;

   case DLL_THREAD_ATTACH :
      break;

   case DLL_THREAD_DETACH :
      break;

   case DLL_PROCESS_DETACH :
      break;
   }
   return TRUE;
}

//
// keyboard hook
//
LRESULT CALLBACK KeyboardProc(int code,       // hook code
                              WPARAM wParam,  // virtual-key code
                              LPARAM lParam)  // keystroke-message information
{
   if ((lParam & 0x80000000) != 0)
   {
      ++iKeyCount;
   }
   return CallNextHookEx(hKeyboardHook, code, wParam, lParam);
}

//
// mouse hook
//
LRESULT CALLBACK MouseProc(int code,       // hook code
                           WPARAM wParam,  // message identifier
                           LPARAM lParam)  // mouse coordinates
{
   switch (wParam)
   {
   case WM_LBUTTONDOWN :
   case WM_MBUTTONDOWN :
   case WM_RBUTTONDOWN :
   case WM_LBUTTONDBLCLK :
   case WM_MBUTTONDBLCLK :
   case WM_RBUTTONDBLCLK :
      ++iKeyCount;
      break;
   }
   return CallNextHookEx(hMouseHook, code, wParam, lParam);
}

//
// install keyboard/mouse hooks
//
void KBM_API InstallHooks(void)
{
   hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hInstance, 0);
   hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, hInstance, 0);
}

//
// remove keyboard/mouse hooks
//
void KBM_API RemoveHooks(void)
{
   UnhookWindowsHookEx(hKeyboardHook);
   UnhookWindowsHookEx(hMouseHook);
   hKeyboardHook = hMouseHook = 0;
}

//
// retrieve number of keystrokes
//
int KBM_API FetchKeyCount(bool bClear)
{
   int kc = iKeyCount;
   if (bClear)
      iKeyCount = 0;
   return kc;
}

Solution 2:

Avoid codeproject samples. ( plenty of bugs, bad copies of MSDN )

See the tons of complete samples on MSDN on hooks (MSDN, SDK, KB, etc)

And you don't need any DLL, just use LL hooks

Solution 3:

For Glopbal Keyboard Hook just for Hot Key's, then Register hotkey is the best (its done by Microsoft):

https://msdn.microsoft.com/en-us/library/windows/desktop/ms646309(v=vs.85).aspx

Download the sample winform app and see for yourself:

https://code.msdn.microsoft.com/CppRegisterHotkey-7bd897a8 C++ https://code.msdn.microsoft.com/CSRegisterHotkey-e3f5061e C# https://code.msdn.microsoft.com/VBRegisterHotkey-50af3179 VB.Net

Winform App:

/****************************** Module Header ******************************\
* Module Name:  MainForm.cs
* Project:      CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
* 
* This is the main form of this application. It is used to initialize the UI 
* and handle the events.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
* 
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CSRegisterHotkey
{
    public partial class MainForm : Form
    {
        HotKeyRegister hotKeyToRegister = null;
        Keys registerKey = Keys.None;
        KeyModifiers registerModifiers = KeyModifiers.None;

        public MainForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Handle the KeyDown of tbHotKey. In this event handler, check the pressed keys.
        /// The keys that must be pressed in combination with the key Ctrl, Shift or Alt,
        /// like Ctrl+Alt+T. The method HotKeyRegister.GetModifiers could check whether 
        /// "T" is pressed.
        /// </summary>
        private void tbHotKey_KeyDown(object sender, KeyEventArgs e)
        {
            // The key event should not be sent to the underlying control.
            e.SuppressKeyPress = true;

            // Check whether the modifier keys are pressed.
            if (e.Modifiers != Keys.None)
            {
                Keys key = Keys.None;
                KeyModifiers modifiers = HotKeyRegister.GetModifiers(e.KeyData, out key);

                // If the pressed key is valid...
                if (key != Keys.None)
                {
                    this.registerKey = key;
                    this.registerModifiers = modifiers;

                    // Display the pressed key in the textbox.
                    tbHotKey.Text = string.Format("{0}+{1}",
                        this.registerModifiers, this.registerKey);

                    // Enable the button.
                    btnRegister.Enabled = true;
                }
            }
        }


        /// <summary>
        /// Handle the Click event of btnRegister.
        /// </summary>
        private void btnRegister_Click(object sender, EventArgs e)
        {
            try
            {
                // Register the hotkey.
                hotKeyToRegister = new HotKeyRegister(this.Handle, 100,
                    this.registerModifiers, this.registerKey);

                // Register the HotKeyPressed event.
                hotKeyToRegister.HotKeyPressed += new EventHandler(HotKeyPressed);

                // Update the UI.
                btnRegister.Enabled = false;
                tbHotKey.Enabled = false;
                btnUnregister.Enabled = true;
            }
            catch (ArgumentException argumentException)
            {
                MessageBox.Show(argumentException.Message);
            }
            catch (ApplicationException applicationException)
            {
                MessageBox.Show(applicationException.Message);
            }
        }

        /// <summary>
        /// Show a message box if the HotKeyPressed event is raised.
        /// </summary>
        void HotKeyPressed(object sender, EventArgs e)
        {
           //Here is the magic!!!!!!!!'

           //DO SOMETHING COOL!!! Or Just activate this winform

            if (this.WindowState == FormWindowState.Minimized)
            {
                this.WindowState = FormWindowState.Normal;
            }
            this.Activate();



        }

        /// <summary>
        /// Handle the Click event of btnUnregister.
        /// </summary>
        private void btnUnregister_Click(object sender, EventArgs e)
        {
            // Dispose the hotKeyToRegister.
            if (hotKeyToRegister != null)
            {
                hotKeyToRegister.Dispose();
                hotKeyToRegister = null;
            }

            // Update the UI.
            tbHotKey.Enabled = true;
            btnRegister.Enabled = true;
            btnUnregister.Enabled = false;
        }


        /// <summary>
        /// Dispose the hotKeyToRegister when the form is closed.
        /// </summary>
        protected override void OnClosed(EventArgs e)
        {
            if (hotKeyToRegister != null)
            {
                hotKeyToRegister.Dispose();
                hotKeyToRegister = null;
            }

            base.OnClosed(e);
        }
    }
}

HotKeyRegister.cs

/****************************** Module Header ******************************\
* Module Name:  HotKeyRegister.cs
* Project:      CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
* 
* This class imports the method RegisterHotKey and UnregisterHotKey in 
* user32.dll to define or free a system-wide hot key.
* 
* The method Application.AddMessageFilter is used to add a message filter to 
* monitor Windows messages as they are routed to their destinations. Before a 
* message is dispatched, the method PreFilterMessage could handle it. If a 
* WM_HOTKEY messages was generated by the hot key that was registered by this 
* HotKeyRegister object, then raise a HotKeyPressed event.
* 
* This class also supplies a static method GetModifiers to get the modifiers 
* and key from the KeyData property of KeyEventArgs.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
* 
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Security.Permissions;

namespace CSRegisterHotkey
{
    public class HotKeyRegister : IMessageFilter, IDisposable
    {
        /// <summary>
        /// Define a system-wide hot key.
        /// </summary>
        /// <param name="hWnd">
        /// A handle to the window that will receive WM_HOTKEY messages generated by the
        /// hot key. If this parameter is NULL, WM_HOTKEY messages are posted to the 
        /// message queue of the calling thread and must be processed in the message loop.
        /// </param>
        /// <param name="id">
        /// The identifier of the hot key. If the hWnd parameter is NULL, then the hot 
        /// key is associated with the current thread rather than with a particular 
        /// window. 
        /// </param>
        /// <param name="fsModifiers">
        /// The keys that must be pressed in combination with the key specified by the 
        /// uVirtKey parameter in order to generate the WM_HOTKEY message. The fsModifiers
        /// parameter can be a combination of the following values.
        /// MOD_ALT     0x0001
        /// MOD_CONTROL 0x0002
        /// MOD_SHIFT   0x0004
        /// MOD_WIN     0x0008
        /// </param>
        /// <param name="vk">The virtual-key code of the hot key.</param>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, 
            KeyModifiers fsModifiers, Keys vk);

        /// <summary>
        /// Frees a hot key previously registered by the calling thread. 
        /// </summary>
        /// <param name="hWnd">
        /// A handle to the window associated with the hot key to be freed. This parameter
        /// should be NULL if the hot key is not associated with a window.
        /// </param>
        /// <param name="id">
        /// The identifier of the hot key to be freed. 
        /// </param>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        /// <summary>
        /// Get the modifiers and key from the KeyData property of KeyEventArgs.
        /// </summary>
        /// <param name="keydata">
        /// The KeyData property of KeyEventArgs. The KeyData is a key in combination
        /// with modifiers.
        /// </param>
        /// <param name="key">The pressed key.</param>
        public static KeyModifiers GetModifiers(Keys keydata, out Keys key)
        {
            key = keydata;
            KeyModifiers modifers = KeyModifiers.None;

            // Check whether the keydata contains the CTRL modifier key.
            // The value of Keys.Control is 131072.
            if ((keydata & Keys.Control) == Keys.Control)
            {
                modifers |= KeyModifiers.Control;

                key = keydata ^ Keys.Control;
            }

            // Check whether the keydata contains the SHIFT modifier key.
            // The value of Keys.Control is 65536.
            if ((keydata & Keys.Shift) == Keys.Shift)
            {
                modifers |= KeyModifiers.Shift;
                key = key ^ Keys.Shift;
            }

            // Check whether the keydata contains the ALT modifier key.
            // The value of Keys.Control is 262144.
            if ((keydata & Keys.Alt) == Keys.Alt)
            {
                modifers |= KeyModifiers.Alt;
                key = key ^ Keys.Alt;
            }

            // Check whether a key other than SHIFT, CTRL or ALT (Menu) is pressed.
            if (key == Keys.ShiftKey || key == Keys.ControlKey || key == Keys.Menu)
            {
                key = Keys.None;
            }

            return modifers;
        }

        /// <summary>
        /// Specify whether this object is disposed.
        /// </summary>
        bool disposed = false;

        /// <summary>
        /// This constant could be found in WinUser.h if you installed Windows SDK.
        /// Each windows message has an identifier, 0x0312 means that the mesage is 
        /// a WM_HOTKEY message.
        /// </summary>
        const int WM_HOTKEY = 0x0312;

        /// <summary>
        /// A handle to the window that will receive WM_HOTKEY messages generated by the
        /// hot key.
        /// </summary>
        public IntPtr Handle { get; private set; }

        /// <summary>
        /// A normal application can use any value between 0x0000 and 0xBFFF as the ID 
        /// but if you are writing a DLL, then you must use GlobalAddAtom to get a 
        /// unique identifier for your hot key. 
        /// </summary>
        public int ID { get; private set; }

        public KeyModifiers Modifiers { get; private set; }

        public Keys Key { get; private set; }

        /// <summary>
        /// Raise an event when the hotkey is pressed.
        /// </summary>
        public event EventHandler HotKeyPressed;


        public HotKeyRegister(IntPtr handle, int id, KeyModifiers modifiers, Keys key)
        {
            if (key == Keys.None || modifiers == KeyModifiers.None)
            {
                throw new ArgumentException("The key or modifiers could not be None.");
            }

            this.Handle = handle;
            this.ID = id;
            this.Modifiers = modifiers;
            this.Key = key;

            RegisterHotKey();

            // Adds a message filter to monitor Windows messages as they are routed to
            // their destinations.
            Application.AddMessageFilter(this);
        }


        /// <summary>
        /// Register the hotkey.
        /// </summary>
        private void RegisterHotKey()
        {
            bool isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);

            // If the operation failed, try to unregister the hotkey if the thread 
            // has registered it before.
            if (!isKeyRegisterd)
            {
                // IntPtr.Zero means the hotkey registered by the thread.
                UnregisterHotKey(IntPtr.Zero, ID);

                // Try to register the hotkey again.
                isKeyRegisterd = RegisterHotKey(Handle, ID, Modifiers, Key);

                // If the operation still failed, it means that the hotkey was already 
                // used in another thread or process.
                if (!isKeyRegisterd)
                {
                    throw new ApplicationException("The hotkey is in use");
                }
            }
        }


        /// <summary>
        /// Filters out a message before it is dispatched.
        /// </summary>
        [PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
        public bool PreFilterMessage(ref Message m)
        {
            // The property WParam of Message is typically used to store small pieces 
            // of information. In this scenario, it stores the ID.
            if (m.Msg == WM_HOTKEY
                && m.HWnd == this.Handle
                && m.WParam == (IntPtr)this.ID
                && HotKeyPressed != null)
            {
                // Raise the HotKeyPressed event if it is an WM_HOTKEY message.
                HotKeyPressed(this, EventArgs.Empty);

                // True to filter the message and stop it from being dispatched.
                return true;
            }

            // Return false to allow the message to continue to the next filter or 
            // control.
            return false;
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Unregister the hotkey.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            // Protect from being called multiple times.
            if (disposed)
            {
                return;
            }

            if (disposing)
            {

                // Removes a message filter from the message pump of the application.
                Application.RemoveMessageFilter(this);

                UnregisterHotKey(Handle, ID);
            }

            disposed = true;
        }
    }
}

KeyModifiers.cs:

/****************************** Module Header ******************************\
* Module Name:  KeyModifiers.cs
* Project:      CSRegisterHotkey
* Copyright (c) Microsoft Corporation.
* 
* This enum defines the modifiers to generate the WM_HOTKEY message. 
* See http://msdn.microsoft.com/en-us/library/ms646309(VS.85).aspx.
* 
* This source is subject to the Microsoft Public License.
* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
* All other rights reserved.
* 
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/

using System;

namespace CSRegisterHotkey
{
    [Flags]
    public enum KeyModifiers
    {
        None = 0,
        Alt = 1,
        Control = 2,
        Shift = 4,

        // Either WINDOWS key was held down. These keys are labeled with the Windows logo.
        // Keyboard shortcuts that involve the WINDOWS key are reserved for use by the 
        // operating system.
        Windows = 8
    }
}