Всем привет.
Эпопея с установкой хука на ОС для определенной комбинации клавиш продолжается. :(
Проблема, которая возникла на данный момент, это работа WinApi функции UnregisterHotKey. После завершения приложения, хук остается в системе и окно с тестовым сообщением, продолжает появляться после завершения программы. При этом, я никак не могу выбить хук из системы. Сначала, это было забавным, когда приложения выгруженное из памяти (но видимо ссылка где-то остается), продолжало показывать тестовое сообщение на комбинацию клавиш, которую я задал при регистрации, но сейчас от смеха не осталось и следа.
Был написан следующий класс:
| Код | public class HotKey : IDisposable { public event Action HotKeyPressed;
private static Object myStaticLock = new Object(); private readonly IntPtr _hWnd; private static int _hotkeyCounter = 0xA000; private readonly int _hotkeyIndex; private bool _isDisposed, _isRegistered; private Keys _key; private ModifierKeys _modifierKeys;
public HotKey() { EventDispatchingNativeWindow.Instance.EventHandler += nw_EventHandler; lock (myStaticLock) { _hotkeyIndex = ++_hotkeyCounter; } _hWnd = EventDispatchingNativeWindow.Instance.Handle; }
public void SetHotKey(ModifierKeys modifierKeys, Keys key) { UnregisteredHotkey(); _modifierKeys = modifierKeys; _key = key; RegisterHotKey(); }
private void nw_EventHandler(ref Message m, ref bool handled) { if (handled) return; if (m.Msg == WinApi.WM_HOTKEY && m.WParam.ToInt32() == _hotkeyIndex) { OnHotKeyPressed(); handled = true; } }
public void RegisterHotKey() { if (!_isRegistered && !_isDisposed) { bool success = WinApi.RegisterHotKey(_hWnd, _hotkeyIndex, _modifierKeys, _key); if (!success) { throw new HotkeyAlreadyInUseException(); } _isRegistered = true; } }
public void UnregisteredHotkey() { if (_isRegistered && !_isDisposed) { WinApi.UnregisterHotKey(_hWnd, _hotkeyIndex); _isRegistered = false; } }
public void Dispose() { if (!_isDisposed) { UnregisteredHotkey(); EventDispatchingNativeWindow.Instance.EventHandler -= nw_EventHandler; _isDisposed = true; } }
~HotKey() { Dispose(); }
private void OnHotKeyPressed() { if (HotKeyPressed != null) HotKeyPressed(); } }
public class HotkeyAlreadyInUseException : Exception { }
|
Вспомогательный класс, который необходим для работы HotKey:
| Код | public delegate void WndProcEventHandler(ref Message m, ref bool handled);
public class EventDispatchingNativeWindow : NativeWindow { private static Object myLock = new Object(); private static EventDispatchingNativeWindow _instance;
public static EventDispatchingNativeWindow Instance { get { lock (myLock) { if (_instance == null) _instance = new EventDispatchingNativeWindow(); return _instance; } } }
public event WndProcEventHandler EventHandler;
public EventDispatchingNativeWindow() { CreateHandle(new CreateParams()); }
protected override void WndProc(ref Message m) { bool handled = false; if (EventHandler != null) EventHandler(ref m, ref handled); if (!handled) base.WndProc(ref m); } }
|
| Код | public class HotKeySingleton { private static HotKey _instance; private static object syncLock = new object();
private HotKeySingleton() { }
public static HotKey GetInstance() { if (_instance == null) { lock (syncLock) { if (_instance == null) { _instance = new HotKey(); } } } return _instance; } }
|
Вот как я работаю с этими классами в mvvm классе:
| Код | private HotKey _hotKey;
// Default ctor public MainPageViewModel() { _hotKey = HotKeySingleton.GetInstance(); _hotKey.SetHotKey(ModifierKeys.Control | ModifierKeys.Shift, Keys.X); _hotKey.HotKeyPressed += () => MessageBox.Show("HotKey"); }
|
В классе HotKey я реализовал интерфейс IDisposable.
И при выходе из приложения я вижу, как вызывается метод ~HotKey(), который в свою очередь вызывает Dispose(), который выбивает хук из системы. Т.е. объект удаляется, как нужно и весь код относящийся к удалению хука, так же выполняется. Но почему хук остается в системе, для меня так и осталась загадкой. :(
Как может появляться сообщение "HotKey", когда приложение уже закрыто?
Подскажите, в чем может быть проблема.
Спасибо!
UPD: Добавил функцию получения ошибки в функцию Unregistred:
| Код | public void UnregisteredHotkey() { if (_isRegistered && !_isDisposed) { WinApi.UnregisterHotKey(_hWnd, _hotkeyIndex); var error = Marshal.GetLastWin32Error(); _isRegistered = false; } }
|
Мне вернулся код 1419, который как бы мне говорит, что HotKey не зарегистрирован. (http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx).
Это меня очень удивило.
А когда я заново запускаю приложение, то регистрация возвращает ошибку с кодом 1409,которая сообщает, что HotKey уже зарегистрирован. :(
UPD2: Я не знаю от чего это зависит, но эта проблема уходит если вырубить VS! Т.е. если VS закрыто, а я запускаю приложение через exe файл, то все работает нормально. И при закрытии приложения, никаких тестовых окон не всплывает.
Осталось понять почему так.
Ну и почему при UnRegistered возвращается код 1419 не понятно!
UPD3: И все же какая-то проблема с самим классом HotKey. У этого класса метод Unregistered публичный. Я добавил кнопки на форму (start и stop) и вызывал их через команды:
| Код | private void StopExecuteCommand() { _hotKey.UnregisteredHotkey(); }
private void StartExecuteCommand() { _hotKey.RegisterHotKey(); }
|
Когда вызывается метод UnregisteredHotkey, код ошибки = 0!!!! :( Но как только закрываю приложения, этот же код (UnregisteredHotkey) возвращает ошибку 1419! :( |