Конференция "WinAPI" » Корректно ли так устанавливать глобальный хук на клавиатуру? [D6, WinXP]
 
  • leonidus (30.07.08 13:19) [0]
    Господа, подскажите корректно ли я ставлю глобальный хук, а то некоторые пользователи жалуются, что вроде из-за моей программы у них подвисает система и хочется понять это причина именно в моей проге или нет.

    Глобальный хук на клаву на клавишу Ctrl ставлю в dll:

    library key_mouse_hook;

    uses
    SysUtils, Classes, Windows, Messages;

    const
    wm_LCtrl_Down=wm_User+1;
    wm_RCtrl_Down=wm_User+2;

    var
     Key_SysHook:HHook = 0;

    {$R *.res}

    function Key_SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
    var H: HWND;
    begin
    if (Code >= 0) and (lParam and $40000000 = 0) then
     begin
      H := FindWindow('TForm1', 'MainWindow');

      if HiWord(GetAsyncKeyState(VK_LCONTROL))<>0 then SendMessage(H, wm_LCtrl_Down, 0, 0);
      if HiWord(GetAsyncKeyState(VK_RCONTROL))<>0 then SendMessage(H, wm_RCtrl_Down, 0, 0);
      end;
     Result:= CallNextHookEx(Key_SysHook, Code, wParam, lParam);
    end;

    { Процедура установки HOOK-а}
    procedure hook(switch : Boolean) export; stdcall;
    begin
     if switch=true then
       //Устанавливаю HOOK, если он не установлен (switch=true).
     begin
       Key_SysHook := SetWindowsHookEx(WH_Keyboard, @Key_SysMsgProc, HInstance, 0);
       if Key_SysHook <> 0 then //MessageBox(0, 'Keyboard hook is working', '', 0)
         else MessageBox(0, 'Keyboard hook is not working', '', 0);

     end
      else
       begin
        //Удаляю функцию-фильтр, если она установлена (т.е. switch=false).
        if UnhookWindowsHookEx(Key_SysHook) then //MessageBox(0, 'Keyboard hook is unload', '', 0)
         else MessageBox(0, 'Keyboard hook is not unload', '', 0);

        Key_SysHook := 0;
       end;
    end;

    exports hook;

    begin
    end.

    А уже в самой программу устанавливаю хук так:

    @hook:= nil; // инициализируем переменную hook
    // ********* динамическая загрузка **************
     Hdll:= LoadLibrary(PChar('key_mouse_hook.dll'));  //загрузка DLL
     if Hdll > HINSTANCE_ERROR then            // если всё без ошибок, то
       begin
         @hook:=GetProcAddress(Hdll, 'hook');    // получаем указатель на необходимую процедуру
         hook(true);
       end
     else
       ShowMessage('Error key_mouse_hook.dll loading!');

    Может я что-то делаю не так?
  • brother © (30.07.08 13:22) [1]
    > что вроде из-за моей программы у них подвисает система

    имхо не из-за нее...
  • Eraser © (30.07.08 15:03) [2]
    > Может я что-то делаю не так?


    > SendMessage

    тут может быть задержка, лучше асинхронно высылать.
  • leonidus (30.07.08 17:22) [3]
    "тут может быть задержка, лучше асинхронно высылать."
    а это как?
  • Eraser © (30.07.08 17:52) [4]
    PostMessage
  • Leonid Troyanovsky © (31.07.08 08:01) [5]

    > leonidus   (30.07.08 13:19)  

    > Может я что-то делаю не так?

    При каждом нажатии  H := FindWindow('TForm1', 'MainWindow');

    Терпеливые юзеры, я б убил.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (31.07.08 09:48) [6]

    > leonidus   (30.07.08 13:19)  

    > function Key_SysMsgProc(code : integer; wParam : word; lParam
    > : longint) : longint; stdcall;

    wParam: WPARAM;

    > Result:= CallNextHookEx(Key_SysHook, Code, wParam, lParam);

    Здесь Key_SysHook всегда будет 0. Т.е., в 9х это работать не будет.

    И, во-ще, для ловли пары клавиш проще WH_KEYBOARD_LL hook.

    --
    Regards, LVT.
  • leonidus (31.07.08 22:52) [7]
    Leonid Troyanovsky, значит я CallNextHookEx неправильно вызываю?

    Мне тут посоветовали переписать функцию таким вот образом:

    function Key_SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
    var
    H: HWND;
    Res: DWORD;
    begin
    Result:= CallNextHookEx(Key_SysHook, Code, wParam, lParam);

    if (Code >= 0) and (lParam and $40000000 = 0) then
    begin
     H := FindWindow('TForm1', 'MainWindow');

     if HiWord(GetAsyncKeyState(VK_LCONTROL))<>0 then
       SendMessageTimeOut(H, wm_Ctrl_Down, 0, 0,SMTO_BLOCK, 5000,Res);

     if HiWord(GetAsyncKeyState(VK_RCONTROL))<>0 then
       SendMessageTimeOut(H, wm_Shift_Down, 0, 0,SMTO_BLOCK, 5000,Res);
     end;

    end;

    Т.е. CallNextHookEx поставить в самое начало, и использовать SendMessageTimeOut вместо SendMessage.

    А в чем проблема с  H := FindWindow('TForm1', 'MainWindow'); ?

    И на счет того что только пара клавиш, на самом деле нужно по крайней мере 4 пары, это я пока просто хочу принцип понять.
  • Leonid Troyanovsky © (01.08.08 00:20) [8]

    > leonidus   (31.07.08 22:52) [7]

    > Мне тут посоветовали переписать функцию таким вот образом:

    А какая разница, в хуковой процедуре Key_SysHook = 0,
    можешь проверить отладчиком.

    > А в чем проблема с  H := FindWindow('TForm1', 'MainWindow');

    Предположим, что в системе 100 окон, а юзер набирает
    текст со скоростью 120 символов/мин. Вот и тормоза.

    Нужно что-то типа
    if H = 0 then H := FindWindow('TForm1', 'MainWindow');

    Просто не надо после активации хука допускать RecreateWnd
    для MainWindow.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (01.08.08 00:25) [9]

    > leonidus   (31.07.08 22:52) [7]

    > SendMessageTimeOut вместо SendMessage.

    Не нужен здесь никакой Send.
    Зацепленный поток получает нажатия через Post,
    через Post же их и передавать дальше.

    --
    Regards, LVT.
  • leonidus (01.08.08 01:48) [10]
    Начинаю понимать... а что значит "Просто не надо после активации хука допускать RecreateWnd для MainWindow." что это за функция такая RecreateWnd  ? Или вы имеете ввиду что один раз получили хэндл H и больше окно не ищем ? Если так то как избежать этого самого RecreateWnd  который я так понимаю вызовет смену хендла? На самом деле, факт нажатия передается главному окну приложения, оно креэйтится только один раз при старте программы.
  • Slym © (01.08.08 08:54) [11]
    а ниче что dll твоя мапится в чужие процессы? а тама Key_SysHook:HHook - не определен...
    читай http://www.delphimaster.ru/articles/hooks/index.html
    и сохраняй Key_SysHook в "шареной" памяти (например именованый FileMapping)
  • Slym © (01.08.08 08:56) [12]
    leonidus   (30.07.08 13:19)
    H := FindWindow('TForm1', 'MainWindow');
     if HiWord(GetAsyncKeyState(VK_LCONTROL))<>0 then SendMessage(H, wm_LCtrl_Down, 0, 0);
     if HiWord(GetAsyncKeyState(VK_RCONTROL))<>0 then SendMessage(H, wm_RCtrl_Down, 0, 0);
     end;

    мессаги шлеш только когда надо... а адресата ищеш всегда... зачем?
  • Slym © (01.08.08 08:59) [13]
    leonidus   (30.07.08 13:19)
    function Key_SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;

    Слава богу не огреб :) т.к. за таких как ты система думает и выравнивает твой 2байтовый word на границу 4байт
  • Slym © (01.08.08 09:02) [14]
    leonidus   (30.07.08 13:19)
    H := FindWindow('TForm1', 'MainWindow');

    можно не искать... а передать хендл при установке хука и сохнарить его рядом с Key_SysHook в шареной памяти для дальнейшего пользования... и конечно асинхронно PostMessage
  • Leonid Troyanovsky © (01.08.08 10:22) [15]

    > leonidus   (01.08.08 01:48) [10]

    > хука допускать RecreateWnd для MainWindow." что это за функция
    > такая RecreateWnd  ?

    Этот метод пересоздает окно формы, например, после изменения стиля
    бордюра, иконок бордюра и т.д.

    > хэндл H и больше окно не ищем ? Если так то как избежать
    > этого самого RecreateWnd  который я так понимаю вызовет

    Посмотри в Forms.pas, какие действия вызовут RecreateWnd
    и избегай их при работающем хуке.
    Либо override CreateWnd формы, повторно передавая в хуковую
    процедуру новый хендл.

    Передать ей значение можно через keybd_event (or SendInput)
    условленного нажатия, скажем, keybd_event(0, 0, 0, dwExtraInfo)
    в последнем параметре.

    И еще раз, low level hook проще - не нужно ничего передавать,
    не нужна длл и т.д.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (01.08.08 10:27) [16]

    > Slym ©   (01.08.08 09:02) [14]

    > можно не искать... а передать хендл при установке хука и
    > сохнарить его рядом с Key_SysHook в шареной памяти

    Если 9х уже не нужны, то нет нужды и в Key_SysHook.

    > Slym ©   (01.08.08 08:54) [11]
    > Slym ©   (01.08.08 08:56) [13]

    Ливия уже предупреждена.

    --
    Regards, LVT.
  • Городской Шаман (09.08.08 06:13) [17]
    Лучше здесь
     if HiWord(GetAsyncKeyState(VK_LCONTROL))<>0 then SendMessage(H, wm_LCtrl_Down, 0, 0);
     if HiWord(GetAsyncKeyState(VK_RCONTROL))<>0 then SendMessage(H, wm_RCtrl_Down, 0, 0);

    SendMessage замени на PostMessage точно не будет зацикливания ни в одном случае.
  • Городской Шаман (09.08.08 06:15) [18]

    > Slym ©   (01.08.08 08:56) [12]
    >
    > leonidus   (30.07.08 13:19)
    > H := FindWindow('TForm1', 'MainWindow');
    >  if HiWord(GetAsyncKeyState(VK_LCONTROL))<>0 then SendMessage(H,
    >  wm_LCtrl_Down, 0, 0);
    >  if HiWord(GetAsyncKeyState(VK_RCONTROL))<>0 then SendMessage(H,
    >  wm_RCtrl_Down, 0, 0);
    >  end;
    >
    > мессаги шлеш только когда надо... а адресата ищеш всегда.
    > .. зачем?


    Под той же Vista64 бит часто менялся хендл окна приложения сам по себе (под некоторыми сборками). Причину определить не удалось. Приходилось использовать поиск окна перед передачей сообщения.
  • brother © (12.08.08 04:33) [19]
    > Под той же Vista64 бит часто менялся хендл окна приложения
    > сам по себе (под некоторыми сборками). Причину определить
    > не удалось

    шойто мене это смущает, на каком основании винда вдруг стала менять хэндл работающего приложения? нонсенс?
  • Городской Шаман (12.08.08 15:45) [20]

    > brother ©   (12.08.08 04:33) [19]
    >
    > > Под той же Vista64 бит часто менялся хендл окна приложения
    > > сам по себе (под некоторыми сборками). Причину определить
    > > не удалось
    >
    > шойто мене это смущает, на каком основании винда вдруг стала
    > менять хэндл работающего приложения? нонсенс?


    Это меня тоже смущало, так как баг наблюдался только у одного или двух пользователей.
  • Leonid Troyanovsky © (12.08.08 20:28) [21]

    > Городской Шаман   (12.08.08 15:45) [20]

    > Это меня тоже смущало, так как баг наблюдался только у одного
    > или двух пользователей.

    Ищи где вызывается RecreateWnd.

    --
    Regards, LVT.
  • Городской Шаман (12.08.08 23:38) [22]

    > Leonid Troyanovsky ©   (12.08.08 20:28) [21]
    >
    >
    > > Городской Шаман   (12.08.08 15:45) [20]
    >
    > > Это меня тоже смущало, так как баг наблюдался только у
    > одного
    > > или двух пользователей.
    >
    > Ищи где вызывается RecreateWnd.
    >
    > --
    > Regards, LVT.


    Было лениво искать. Так как в том унаследованном коде не один скелет в шкафу был зарыт. Решил подобным способом - работает.
 
Конференция "WinAPI" » Корректно ли так устанавливать глобальный хук на клавиатуру? [D6, WinXP]
Есть новые Нет новых   [134435   +33][b:0][p:0.001]