Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# global keyboard hook, that opens a form from a console application [duplicate]

So I have a C# Console Application with a Form, which I want to open using hotkeys. Let's say for example Ctrl + < opens the form. So I got the code to handle a globalkeylistener now, but it looks like I failed by implementing it. It made a while loop to prevent it from closing the program and I tryed to get an input from the user with the kbh_OnKeyPressed method.

I tryed to implement it this way:

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Globalkey
{
    static class Program
    {
        [DllImport("user32.dll")]
        private static extern bool RegisterHotkey(int id, uint fsModifiers, uint vk);

        private static bool lctrlKeyPressed;
        private static bool f1KeyPressed;


        [STAThread]
        static void Main()
        {

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            LowLevelKeyboardHook kbh = new LowLevelKeyboardHook();
            kbh.OnKeyPressed += kbh_OnKeyPressed;
            kbh.OnKeyUnpressed += kbh_OnKeyUnpressed;
            kbh.HookKeyboard();

            while(true) { }

        }

        private static void kbh_OnKeyUnpressed(object sender, Keys e)
        {
            if (e == Keys.LControlKey)
            {
                lctrlKeyPressed = false;
                Console.WriteLine("CTRL unpressed");
            }
            else if (e == Keys.F1)
            {
                f1KeyPressed = false;
                Console.WriteLine("F1 unpressed");
            }
        }

        private static void kbh_OnKeyPressed(object sender, Keys e)
        {
            if (e == Keys.LControlKey)
            {
                lctrlKeyPressed = true;
                Console.WriteLine("CTRL pressed");
            }
            else if (e == Keys.F1)
            {
                f1KeyPressed = true;
                Console.WriteLine("F1 pressed");
            }
            CheckKeyCombo();
        }

        static void CheckKeyCombo()
        {
            if (lctrlKeyPressed && f1KeyPressed)
            {
                Application.Run(new Form1());
            }
        }
    }
}
like image 731
Craiy Avatar asked Sep 02 '17 11:09

Craiy


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.


1 Answers

What you need is a Lowlevel Keyboard Hook.

This could look somewhat like this:

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

public class LowLevelKeyboardHook
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_SYSKEYDOWN = 0x0104;
    private const int WM_KEYUP = 0x101;
    private const int WM_SYSKEYUP = 0x105;

    [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);

    public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    public event EventHandler<Keys> OnKeyPressed;
    public event EventHandler<Keys> OnKeyUnpressed;

    private LowLevelKeyboardProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    public LowLevelKeyboardHook()
    {
        _proc = HookCallback;
    }

    public void HookKeyboard()
    {
        _hookID = SetHook(_proc);
    }

    public void UnHookKeyboard()
    {
        UnhookWindowsHookEx(_hookID);
    }

    private 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 IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);

            OnKeyPressed.Invoke(this, ((Keys)vkCode));
        }
        else if(nCode >= 0 && wParam == (IntPtr)WM_KEYUP ||wParam == (IntPtr)WM_SYSKEYUP)
        {
            int vkCode = Marshal.ReadInt32(lParam);

            OnKeyUnpressed.Invoke(this, ((Keys)vkCode));
        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);            
    }
}

To implement it, you could use something like this:

kbh = new LowLevelKeyboardHook();
kbh.OnKeyPressed += kbh_OnKeyPressed;
kbh.OnKeyUnpressed += kbh_OnKeyUnpressed;
kbh.HookKeyboard();

The event could be handled like that:

bool lctrlKeyPressed;
bool f1KeyPressed;

void kbh_OnKeyPressed(object sender, Keys e)
{
    if (e == Keys.LControlKey)
    {
        lctrlKeyPressed = true;
    }
    else if (e == Keys.F1)
    {
        f1KeyPressed= true;
    }
    CheckKeyCombo();
}

void kbh_OnKeyUnPressed(object sender, Keys e)
{
    if (e == Keys.LControlKey)
    {
        lctrlKeyPressed = false;
    }
    else if (e == Keys.F1)
    {
        f1KeyPressed= false;
    }
}

void CheckKeyCombo()
{
    if (lctrlKeyPressed && f1KeyPressed)
    {
        //Open Form
    }
}

For actual understanding, i would recommend you to have a read on P/Invoke. That is making use of the unmanaged APIs that windows provides.

For a full list of P/Invoke possibilites, pinvoke.net is a great source.

For better understanding in general, The official MSDN Website is a good source, too.

EDIT:

It seems like you're actually using a Console Application, not a WinForm one. In that case, you have to run the program a bit differently:

[STAThread]
static void Main()
{

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    LowLevelKeyboardHook kbh = new LowLevelKeyboardHook();
    kbh.OnKeyPressed += kbh_OnKeyPressed;
    kbh.OnKeyUnpressed += kbh_OnKeyUnpressed;
    kbh.HookKeyboard();

    Application.Run();

    kbh.UnHookKeyboard();

}

The Run() method of the Application Class starts a standard loop for your application. This is necessary for the Hook to work, because a mere Console Application without this loop is, as far as I know, not capable of triggering those global key events.

Using this implementation, pressing and releasing the defined keys gives the following output:

Console Output

Note: I obviously replaced

Application.Run(new Form1());

in the CheckKeyCombo() method with

Console.WriteLine("KeyCombo pressed");

like image 174
rqnn Avatar answered Oct 27 '22 08:10

rqnn