Global hotkeys in windowless .NET app

孤街醉人 提交于 2021-01-20 07:54:10

问题


I've read similar questions around this issue, including Best way to tackle global hotkey processing in C#? and Set global hotkeys using C#. I've also investigated a NuGet package Global Hotkeys which appears to be in its infancy.

The problem here is, most of them seem to be designed for Winforms, or could probably run in WPF. The P/Invoke they're using seems to require a window handle. I'm thinking of having a windoless application here, i.e running without a main form or window, except when a certain key combo is pressed, so there might not actually be a handle.

So, would passing a cheeky 0 as the Window handle for the P/Invoke cause it to not look for a window to process the keypress on? Or is my best bet here to use an invisible unfocusable Window?

To add a little context, I'm making a windowless app to be used with TTS providing the feedback for control, my target audience here are blind and visually impaired users. Occasionally, things will have to be entered, so I want to be able to launch forms when necessary, but for the most part I'd like there to be no window cluttering things up.

Some sample code (I can't verify if this would work properly yet).

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    ScreenReader.sapiEnable(true);
    ScreenReader.SayString("Launching application...");
    // bind hotkeys here
    Application.Run();
}

// called when the right keyboard shortcut is pressed
static void ExitApp()
{
    ScreenReader.SayString("Exiting program");
    Application.Exit();
}

Thanks for any help you may be able to provide.


回答1:


Using RegisterHotkey() is boilerplate to detect a hotkey press. I won't repeat it here, you can easily google sample code from the function name.

It does however require a window, no workaround for that. It just doesn't have to be a visible window. By far the simplest way to create an invisible window is to use a Form class, like you do in any Winforms application, and set ShowInTaskbar = False, WindowState = Minimized, FormBorderStyle = FixedToolWindow. Call RegisterHotkey() in your OnLoad() method override, you'll need the Handle property. Easy peasy.




回答2:


You could also use the NativeWindow class. In the example below, I'm also using an ApplicationContext which is a great way to start a "windowless" application:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MyContext());
    }
}

public class MyContext : ApplicationContext
{

    private MyHotKey hotkeys = null;

    public MyContext()
    {
        hotkeys = new MyHotKey();
        hotkeys.HotkeyPressed += new MyHotKey.HotkeyDelegate(hotkeys_HotkeyPressed);
    }

    private void hotkeys_HotkeyPressed(int ID)
    {
        switch (ID)
        {
            case 1001:
                MessageBox.Show("Alt-1");
                break;

            case 1002:
                MessageBox.Show("Alt-2");
                break;

            case 1003: // Alt-Q
                Application.Exit();
                break;
            default:
                break;
        }
    }

}

public class MyHotKey : NativeWindow
{

    private const int WM_HOTKEY = 0x0312;
    private const int WM_DESTROY = 0x0002;

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    private List<Int32> IDs = new List<int>();

    public delegate void HotkeyDelegate(int ID);
    public event HotkeyDelegate HotkeyPressed;

    public MyHotKey()
    {
        this.CreateHandle(new CreateParams());
        Application.ApplicationExit += new EventHandler(Application_ApplicationExit);

        RegisterCombo(1001, 1, (int)Keys.D1);
        RegisterCombo(1002, 1, (int)Keys.D2);
        RegisterCombo(1003, 1, (int)Keys.Q);
    }

    private void RegisterCombo(Int32 ID, int fsModifiers, int vlc)
    {
        if (RegisterHotKey(this.Handle, ID, fsModifiers, vlc))
        {
            IDs.Add(ID);
        }
    }

    private void Application_ApplicationExit(object sender, EventArgs e)
    {
        this.DestroyHandle();
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_HOTKEY:
                if (HotkeyPressed != null)
                {
                    HotkeyPressed(m.WParam.ToInt32());
                }
                break;

            case WM_DESTROY: // fires when "Application.Exit();" is called
                foreach (int ID in IDs)
                {
                    UnregisterHotKey(this.Handle, ID);
                }
                break;
        }
        base.WndProc(ref m);
    }

}



回答3:


Just create global kbhook like this



来源:https://stackoverflow.com/questions/17133656/global-hotkeys-in-windowless-net-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!