【问题标题】:Catch WinKey+D key sequence in Winforms在 Winforms 中捕获 WinKey+D 键序列
【发布时间】:2015-08-02 11:31:39
【问题描述】:

我正在尝试让我的应用程序始终呈现在桌面级别。这意味着我的应用程序需要忽略 LWin+D 或 RWin+D 等键序列。我试图让它这样工作:

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

但它只捕获 RWin/LWin 按钮,没有 D 按钮。

我也尝试过创建自己的消息过滤器,但我迷路了。所有这些messages 和 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);

那么,如何让我的应用程序捕获/忽略 R/LWin+D?

【问题讨论】:

  • 仅仅捕捉 R/LWin+D 本身是不够的,因为单击屏幕的右下角会产生相同的反应。您需要改为处理最小化事件。
  • 你是对的,@Dracor。我试图处理最小化事件,但它已经足够了。就像 R/LWin+D 更重要,它虽然处理了事件,但它最小化了窗口。我试过这个: private void OpenFileWin_Resize(object sender, EventArgs e) { if (WindowState == FormWindowState.Minimized) { this.Focus(); } }

标签: c# winforms hook window-messages


【解决方案1】:

此代码注册一个低级键盘挂钩并在按住 windows 键时侦听D 按键。如果检测到这一点,钩子会简单地忽略按键。否则,它会转发按键。

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;
        }
    }
}

另见How to hook Win + Tab using LowLevelKeyboardHook

【讨论】:

    【解决方案2】:

    我知道这个问题太老了。但是,嘿,我最近不得不开发一个像小部件一样留在桌面的应用程序(我公司年度庆典的倒计时应用程序)。

    即使通过 Windows 低级键盘挂钩按下 WinKey+D 或 WinKey+M,我也设法使窗口保持在后台(桌面 winform)。我使用了 dss539 的代码并对其进行了改进,以使我的 winforms 应用程序在桌面级别保持活跃。

    诀窍是在遇到 WinKey+D 或 WinKey+M 时将表单的 TopMost 属性设置为 true,然后将其他键设置回 false,并移交句柄以供窗口处理。

    下面是示例代码。

    在 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);
    }
    

    然后编写你的键盘钩子方法:

    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;
    }
    

    我为表单公开了一个公共属性,它在其代码中设置了最顶层的属性!

    private bool topMost = false;
    public bool SetTopMost
    {
        get
        {
            return topMost;
        }
        set
        {
    
            this.TopMost = topMost = value;
        }
    }
    

    维奥拉。现在,无论 Windows 键组合如何,我的代码都在后台保持活跃。但不会停留在其他应用程序之上,让用户做他们的正常工作!这就是要求!

    它在 Windows 10 中运行良好 :)

    【讨论】:

      【解决方案3】:

      尝试How to detect when a windows form is being minimized? 的解决方案,在事件触发之前捕获并处理它;它应该允许你忽略它。

      【讨论】:

      • 感谢您的回复,@Dracor。我已经尝试捕获最小化事件,但根据this article 它永远不会进入最小化事件。 R/LWin+D 不会触发最小化事件,不像 R/LWin+M 女巫实际上是最小化窗口,因此它会触发最小化事件。
      猜你喜欢
      • 2011-01-28
      • 1970-01-01
      • 2023-03-28
      • 2016-02-03
      • 2011-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多