altgov3en
altgov3en

Reputation: 1206

Catch WinKey+D key sequence in Winforms

I'm trying to make my application to be always presented on desktop level. It means that my app need to ignore key sequences like LWin+D or RWin+D . I tried to make it work this way:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (prefixSeen)
    {
        if (keyData == Keys.D)
        {
            MessageBox.Show("Got it!");
        }
        prefixSeen = false;
        return true;
    }
    if (keyData == Keys.LWin)
    {
        prefixSeen = true;
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

But it catches only the the RWin/LWin buttons, without the D button.

I've also tried to create my own message filter, but I've got lost in it. All these messages and Bitwise:

    public class KeystrokMessageFilter : System.Windows.Forms.IMessageFilter
    {
        public KeystrokMessageFilter() { }

        public bool PreFilterMessage(ref Message m)
        {
            if ((m.Msg == 256 /*0x0100*/))
            {
                switch (((int)m.WParam) | ((int)Control.ModifierKeys))
                {
                    case (int)(Keys.Control | Keys.Alt | Keys.K):
                        MessageBox.Show("You pressed ctrl + alt + k");
                        break;
                    case (int)(Keys.Control | Keys.C): MessageBox.Show("ctrl+c");
                        break;
                    case (int)(Keys.Control | Keys.V): MessageBox.Show("ctrl+v");
                        break;
                    case (int)Keys.Up: MessageBox.Show("You pressed up");
                        break;
                }
            }
            return false;
        }
}

Application.AddMessageFilter(keyStrokeMessageFilter);

So, how do I make my application to catch/ignore R/LWin+D?

Upvotes: 3

Views: 2259

Answers (3)

Prabu Kumar GP
Prabu Kumar GP

Reputation: 11

I know this is way too old question. But hey I recently had to develop an app which will stay in desktop like a widget (count down app for my companys annual bash).

I managed to keep the window stay in background (desktop winform) even when pressing the WinKey+D or WinKey+M via windows low level keyboard hook. I used dss539's code and just improved it to keep my winforms app alive at desktop level.

The trick is set the forms TopMost property to true when you encounter WinKey+D or WinKey+M and set it back to false for other keys and handover the handle for windows to process.

Below is the sample code.

In Program.cs - Main()

try
{
    using (var proc = Process.GetCurrentProcess())
    using (var curModule = proc.MainModule)
    {
        var moduleHandle = GetModuleHandle(curModule.ModuleName);
        HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, IgnoreWin_DOrM, moduleHandle, 0);
    }

    frmForm1 = new frmIGS();
    Application.Run(frmForm1);
}
finally
{
    UnhookWindowsHookEx(HookHandle);
}

Then write your keyboard hook method:

static IntPtr IgnoreWin_DOrM(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode == HC_ACTION
        && (IsWin_D(wParam, lParam) || IsWin_M(wParam, lParam)))
    {
        frmForm1.SetTopMost = true;
    }
    else
    {
        //if (frmForm1.SetTopMost)
        {
            frmForm1.SetTopMost = false;
        }
    }

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

static bool IsWin_D(IntPtr wParam, IntPtr lParam)
{
    if ((int)wParam != WM_KEYDOWN)
        return false;

    var keyInfo = (KbHookParam)Marshal.PtrToStructure(lParam, typeof(KbHookParam));
    if (keyInfo.VkCode != (int)Keys.D) return false;
    return GetAsyncKeyState(Keys.LWin) < 0
            || GetAsyncKeyState(Keys.RWin) < 0;
}

static bool IsWin_M(IntPtr wParam, IntPtr lParam)
{
    if ((int)wParam != WM_KEYDOWN)
        return false;

    var keyInfo = (KbHookParam)Marshal.PtrToStructure(lParam, typeof(KbHookParam));
    if (keyInfo.VkCode != (int)Keys.M) return false;
    return GetAsyncKeyState(Keys.LWin) < 0
            || GetAsyncKeyState(Keys.RWin) < 0;
}

I exposed a public property for form, which sets the topmost property in its code!

private bool topMost = false;
public bool SetTopMost
{
    get
    {
        return topMost;
    }
    set
    {

        this.TopMost = topMost = value;
    }
}

Viola. Now my code stays alive in background irrespective of the windows key combos. But will not stay on top of the other applications, allowing the user to do their normal work! And this was the requirement!

It worked well in Windows 10 :)

Upvotes: 1

dss539
dss539

Reputation: 6950

This code registers a low level keyboard hook and listens for D keypress while the windows key is held. If this is detected, the hook simply ignores the keypress. Otherwise, it forwards the keypress along.

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

namespace KbHook
{
    public static class Program
    {
        const int HC_ACTION = 0;
        const int WH_KEYBOARD_LL = 13;
        const int WM_KEYDOWN = 0x0100;
        static IntPtr HookHandle = IntPtr.Zero;
        static Form1 Form1;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr SetWindowsHookEx(int idHook, KbHook lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr GetModuleHandle(string lpModuleName);

        [STAThread]
        static void Main()
        {
            try
            {
                using (var proc = Process.GetCurrentProcess())
                    using (var curModule = proc.MainModule)
                    {
                        var moduleHandle = GetModuleHandle(curModule.ModuleName);
                        HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, IgnoreWin_D, moduleHandle, 0);
                    }

                Form1 = new Form1();
                Application.Run(Form1);
            }
            finally
            {
                UnhookWindowsHookEx(HookHandle);
            }
        }

        [DllImport("user32.dll")]
        static extern short GetAsyncKeyState(Keys vKey);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        static IntPtr IgnoreWin_D(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode == HC_ACTION
                && IsWin_D(wParam, lParam))
                return (IntPtr) 1; //just ignore the key press

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

        static bool IsWin_D(IntPtr wParam, IntPtr lParam)
        {
            if ((int) wParam != WM_KEYDOWN)
                return false;

            var keyInfo = (KbHookParam) Marshal.PtrToStructure(lParam, typeof (KbHookParam));
            if (keyInfo.VkCode != (int) Keys.D) return false;
            return GetAsyncKeyState(Keys.LWin) < 0
                   || GetAsyncKeyState(Keys.RWin) < 0;
        }

        delegate IntPtr KbHook(int nCode, IntPtr wParam, [In] IntPtr lParam);

        [StructLayout(LayoutKind.Sequential)]
        struct KbHookParam
        {
            public readonly int VkCode;
            public readonly int ScanCode;
            public readonly int Flags;
            public readonly int Time;
            public readonly IntPtr Extra;
        }
    }
}

See also How to hook Win + Tab using LowLevelKeyboardHook

Upvotes: 2

Kanadaj
Kanadaj

Reputation: 991

Try the solution from How to detect when a windows form is being minimized? to catch the event before it has fired and handle it; it should allow you to ignore it.

Upvotes: 0

Related Questions