带有 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-1
、Alt-2
和Alt-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窗口最小化到系统托盘代码,并且判断左右鼠标的事件