Страницы: 1
RSS
Клавиатурный хук: перехват нажатия клавиш на клавиатуре
 
Доброе утро, форумчане.
Помогите разобраться.

У меня есть макрос - клавиатурный хук. Выполняет он следующую функцию: перехватывает нажатия клавиш на клавиатуре - и запускает более мелкие макросы в соответствии с той клавишей - которая нажата (стрелки влево-вправо-вверх-вниз).
Но в нем есть одно ограничение - он перехватывает клавиатурные нажатия - только в том случае, если экселевская книга с его расположением является активной.

Как заставить макрос - перехватывать нажатия клавиш в любом случае, когда нажата одна из этих клавиш ?
(То есть даже когда, например в ТоталКоммандере на клавиатуре нажимается - клавиша "стрелка вверх" - макрос на экселевском листе - выводил бы сообщение "Up".)
Код
 Option Explicit
  Public Declare Function CallNextHookEx Lib "user32" _
     (ByVal hHook As Long, _
     ByVal nCode As Long, _
     ByVal wParam As Long, _
     ByVal lParam As Long) As Long
 Public Declare Function UnhookWindowsHookEx Lib "user32" _
    (ByVal hHook As Long) As Long
 Public Declare Function SetWindowsHookEx Lib "user32" _
    Alias "SetWindowsHookExA" _
    (ByVal idHook As Long, _
    ByVal lpfn As Long, _
    ByVal hmod As Long, _
    ByVal dwThreadId As Long) As Long
 Public Declare Function GetCurrentThreadId Lib "kernel32" () As Long
 Public Const WH_KEYBOARD = 2
 Public hHook As Long
 Public Hooked As Boolean
 Public Key As String
 Public Function KeyboardProc(ByVal nCode As Long, _
                              ByVal wParam As Long, _
                              ByVal lParam As Long) As Long
 'Debug.Print wParam      раскомментировать чтобы посмотреть что будет при нажатии других клавиш
         If wParam = 37 Or wParam = 39 Or wParam = 38 Or wParam = 40 Or wParam = 32 Then
             Hooked = True
             If wParam = 37 Then Key = "Left"
             If wParam = 39 Then Key = "Right"
             If wParam = 38 Then Key = "Up"
             If wParam = 40 Then Key = "Down"
             If wParam = 32 Then Key = "Space"
         End If
    KeyboardProc = CallNextHookEx(hHook, nCode, wParam, lParam)
 End Function

 Sub Start()
  Dim i As Long

  'Типа
  Do

  Hooked = False
    hHook = SetWindowsHookEx(WH_KEYBOARD, _
                             AddressOf KeyboardProc, _
                             0&, _
                             GetCurrentThreadId)

     'Пока не хукнули (Hooked=true в KeyboardProc)
   While (Not Hooked)
   i = i + 1
   Cells(1, 1) = i
 'Без этого зависает
   DoEvents
   Wend

    Call UnhookWindowsHookEx(hHook)
    MsgBox Key

 'И типа
  Loop
 End Sub
Изменено: ttt480 - 14.03.2017 02:27:06
 
GetCurrentThreadId - насколько помню, получает ID текущего процесса(из которого вызвана), т.е. Excel. И хук будет работать только в том процессе, для которого создан, т.е. в Excel. Делайте выводы. По логике, если в этой строке:
Код
hHook = SetWindowsHookEx(WH_KEYBOARD, _
                             AddressOf KeyboardProc, _
                             0&, _
                             GetCurrentThreadId)

вместо GetCurrentThreadId поставить 0, хук должен работать глобально, на все приложения. Правда, могут возникнуть свои нюансы. А вообще все эти хуки ни разу не для Excel работа, глюков потом не оберетесь.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
The_Prist написал:
вместо GetCurrentThreadId поставить 0, хук должен работать глобально, на все приложения.
Вместо GetCurrentThreadId поставил 0, как вы сказали  - и сообщения перестали запускаться.
То есть - вообще нет реакции на нажатие клавиш - ни в самом экселе, ни в других приложениях.
Изменено: ttt480 - 11.03.2017 10:16:13
 
Откройте поисковик, забейте название своей функций и добавьте "глобальный хук". Забыл я, что третий аргумент не должен равняться нулю, если четвертый 0. Там должна идти ссылка на библиотеку, осуществляющую хук. В этом есть проблема использования в VBA. Есть, конечно, обходные пути, но как писал выше - глюков не оберетесь.
В VBA скорее проще будет использовать RegisterHotKey для отлова исключительно нужных сочетаний, чем глобальный хук пробовать внедрить.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
The_Prist написал:
В VBA скорее проще будет использовать RegisterHotKey для отлова исключительно нужных сочетаний
А RegisterHotKey  - он глобально умеет работать?
Просто смотрю в интернете - везде пишут, что только SetWindowsHookEx для этой цели можно использовать.
Изменено: ttt480 - 11.03.2017 11:58:46
 
Подскажите, что нужно изменить в приведенном вначале примере чтобы он запускался в MS Office 2021 ?
Пример работает (с небольшими доработками) в MS Office 2016, а в MS Office 2021 выдает сообщение
"Compile error:
The code in this project must be updated for use on 64-bit
systems. Please review and update Declare statements and
then mark them with the PtrSafe attribute."
(Возможно проблема связана с версией VBA7.1 или 64 битной системой.)
попробовал добавить в описание всех функций PtrSafe?
но теперь ей не нравится строка "AddressOf KeyboardProc", пишет
"Compile error:Type mismatch "
но я совершенно не понимаю, что с ним не так, как изменить его тип, какой тип задать?
Изменено: hnomus - 12.08.2023 17:54:40
 
Решил свою проблему заменив в объявлениии функции SetWindowsHookEx тип параметра lpfn на LongPtr.
К сожалению в интернете не нашел таких примеров, все примеры которые попадались хранили указатели в переменных типа Long, хотя казалось бы проблема должна быть распространенной, пришлось решать проблему методом научного тыка.
ЗЫ почему-то во время отладки Excel закрывается сам после выполнения команды SetWindowsHookEx.
Изменено: hnomus - 12.08.2023 21:23:44
Страницы: 1
Читают тему
Наверх