как создать горячие клавиши без привязки к окну

Я создаю приложение C #, может быть WinForm, но предпочтительно консольное приложение, которое должно захватывать сочетания клавиш, даже когда приложение не находится на переднем плане. Как бы это сделать, я знаю, что это возможно, т.е. Songbird может это сделать.

Эти сочетания клавиш будут иметь форму ctrl+-> У меня пока нет никакого кода, так как я даже не имею ни малейшего представления о том, как зарегистрировать клавиатуру ярлыки глобально.


person Pim Jager    schedule 04.01.2011    source источник


Ответы (4)


Вы должны использовать RegisterHotkey, а не перехват клавиатуры.

Использование глобальной клавиатуры, когда вам просто нужна глобальная горячая клавиша, является злоупотреблением API. Это также отрицательно сказывается на производительности, поскольку каждый ключ должен быть обработан вашей программой до того, как он попадет в целевую программу.

person CodesInChaos    schedule 04.01.2011
comment
Это не будет работать в консольном приложении, которое было предпочтительным стилем приложения. RegisterHotKey сопоставляет горячую клавишу с очередью сообщений определенного окна, но в консольном приложении нет перекачки сообщений. Добавление всего насоса сообщений для обработки этого является более оскорбительным, IMO, чем использование LL Keyboard Hook. - person Reed Copsey; 05.01.2011
comment
Если я правильно помню, хук клавиатуры LL требует, чтобы ваш поток также обрабатывал сообщения. - person CodesInChaos; 05.01.2011
comment
@CodeInChaos: Нет. Он использует обратный вызов напрямую. Дополнительные сведения см. в разделе SetWindowsHookEx — msdn.microsoft.com/en- us/library/ms644990(VS.85).aspx - person Reed Copsey; 05.01.2011
comment
Хуки обрабатываются с помощью обратных вызовов, а не сообщений Windows. - person Reed Copsey; 05.01.2011
comment
MSDN на RegisterHotkey: если этот параметр [параметр hWnd] имеет значение NULL, сообщения WM_HOTKEY отправляются в очередь сообщений вызывающего потока и должны обрабатываться в цикле обработки сообщений. поэтому для этого не требуется HWND, требуется только, чтобы ваш поток обрабатывал сообщения. - person CodesInChaos; 05.01.2011
comment
MSDN на функции обратного вызова LowLevelKeyboardProc: этот хук вызывается в контексте потока, который его установил. Вызов осуществляется путем отправки сообщения потоку, который установил хук. Следовательно, поток, установивший хук, должен иметь цикл обработки сообщений. - person CodesInChaos; 05.01.2011
comment
Спасибо! В итоге я использовал ‹a href=pinvoke.net/default.aspx /› библиотека от pinvoke.net. Что сделало это довольно простым в использовании. - person Pim Jager; 07.01.2011

Вот пример кода, который, я надеюсь, поможет... (удачи!!!)

Применение:

            _hotKey0 = new HotKey(Key.F9, KeyModifier.Shift | KeyModifier.Win, OnHotKeyHandler);

...

        // ******************************************************************
    private void OnHotKeyHandler(HotKey hotKey)
    {
        SystemHelper.SetScreenSaverRunning();
    }

Сорт:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;

namespace UnManaged
{
    public class HotKey : IDisposable
    {
        private static Dictionary<int, HotKey> _dictHotKeyToCalBackProc;

        [DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, UInt32 fsModifiers, UInt32 vlc);

        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        public const int WmHotKey = 0x0312;

        private bool _disposed = false;

        public Key Key { get; private set; }
        public KeyModifier KeyModifiers { get; private set; }
        public Action<HotKey> Action { get; private set; }
        public int Id { get; set; }

        // ******************************************************************
        public HotKey(Key k, KeyModifier keyModifiers, Action<HotKey> action, bool register = true)
        {
            Key = k;
            KeyModifiers = keyModifiers;
            Action = action;
            if (register)
            {
                Register();
            }
        }

        // ******************************************************************
        public bool Register()
        {
            int virtualKeyCode = KeyInterop.VirtualKeyFromKey(Key);
            Id = virtualKeyCode + ((int)KeyModifiers * 0x10000);
            bool result = RegisterHotKey(IntPtr.Zero, Id, (UInt32)KeyModifiers, (UInt32)virtualKeyCode);

            if (_dictHotKeyToCalBackProc == null)
            {
                _dictHotKeyToCalBackProc = new Dictionary<int, HotKey>();
                ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
            }

            _dictHotKeyToCalBackProc.Add(Id, this);

            Debug.Print(result.ToString() + ", " + Id + ", " + virtualKeyCode);
            return result;
        }

        // ******************************************************************
        public void Unregister()
        {
            HotKey hotKey;
            if (_dictHotKeyToCalBackProc.TryGetValue(Id, out hotKey))
            {
                UnregisterHotKey(IntPtr.Zero, Id);
            }
        }

        // ******************************************************************
        private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
        {
            if (!handled)
            {
                if (msg.message == WmHotKey)
                {
                    HotKey hotKey;

                    if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey))
                    {
                        if (hotKey.Action != null)
                        {
                            hotKey.Action.Invoke(hotKey);
                        }
                        handled = true;
                    }
                }
            }
        }

        // ******************************************************************
        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // ******************************************************************
        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be _disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be _disposed.
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this._disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    Unregister();
                }

                // Note disposing has been done.
                _disposed = true;
            }
        }
    }

    // ******************************************************************
    [Flags]
    public enum KeyModifier
    {
        None = 0x0000,
        Alt = 0x0001,
        Ctrl = 0x0002,
        NoRepeat = 0x4000,
        Shift = 0x0004,
        Win = 0x0008
    }

    // ******************************************************************
}
person Eric Ouellet    schedule 17.02.2012

Одним из возможных подходов может быть использование Windows Hooks. Но для этого вам нужно создать собственную DLL и написать в ней свою функцию обратного вызова для установки в качестве ловушки.

Найдите SetWindowsHookEx отправную точку. В Интернете также есть множество примеров того, как его использовать.

person Ran    schedule 04.01.2011
comment
Это можно сделать с помощью P/Invoke без использования родной DLL. См. Статью, на которую я ссылаюсь, для примера кода... - person Reed Copsey; 05.01.2011
comment
Вам не нужна собственная dll, если вы используете LowLevelKeyboardHook, так как в этом случае не требуется внедрение в другие процессы. Ловушка низкого уровня обрабатывается в процессе, в котором вы устанавливаете хук, а не в процессе, получающем ввод. Собственная dll необходима для некоторых других хуков, в частности хука клавиатуры не низкого уровня. - person CodesInChaos; 05.01.2011
comment
@CodeInChaos: Интересно... спасибо за исправление. Я думал, что все глобальные хуки должны быть в DLL. - person Ran; 05.01.2011

Это можно сделать с помощью низкоуровневого клавиатурного хука с помощью хука Windows, перехватывающего WH_KEYBOARD_LL.

Вот статья CodeProject о настройке Global Hook в C#, которая демонстрирует весь обработать.

person Reed Copsey    schedule 04.01.2011
comment
Потому что это неправильный API для этой цели, и я не хочу, чтобы больше программ делали это таким образом. - person CodesInChaos; 05.01.2011
comment
@CodeInChaos: у вас нет дескриптора окна в консольном приложении. RegisterHotKey будет хорошо работать для приложения Windows Forms, но не для любого приложения общего назначения. Требуется целевой HWND для получения сообщений уведомлений в своей очереди сообщений. - person Reed Copsey; 05.01.2011
comment
Низкоуровневый хук клавиатуры имеет точно такую ​​же проблему. Клавиатурный хук не низкого уровня не страдает от этой проблемы, но у него гораздо больше проблем, так что это еще хуже. - person CodesInChaos; 05.01.2011