Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Key hook, перехват нажатия клавиш


Автор: ReFleX 30.10.2006, 19:05
Добрый день!

Рылся по форуму, но везде из подобных найденных тем в примерах используется библиотека которая отслеживает хук.

Из кучи примеров у меня получилось собрать именно тот который мне нужен:

DLL:
Код

#include <windows.h>
#include <iostream>

#ifdef __cplusplus
extern "C" {
#endif

HHOOK hHKeyBrd = NULL;

LRESULT CALLBACK KeyBrdProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0){
        MessageBox(NULL,"Hook","Information",MB_OK);
    }
    return CallNextHookEx (hHKeyBrd,nCode,wParam,lParam);
}

__declspec(dllexport) bool SetHooks(HINSTANCE hModule)
{
    hHKeyBrd = SetWindowsHookEx(WH_KEYBOARD, KeyBrdProc, (HINSTANCE) hModule, NULL);
    return true; 
}

__declspec(dllexport) bool UnHook()
{
    return true;
}

#ifdef __cplusplus
}
#endif


EXE
Код

#include <windows.h>
#include <iostream>
#include <tchar.h>

HHOOK hH;

typedef bool(*dllProc)(HINSTANCE);

int _tmain(int argc, _TCHAR* argv[])
{    
    HINSTANCE hDll;
    dllProc prAddr;

    char *DllFile = "HookDll.dll";

    HINSTANCE  hModule = GetModuleHandle(DllFile);
    if ((hDll = LoadLibrary(DllFile))!=NULL){
        prAddr = (dllProc)GetProcAddress(hDll,"SetHooks");
    }else{
        printf("Can't load DLL");
        Sleep(3000);
        return 0;
    }
    (prAddr)(hDll);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}


Вопрос остаётся тот же, возможно ли объеденить DLL вместе с EXE?
Просто не очень хочеться тоскать библиотеку, пускай лучше EXE будет немного больше.

Я слышал, что microsoft позволяет работать с Hook как в DLL так и для EXE, но примера я нигде не нашёл, решил обратиться с помощью на любимый форум. smile 

Автор: SergeCpp 30.10.2006, 19:40
Попробуйте вот так

char* DllFile = "HookExe.exe"; // и перенесите все функции в EXE

Автор: ReFleX 30.10.2006, 19:44
Не думаю что это будет правильно... smile 

Автор: anwe 30.10.2006, 20:27
А такой хук не всегда требует нахождения функции-фильтра в отдельной длл-ке. Можно устанавливать и в своем приложении. Только и ловится будут свои сообщения.

Автор: ReFleX 30.10.2006, 20:34
Мне надо чтобы ловились нажатия клавиш вне программы, этого я добился, но так же нужно чтобы библиотеки небыло, чтобы не таскаться с ней.

Автор: anwe 30.10.2006, 21:38
Все системные хуки и хуки для потоков в другом приложении должны находиться в DLL.

Автор: Dude03 31.10.2006, 02:01
Цитата(ReFleX @  30.10.2006,  20:34 Найти цитируемый пост)
Мне надо чтобы ловились нажатия клавиш вне программы, этого я добился, но так же нужно чтобы библиотеки небыло, чтобы не таскаться с ней. 

А в чем проблема?=) Почему нельзя загружать либу из ресурсов приложения.
Пусть тебя не смущает асм-код. Просто посмотри какие надо юзать функции(их имена после invoke):
Код

invoke FindResource, NULL, ADDR mres, RT_RCDATA
test eax, eax
je WMDESTROY
mov [hRes], eax
invoke SizeofResource, [hInstance], eax
mov [SizeRes], eax
invoke LoadResource, NULL, [hRes]
invoke LockResource, eax

Автор: ReFleX 31.10.2006, 02:15
Т.е. если я правильно понял, включить dll в приложение а после уже при открытие EXE работать с включённой библиотекой.


Цитата(Dude03 @  31.10.2006,  02:01 Найти цитируемый пост)
Пусть тебя не смущает асм-код.
тут с WinAPI голову сломаешь, а тут ещё и ассемблер smile  smile  smile 

Автор: Earnest 31.10.2006, 08:02
Эта DLL, содержащаю хук, должна подгружаться во все процессы, в которых будет выполняться код, так что
anwe прав: только локальные хуки можно ставить, используя процедуру, которая находится в EXE.
Цитата(MSDN)

The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in libraries. 

Автор: SergeCpp 31.10.2006, 08:42
Код
BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,
  DWORD fdwReason,
  LPVOID lpvReserved
);

int WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
);


Остаётся лишь распознать вызов LoadLibrary (по третьему параметру) и подкорректировать стек.

Не так велики ухищрения. Сделать можно.

Естественно, Entry Point должна быть своя — не та, что берётся из CRT0.C и CRTDLL.C, например (имена файлов VC).

Автор: ReFleX 31.10.2006, 11:40
Цитата(SergeCpp @  31.10.2006,  08:42 Найти цитируемый пост)
Не так велики ухищрения. Сделать можно

 smile А можешь пример сделать, я просто в WinAPI не совсем хорошо разбираюсь. Так с лёту догнать написанное не могу.

Автор: v2v 6.11.2006, 18:14
Вероятно это уже не актуально, но я хотел бы поделиться своими изыскаиями в области перехвата нажатия кнопки клавиатуры.
Не обязательно создавать длл для перехвата нажатий в текущем окне, но вот если вы хотите перехватывать нажатия из других окон...
единственный выход - это длл.
Если кому то интересно, могу выложитькод, где я перехватываю нажатия выполненные в этом окне

Автор: Yra 30.12.2006, 14:18
v2v,  Выкладывай!!!! Или если не трудно - на мыло мне скинь пожалуйста : [email protected]
 smile 

Автор: jonie 3.1.2007, 01:44
погавариват что есть еще и хуки низкого уровня... и они не требуют типа без длл. Я честно маловерю) хотите - проверяйте) да и на делфях)...
Код

 LT> Установка системных WH_KEYBOARD_LL и WH_MOUSE_LL, т.е. хуков низкого 
 LT> уровня, не требуют библиотеки. 
 DVS> жаль только Delphi о них понятия не имеет. 


const 
  WH_KEYBOARD_LL = 13; 
  WH_MOUSE_LL = 14; 


  LLKHF_EXTENDED = KF_EXTENDED shr 8; 
  LLKHF_INJECTED = $00000010; 
  LLKHF_ALTDOWN = KF_ALTDOWN shr 8; 
  LLKHF_UP = KF_UP shr 8; 


  LLMHF_INJECTED = $00000001; 


{ Structure used by WH_KEYBOARD_LL } 
type 
  tagKBDLLHOOKSTRUCT  = packed record 
    vkCode : DWord; 
    scanCode : DWord; 
    flags : DWord; 
    time : DWord; 
    dwExtraInfo : PDWord; 
  end; 
  TKBDLLHOOKSTRUCT = tagKBDLLHOOKSTRUCT; 
  PKBDLLHOOKSTRUCT = ^TKBDLLHOOKSTRUCT; 


{ Structure used by WH_MOUSE_LL } 
  tagMSLLHOOKSTRUCT = packed record 
    pt : TPoint; 
    mouseData : DWord; 
    flags : DWord; 
    time : DWord; 
    dwExtraInfo : PDWord; 
  end; 
  tMSLLHOOKSTRUCT = tagMSLLHOOKSTRUCT; 
  PMSLLHOOKSTRUCT = ^TMSLLHOOKSTRUCT; 


  Вот, например, что когда-то предлагал Джеф Рихтер для 
  отключения любимых номеров (в вольном переводе): 


function  LowLevelKeyboardProc(nCode :Longint; awParam: WParam; alParam: 
LParam): DWord; stdcall; 
var 
  fEatKeystroke: Boolean; 
  p: PKBDLLHOOKSTRUCT; 
  buf: array [0..255] of Char; 
  sc: UINT; 
begin 
   fEatKeystroke := False; 


   if (nCode = HC_ACTION) then 
      case (awParam) of 
       WM_KEYDOWN, WM_SYSKEYDOWN, 
       WM_KEYUP, WM_SYSKEYUP: 
         begin 
          p := PKBDLLHOOKSTRUCT(alParam); 
          fEatKeystroke := 
            ((p.vkCode = VK_TAB) and (p.flags and LLKHF_ALTDOWN <> 0)) or 
            ((p.vkCode = VK_ESCAPE) and ((p.flags and LLKHF_ALTDOWN) <> 0)) or 
            ((p.vkCode = VK_ESCAPE) and ((GetKeyState(VK_CONTROL) and $8000) 
<>0)); 
         end; 
       end; 


   if fEatKeystroke then 
     Result := 1 
   else 
     Result := CallNextHookEx(0, nCode, awParam, alParam); 
end; 


var 
  hhk: HHOOK; 


procedure TForm1.Button1Click(Sender: TObject); 
begin 
  hhk:= SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, Hinstance, 0); 
end; 


procedure TForm1.Button2Click(Sender: TObject); 
begin 
  UnHookWindowsHookEx(hhk); 
end; 

а вообще можно и поэксперементировать с Dll-Exe-шкой)) SergeCpp писал...

Автор: W4FhLF 3.1.2007, 08:51
Цитата

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


Подмена адресов в SDT чтоли?


Как бы там ни было, вы должны понять, что когда устанавливается хук в чужом адресном пространстве должен находится Ваш модуль. Dll можно легко подгрузить в чужое адресное пространство, там все смещения вычисляются с учётом поправок(reloc), т.е. базовый адрес dll в адресном пространстве процесса может быть динамическим. А вот подгрузить Exe уже не так просто, нужно учесть кучу тонкостей PE формата, мне даже лень перечислять все. А сделать так, чтобы твой exe работал параллельно с основным в одном процессе вообще гиблая идея.

Автор: kapbepucm 28.8.2007, 09:54
Проблема из этой области. Вызываю дочерний процесс. Надо в дочке на все потоки повесить клавиатурый Hook. На не большое время. Таких действий будет несколько (дочек много). Можно ли обойтись без dll? И как в этом случае корректно вызвать SetWindowsHookExA?

Автор: jonie 28.8.2007, 23:21
есть низкоуровневые хуки , они работают без длл-ок (но мне это не нравится лично)..
Цитата

И как в этом случае корректно вызвать SetWindowsHookExA?
?всмысле чтобы "не поставить дважды" чтоли?

P.S. возможно стоит пересмотреть архитектуру приложения и "включать" хук перед вызовом "дочек" и выключать при необходимости и уведомлять их о  сообщениях.. (я прост не вижу смысла дублировать код в "дочках" если их не одна)....

Автор: kapbepucm 29.8.2007, 08:32
Ну, например, есть запущенных несколько дочек, и я хочу увидеть, что было нажато у конкретной дочке. Если использую WH_JOURNALRECORD, то получается в exe'шнике всё реализовать, правда Hook получается поставить на всю систему. Мне бы на конкретный процесс и только на клаву.

Автор: jonie 29.8.2007, 08:37
хм... но ведь это ХУК глобальный, а не отправка уведомлений о нажатии клавиш процесса N родителю. Зачем хук тогда ?
Для конкретной и делай конкретно.... т.е. просто отсылай уведомления головному процессу о нажатии клавиши в дочке.
Зачем хук?

Автор: kapbepucm 29.8.2007, 08:41
У меня получается нестандартная ситуация. Дочка-это уже готовая (не моя) прога. Кроме как использовать Hook не вижу выхода... Может есть лучшие варианты?

Автор: jonie 29.8.2007, 21:32
чТО значит "готовая не моя" ? Нет доступа к исходникам или вам лень разбираться в чужом коде? Выражайтесь яснее....

если есть исходники то:
[Ваш модуль, имеет окно hwndMainProg]
[Дочка : запускается, ей передается HWND окна главной программы (hwndMainProg)  (да хоть через параметры вызова))]
{
оработка сообщений оконных дочкой,
если нажата клавиша, то PostMessage(hwndMainProg, WM_***,wParam,lParam);
где WM_ - нужное сообщение (вплоть до своих вида WM_USER+N, N>0; или зарегистрированных через RegisterWindowMessage(), или через нечто вроде WM_COPYDATA (ну что-то подобное было для переноса данных...))
}

в главном модуле реализуете обработку того что надо (идентификацию от какой дочки сообщение тоже можете сделать как-то дополнительно....)
Можете для связи использовать пайпы или сетевое соединение... вариантов межпроцессорного взаимодействия не один (ябы наверно юзал пайпы (pipes)..)...

Если исходников нет - тогда хук в главном модуле. И смотреть какой окно активно (например) по нему идентифицировать дочку во время обработки в хук-процедуре клавиатурного сообщения....

В общем хотите чтобы подсказали более конкретно описывайте проблему.. мыж не гадалки)

Автор: kapbepucm 30.8.2007, 10:22
Цитата(jonie @  29.8.2007,  21:32 Найти цитируемый пост)
мыж не гадалки
Дочка- *.exe без исходников. Моя ситуация не позволяет создавать DLL (другой язык программирования, не важно какой). В общем, меня заинтересовал параметр dwThreadId. Думал, можно конкретно указать на что Hook вешать (типа, зачем вешать на всё, если есть возможность указать куда).

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)