Keyboard hook - ошибка с памятью
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.94] |
|
|
MSDN Library
FAQ раздела
Поиск по разделу
Как правильно задавать вопросы
Keyboard hook - ошибка с памятью
|
Сообщ.
#1
,
|
|
|
|
Для определения времени печатания пришлось ставить хук на клавиатуру. Взял готовую библиотеку из примера клавиатурного шпиона, немного подправил. Все вроде заработало: клавиши ловятся, передаются в приложение, время считается. Однако после запуска программы и установки хука почти все приложения в системе от фотошопа до блокнота стали периодически (раз в несколько минут или чаще) выдавать ошибки нарушения доступа к памяти с сообщением "Ошибка по адресу траляля. Память не может быть READ" При отключении ловушки (просто снес dll-ку из папки) ошибки прекратились. Проверено на двух компах. Я сделал вывод, что библиотека где-то делает грубую ошибку при работе с памятью, но сам не докумекал.
Плиз, собственно, хелп. Код приложения ![]() ![]() procedure TForm1.WndProc(var Msg: TMessage); var CharCode:char; begin inherited ; if Msg.Msg = WM_MYKEYHOOK then begin if Msg.wParam<>0 then LastKeyPressedTime:=GetTickCount; end; end; procedure TForm1.StartLogKey(); var Hook: procedure (switch : Boolean; hMainProg: HWND) stdcall; begin @hook:= nil; HookDLLHandle:= LoadLibrary(PChar(installpath+'keyproc.dll')); if HookDLLHandle > HINSTANCE_ERROR then begin @hook:=GetProcAddress(HookDLLHandle, 'hook'); hook(true, form1.Handle); end; end; procedure TForm1.StopLogKey(); var Hook: procedure (switch : Boolean; hMainProg: HWND) stdcall; begin @hook:= nil; if HookDLLHandle > HINSTANCE_ERROR then begin @hook:=GetProcAddress(HookDLLHandle, 'hook'); hook(false, Form1.Handle); if FreeLibrary(HookDLLHandle) then begin log(0,'Keyproc close ok'); end else begin log(0,'Error close keyproc'); end; end; end; initialization WM_MYKEYHOOK:= RegisterWindowMessage('WM_MYKEYHOOK'); Код ловушки ![]() ![]() ------------------------------------------------------------------- library keyproc; uses SysUtils, Windows, Messages; const MMFName: PChar = 'KeyMMF'; type PGlobalDLLData = ^TGlobalDLLData; TGlobalDLLData = packed record SysHook: HWND; MyAppWnd: HWND; end; var GlobalData: PGlobalDLLData; MMFHandle: THandle; WM_MYKEYHOOK: Cardinal; a:integer; function KeyboardProc(code : integer; wParam : word; lParam : longint) : longint; stdcall; var AppWnd: HWND; // дескриптор приложения, в котором произошло нажатие клавишы begin if code < 0 then begin Result:= CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam); Exit; end; if (Code = HC_ACTION) and (((lParam shr 16) and KF_UP) = 0) then SendMessage(GlobalData^.MyAppWnd, WM_MYKEYHOOK, wParam, AppWnd); CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam); Result:= 0; end; {Процедура установки HOOK-а} procedure hook(switch : Boolean; hMainProg: HWND) export; stdcall; begin if switch=true then begin {Устанавливаю HOOK, если он не установлен (switch=true). } GlobalData^.SysHook := SetWindowsHookEx(WH_keyboard, @KeyboardProc, HInstance, 0); GlobalData^.MyAppWnd:= hMainProg; if GlobalData^.SysHook <> 0 then a:=1 //MessageBox(0, 'KEYBOARD HOOK установлен !', 'Message from keyhook.dll', 0) else a:=0; // MessageBox(0, 'HOOK установить не удалось !', 'Message from keyhook.dll', 0); end else begin {Удаляю функцию-фильтр, если она установлена (т.е. switch=false). } if UnhookWindowsHookEx(GlobalData^.SysHook) then a:=1 // MessageBox(0, 'HOOK снят !', 'Message from keyhook.dll', 0) else a:=1; // MessageBox(0, 'HOOK снять не удалось !', 'Message from keyhook.dll', 0); end; end; procedure OpenGlobalData(); begin {регестрируем свой тип сообщения в системе} WM_MYKEYHOOK:= RegisterWindowMessage('WM_MYKEYHOOK'); {получаем объект файлового отображения} MMFHandle:= CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TGlobalDLLData), MMFName); if MMFHandle = 0 then begin //MessageBox(0, 'Can''t create FileMapping', 'Message from keyhook.dll', 0); Exit; end; {отображаем глобальные данные на АП вызывающего процесса и получаем указатель на начало выделенного пространства} GlobalData:= MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TGlobalDLLData)); if GlobalData = nil then begin CloseHandle(MMFHandle); MessageBox(0, 'Can''t make MapViewOfFile', 'Message from keyhook.dll', 0); Exit; end; end; procedure CloseGlobalData(); begin UnmapViewOfFile(GlobalData); CloseHandle(MMFHandle); end; procedure DLLEntryPoint(dwReason: DWord); stdcall; begin case dwReason of DLL_PROCESS_ATTACH: OpenGlobalData; DLL_PROCESS_DETACH: CloseGlobalData; end; end; exports hook; begin //MessageBox(0, PChar(Application.ExeName), 'Message from keyhook.dll', 0); {назначим поцедуру переменной DLLProc} DLLProc:= @DLLEntryPoint; {вызываем назначенную процедуру для отражения факта присоединения данной библиотеки к процессу} DLLEntryPoint(DLL_PROCESS_ATTACH); end. |
|
Сообщ.
#2
,
|
|
|
|
M Тема перенесена из Delphi -> Delphi: Общие вопросы. |
|
Сообщ.
#3
,
|
|
|
|
в KeyboardProc аргумент wParam должен иметь тип Longint
|
|
Сообщ.
#4
,
|
|
|
|
![]() ![]() function KeyboardProc(code : integer; wParam : word; lParam : longint) : longint; stdcall; var AppWnd: HWND; // дескриптор приложения, в котором произошло нажатие клавишы begin if code < 0 then begin Result:= CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam); Exit; end; if (Code = HC_ACTION) and (((lParam shr 16) and KF_UP) = 0) then SendMessage(GlobalData^.MyAppWnd, WM_MYKEYHOOK, wParam, AppWnd); CallNextHookEx(GlobalData^.SysHook, Code, wParam, lParam); Result:= 0; end; GlobalData и AppWnd здесь могут быть неопределены Поставьте проверку if GlobalData = nil then error if NOT IsWindow(AppWnd) then error |
|
Сообщ.
#5
,
|
|
|
|
Цитата alex31 @ в KeyboardProc аргумент wParam должен иметь тип Longint |
|
Сообщ.
#6
,
|
|
|
|
Цитата Serge @ GlobalData и AppWnd здесь могут быть неопределены Чушь... И каким образом они могут быть не определены? по DLL_PROCESS_ATTACH отрабатывает OpenGlobalData, где и происходит MapViewOfFile, а SetWindowsHookEx происходит в procedure hook, без которой невозможен вызов KeyboardProc а вот тут: ![]() ![]() procedure DLLEntryPoint(dwReason: DWord); stdcall; явный перебор с stdcall... Собственно здесь у тебя и происходит вылет №1 при попытке выгрузки хука... Смотри реализацию здесь: Перехват API функций и вот здесь: Перехват recv Ну и естественно классический CALLBACK KeyboardProc выглядит вот так: ![]() ![]() function KeyboardProc(Code: Integer; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall; Где ты спокойно ловишь вылет №2... Ну и в заключение, а нафига спрашивается козе баян в виде неотлаженных библиотек, когда можно спокойно поставить LowLevelHook при помощи константы WH_KEYBOARD_LL = 13 и ловить все сообщения через application-defined callback function, которая аналогичны вышеописаной? |
|
Сообщ.
#7
,
|
|
|
|
Цитата Rouse_ @ Цитата Serge @ GlobalData и AppWnd здесь могут быть неопределены Чушь... И каким образом они могут быть не определены? AppWnd будет не определена... это локальная переменная функции KeyboardProc я не вижу где эта переменная там устанавливается |
|
Сообщ.
#8
,
|
|
|
|
alex31, точно, пропустил я это дело с AppWnd...
Да вот только тут мало того что она не определена, а вообще непонятно какое значение будет иметь ибо пустая переменная, поэтому ее можно откидывать, роли она никакой не сыграет... |
|
Сообщ.
#9
,
|
|
|
|
Цитата Rouse_ @ alex31, точно, пропустил я это дело с AppWnd... Да вот только тут мало того что она не определена, а вообще непонятно какое значение будет иметь ибо пустая переменная, поэтому ее можно откидывать, роли она никакой не сыграет... Всего скорее,судя по комментарию, автор забыл поставить AppWnd:=GetForegroundWindow; |
|
Сообщ.
#10
,
|
|
|
|
Да, автор не забыл, там это и было - ведь шпион должен не только логить клавиши, но и записывать в каком приложении они были нажаты. Я это убрал, потому что не шпиона делал, а от переменной забыл избавится.[b]
В общем я принял непростое, но единственно верное решение - передрать внаглую все у _Rouse и не думать из расчета что много будешь знать скоро состаришься Копирайты ессно оставил Все завелось с полоборота За сим всех благодарю и желаю долгих лет жизни, здоровья, счастья и всего самого наилучшего! |
|
Сообщ.
#11
,
|
|
|
|
Цитата мыш @ передрать внаглую все у _Rouse и не думать из расчета что много будешь знать скоро состаришься Спасибо, конечно, за характеристику, но советую всежтаки прочесть Рихтера хотя бы в качестве теории... Если и дальше будешь заниматься системой, то можешь много шишек набить при отладке хуков, а это потраченое впустую время... |
|
Сообщ.
#12
,
|
|
|
|
Rouse_, твой пример почему то у меня ловит только нажатия клавиш в моем приложении, из других сообщения не приходят. Почему так может быть?
|
|
Сообщ.
#13
,
|
|
|
|
Цитата мыш @ Rouse_, твой пример почему то у меня ловит только нажатия клавиш в моем приложении, из других сообщения не приходят. Почему так может быть? Не знаю, у меня все нормально, вот еще раз собрал демку - держи в архиве... Прикреплённый файл KeyboardHook.rar (187.39 Кбайт, скачиваний: 93)
|