带有 NotifyIcon 的无窗口应用程序的全局热键

Posted

技术标签:

【中文标题】带有 NotifyIcon 的无窗口应用程序的全局热键【英文标题】:Global hotkey for windowless app with NotifyIcon 【发布时间】:2020-10-07 21:10:27 【问题描述】:

我有一个无窗口的 winforms 应用程序,它使用 ApplicationContext 来设置用户可以控制的 NotifyIcon (TrayIcon)。但我也想使用热键。

我发现了一些使用 RegisterHotkey 的好方法(例如Global hotkeys in windowless .NET app),但它们都需要一个表单或一个原生窗口,由于副作用,我不想使用它们。

但我已经使用了 NotifyIcon (TrayIcon),我猜它已经有某种消息管道来触发点击等。我如何使用它来注册全局热键?

【问题讨论】:

这是不可能的,NotifyNativeWindow 是故意隐藏的,无法覆盖其行为。您需要的表单不必变得可见,只需要 5 行代码,并让您减去 ApplicationContext doowop,是的负代码:***.com/a/1732294/17034 请参阅我的answer here,了解另一种通过 NativeWindow 而不是 Form 来完成此操作的方法。刚刚意识到你已经找到了那个线程。您在 NativeWindow 中看到了哪些“副作用”? @Idle_Mind 首先是性能,加载窗口/表单会占用不必要的资源。其次,有时我在启动时会短暂闪烁窗口。第三,窗口显示在任务视图/alt+tab 视图中。第四,在关闭所有其他应用程序后,窗口有时会获得焦点。 我认为您实际上没有尝试过我的 NativeWindow 方法here,因为它没有做这些事情。从一个标准的 WinForms 项目开始,然后在 Program.cs 文件中添加代码。请务必更改Application.Run() 行,使其以new MyContext() 开头,而不是默认表单。之后,您实际上可以从项目中完全删除 Form1。运行它并尝试点击Alt-1Alt-2Alt-Q。我在 Alt-Tab 列表中找不到我的程序,并且它没有显示在任务栏中。从来没有出现过窗户。 @Idle_Mind 没试过,不。我以后可能会做。我现在用一个钩子(user32 > SetWindowsHookEx)解决了它,它运行良好。但是我在某处读到钩子不好,嗯.. 【参考方案1】:

我也有同样的要求,最后我在另一个问题Set global hotkeys using C# 上找到了这个答案,它与托盘(NotifyIcon)应用程序完美配合。

我正在对齐托盘应用程序的代码,只需按照以下步骤操作:

1.创建新类“KeyboardHook”

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

namespace MyTrayApp

    public sealed class KeyboardHook : IDisposable
    
        // Registers a hot key with Windows.
        [DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
        // Unregisters the hot key with Windows.
        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        /// <summary>
        /// Represents the window that is used internally to get the messages.
        /// </summary>
        private class Window : NativeWindow, IDisposable
        
            private static int WM_HOTKEY = 0x0312;

            public Window()
            
                // create the handle for the window.
                this.CreateHandle(new CreateParams());
            

            /// <summary>
            /// Overridden to get the notifications.
            /// </summary>
            /// <param name="m"></param>
            protected override void WndProc(ref Message m)
            
                base.WndProc(ref m);

                // check if we got a hot key pressed.
                if (m.Msg == WM_HOTKEY)
                
                    // get the keys.
                    Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
                    ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF);

                    // invoke the event to notify the parent.
                    if (KeyPressed != null)
                        KeyPressed(this, new KeyPressedEventArgs(modifier, key));
                
            

            public event EventHandler<KeyPressedEventArgs> KeyPressed;

            #region IDisposable Members

            public void Dispose()
            
                this.DestroyHandle();
            

            #endregion
        

        private Window _window = new Window();
        private int _currentId;

        public KeyboardHook()
        
            // register the event of the inner native window.
            _window.KeyPressed += delegate (object sender, KeyPressedEventArgs args)
            
                if (KeyPressed != null)
                    KeyPressed(this, args);
            ;
        

        /// <summary>
        /// Registers a hot key in the system.
        /// </summary>
        /// <param name="modifier">The modifiers that are associated with the hot key.</param>
        /// <param name="key">The key itself that is associated with the hot key.</param>
        public void RegisterHotKey(ModifierKeys modifier, Keys key)
        
            // increment the counter.
            _currentId = _currentId + 1;

            // register the hot key.
            if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key))
            
                string message = "The hotkey \"Ctrl+Alt+K\" could not be registered. This problem is probably causedby another tool claiming usage of the same hotkey!\r\n\r\nAll KeepOn features still work directly from the tray icon context menu without hotkey.";
                string title = "KeepOn";
                MessageBoxButtons buttons = MessageBoxButtons.OK;
                MessageBoxIcon icon = MessageBoxIcon.Exclamation;
                DialogResult result = MessageBox.Show(message, title, buttons,icon);
            
        

        /// <summary>
        /// A hot key has been pressed.
        /// </summary>
        public event EventHandler<KeyPressedEventArgs> KeyPressed;

        #region IDisposable Members

        public void Dispose()
        
            // unregister all the registered hot keys.
            for (int i = _currentId; i > 0; i--)
            
                UnregisterHotKey(_window.Handle, i);
            

            // dispose the inner native window.
            _window.Dispose();
        

        #endregion
    

    /// <summary>
    /// Event Args for the event that is fired after the hot key has been pressed.
    /// </summary>
    public class KeyPressedEventArgs : EventArgs
    
        private ModifierKeys _modifier;
        private Keys _key;

        internal KeyPressedEventArgs(ModifierKeys modifier, Keys key)
        
            _modifier = modifier;
            _key = key;
        

        public ModifierKeys Modifier
        
            get  return _modifier; 
        

        public Keys Key
        
            get  return _key; 
        
    

    /// <summary>
    /// The enumeration of possible modifiers.
    /// </summary>
    [Flags]
    public enum ModifierKeys : uint
    
        Alt = 1,
        Control = 2,
        Shift = 4,
        Win = 8
    

2。将代码添加到您的主类(应用启动类)

Program.cs 文件的 Main() 方法中检查您的启动类 Application.Run(new TrayApplicationContext());

public class TrayApplicationContext : ApplicationContext

     KeyboardHook hook = new KeyboardHook();

    public TaskTrayApplicationContext()
    
        // register the event that is fired after the key press.
        hook.KeyPressed += new EventHandler<KeyPressedEventArgs>(Close_App);
    
        // register the control + alt+ F12 combination as hot key.
        hook.RegisterHotKey(ModifierKeys.Control | ModifierKeys.Alt, Keys.F12);
    

    void Close_App(object sender, KeyPressedEventArgs e)
    
        Application.Exit();
    

感谢 stackoverflow 社区使之成为可能!!

【讨论】:

它使用了一个窗口,所有的开销都是如此,这就是我不想要的,如上所述。

以上是关于带有 NotifyIcon 的无窗口应用程序的全局热键的主要内容,如果未能解决你的问题,请参考以下文章

WPF NotifyIcon 中 ItemsControl 中按钮的命令绑定

winform 不想自动弹出窗口, 最小化後图标要显示在右下角 (像杀毒软件那样),要怎麼做?

C# 实现WinForm窗口最小化到系统托盘代码,并且判断左右鼠标的事件

WinForm 之 窗口最小化到托盘及右键图标显示菜单

【C#】WinForm中如何让窗口最小化时直接最小化到托盘,后台运行

Chrome扩展程序中的无Chrome窗口?