Имеется DLL, в которой имеется хуковая процедура,
которая отлавливает мышиные сообщения. Процедура
нормально регистрируется с помощью вызова
hHook = SetWindowsHookEx (WH_MOUSE, HookProc, hInstDLL, 0)
и нормально разрегистрируется с помощью вызова
UnhookWindowsHookEx (hHook). И все это нормально
работало, но возникла потребность отсчитать заданное
время от последнего мышиного события. Соответственно,
в HookProc был добавлен в числе прочего такой вызов:
SetTimer (NULL, 0, TIMEOUT, TimerProc)
и вроде-бы тоже работает, НО при попытке
снять хук вызовом UnhookWindowsHookEx имеем
страшное сообщение о том, что программа выполнила
некорректную операцию и про сбой при обращении к
странице памяти. В чем может быть дело? Если нет
мыслей по поводу возможных причин — хоть киньте пример
кода, если приходилось делать вызов таймера в
хуковой процедуре в DLL.
Помогите люди добрые, сами мы не местные... :(
Спасибо.
Re: запуск таймера из хуковой процедуры
От:
Аноним
Дата:
17.03.02 16:44
Оценка:
T>в HookProc был добавлен в числе прочего такой вызов: T>SetTimer (NULL, 0, TIMEOUT, TimerProc)
Забыл написать: проверено — проблема именно в этом
вызове. Если его убрать — хук опять начинает нормально
сниматься.
Здравствуйте Tagus, Вы писали:
T>Здравствуйте уважаемый All.
T>Это снова я и снова со странным вопросом .
T>Имеется DLL, в которой имеется хуковая процедура, T>которая отлавливает мышиные сообщения. Процедура T>нормально регистрируется с помощью вызова T>hHook = SetWindowsHookEx (WH_MOUSE, HookProc, hInstDLL, 0) T>и нормально разрегистрируется с помощью вызова T>UnhookWindowsHookEx (hHook). И все это нормально T>работало, но возникла потребность отсчитать заданное T>время от последнего мышиного события. Соответственно, T>в HookProc был добавлен в числе прочего такой вызов: T>SetTimer (NULL, 0, TIMEOUT, TimerProc) T>и вроде-бы тоже работает, НО при попытке T>снять хук вызовом UnhookWindowsHookEx имеем T>страшное сообщение о том, что программа выполнила T>некорректную операцию и про сбой при обращении к T>странице памяти. В чем может быть дело? Если нет T>мыслей по поводу возможных причин — хоть киньте пример T>кода, если приходилось делать вызов таймера в T>хуковой процедуре в DLL.
T>Помогите люди добрые, сами мы не местные...
T>Спасибо.
Если процедура, в которую приходит WM_TIMER в этой же ДЛЛ, то поробуй перед вызовом UnhookWindowsHookEx вызвать KillTimer
Do not fake yourself ;) ICQ#: 198114726
Re[2]: запуск таймера из хуковой процедуры
От:
Аноним
Дата:
17.03.02 19:01
Оценка:
Здравствуйте Dr_Sh0ck, Вы писали:
DS>Если процедура, в которую приходит WM_TIMER в этой же ДЛЛ, то поробуй перед вызовом UnhookWindowsHookEx вызвать KillTimer
Таймерная процедура в той-же DLL. Ввызов KillTimer
не помогает.
Здравствуйте Tagus, Вы писали:
T>Здравствуйте уважаемый All.
T>Это снова я и снова со странным вопросом .
T>Имеется DLL, в которой имеется хуковая процедура, T>которая отлавливает мышиные сообщения. Процедура T>нормально регистрируется с помощью вызова T>hHook = SetWindowsHookEx (WH_MOUSE, HookProc, hInstDLL, 0) T>и нормально разрегистрируется с помощью вызова T>UnhookWindowsHookEx (hHook). И все это нормально T>работало, но возникла потребность отсчитать заданное T>время от последнего мышиного события. Соответственно, T>в HookProc был добавлен в числе прочего такой вызов: T>SetTimer (NULL, 0, TIMEOUT, TimerProc) T>и вроде-бы тоже работает, НО при попытке T>снять хук вызовом UnhookWindowsHookEx имеем T>страшное сообщение о том, что программа выполнила T>некорректную операцию и про сбой при обращении к T>странице памяти. В чем может быть дело? Если нет T>мыслей по поводу возможных причин — хоть киньте пример T>кода, если приходилось делать вызов таймера в T>хуковой процедуре в DLL.
T>Помогите люди добрые, сами мы не местные...
T>Спасибо.
Я думаю, проблема в том, что происходит попытка вызвать TimerProc, когда DLL уже выгружена. Почему не помогает KillTimer не знаю. Можно поместить TimerProc в другую dll, загружать её при загрузке в новый процесс, а потом не выгружать.
Таймер к моменту снятия хука должен уже давно завершиться,
т.к. хук снимается по команде из меню основной программы.
Соответсвенно, почему KillTimer в данном случае не помогает
вполне понятно (по крайней мере кажется, что понятно ;) ).
Здравствуйте SergH, Вы писали:
SH>Я думаю, проблема в том, что происходит попытка вызвать TimerProc, когда DLL уже выгружена. Почему не помогает KillTimer не знаю. Можно поместить TimerProc в другую dll, загружать её при загрузке в новый процесс, а потом не выгружать.
Вряд-ли. DLL не выгружается вызовом UnhookWindowsHookEx.
Для выгрузки DLL нужно вызвать FreeLibrary, чего не делается.
Кроме того, в случае глобального хука, который имеет место
быть, DLL не выгрузится даже после FreeLibrary, поскольку
глобальный хук привязывается ко всем запущенным в системе
процессам, соответственно, DLL должна висеть в памяти
пока все процессы не завершатся.
Здравствуйте Tagus, Вы писали:
T>Здравствуйте SergH, Вы писали:
SH>>Я думаю, проблема в том, что происходит попытка вызвать TimerProc, когда DLL уже выгружена. Почему не помогает KillTimer не знаю. Можно поместить TimerProc в другую dll, загружать её при загрузке в новый процесс, а потом не выгружать.
T>Вряд-ли. DLL не выгружается вызовом UnhookWindowsHookEx. T>Для выгрузки DLL нужно вызвать FreeLibrary, чего не делается. T>Кроме того, в случае глобального хука, который имеет место T>быть, DLL не выгрузится даже после FreeLibrary, поскольку T>глобальный хук привязывается ко всем запущенным в системе T>процессам, соответственно, DLL должна висеть в памяти T>пока все процессы не завершатся.
Дело в том, что ты ведь явно ДЛЛ не загружаешь by LoadLibrary. Как только ты ставишь хук (в частности на события от мыши), и когда такое событие происходит, система проверяет, не установлен ли хук. Если установлен, то далее система проверяет, находится ли ДЛЛ с нужной хуковой процедурой в памяти — если да, то она просто отображается на адресное пространство соответствующего процесса, если нет, то ДЛЛ в память загружается и затем отображается. Затем происходит вызов хуковой процедуры. Когда система определит, что хуков нет (UnhookWindowsHookEx), то никакого смысла держать в памяти ДЛЛ с хуковой процедурой нет и она (ДЛЛ) будет выгружена.
T>Код DLL (все лишнее для простоты выкинуто, вызов таймера T>сделан только 1 раз, в таком виде компилировалось, — точно T>так-же не работает):
[skipped]
T>Таймер к моменту снятия хука должен уже давно завершиться, T>т.к. хук снимается по команде из меню основной программы. T>Соответсвенно, почему KillTimer в данном случае не помогает T>вполне понятно (по крайней мере кажется, что понятно ).
Тут еще вот здесь может быть косяк:
The KillTimer function does not remove WM_TIMER messages already posted to the message queue.
Здравствуйте Dr_Sh0ck, Вы писали:
DS> Дело в том, что ты ведь явно ДЛЛ не загружаешь by LoadLibrary. Как только ты ставишь хук (в частности на события от мыши), и когда такое событие происходит, система проверяет, не установлен ли хук. Если установлен, то далее система проверяет, находится ли ДЛЛ с нужной хуковой процедурой в памяти — если да, то она просто отображается на адресное пространство соответствующего процесса, если нет, то ДЛЛ в память загружается и затем отображается. Затем происходит вызов хуковой процедуры. Когда система определит, что хуков нет (UnhookWindowsHookEx), то никакого смысла держать в памяти ДЛЛ с хуковой процедурой нет и она (ДЛЛ) будет выгружена.
Сорри, просмотрел — ты ее действительно LoadLibrary загружаешь...
Подумал я над свои последним сообщением и понял — наврал позорно. Под "выгрузкой" ДЛЛ я понимал отмену ее проекции.
Ну да ладно. Подумал я еще и вот что мне в голову пришло:
Проблема в том, что ты делаешь SetTimer, когда вызывается твоя хуковая процедура. Но ведь она вызывается в контексте совершенно неизвестного процесса. Следовательно адрес процедуры таймера — это адрес в виртуальном адресном пространстве ентого процесса. Пока существует проекция ДЛЛ с хукой процедурой все нормально — идут себе WM_TIMER'ы.
А вот KillTimer ты вызываешь в контексте _своего_ процесса и передаешь ему идентификатор таймера, который фактически создан в другом процессе. Естессно Таймер не убивается (такого идентификатора в твоем процесса просто _нет_). Затем, после вызова UnhookWindowsHookEx, хук убивается => проекция отменяется => после прихода очередного WM_TIMER'а происходит переход по адресу где _раньше_ _была_ таймерная процедура (из ДЛЛ). А там уже ею и не пахнет...
Тебе надо переделать механизм установки таймера так, чтобы он устанавливался в контексте твоего процесса.
И еще. По-моему у тебя еще косячок здесь
Дело в том, что твой проецирование ДЛЛ с хуковой процедурой на адресное пространство процессов будет происходить много раз (по разу для каждого нужного процесса). И каждый раз будет происходить инициализация TimerRunning = FALSE => и условие будет выполнятся неоднократно => и таймеров будет установлена куча (хотя там не одного быть не должно).
Так что по-моему лучше уэту переменную поместить в общую секцию.
P.S. Если кто-уверен, что я опять наврал, то дико извиняюсь. Тогда уж и мне заодно объясните в чем тут дело .
Здравствуйте Tagus, Вы писали:
T>Код DLL (все лишнее для простоты выкинуто, вызов таймера T>сделан только 1 раз, в таком виде компилировалось, — точно T>так-же не работает):
T>HHOOK hHook;
T>UINT TimerID;
T>BOOL TimerRunning = FALSE;
T>LRESULT CALLBACK MouseHook (int nCode, WPARAM wParam, LPARAM lParam)
T>{
T> switch (wParam)
T> {
T> case WM_MOUSEMOVE:
T> if (TimerRunning == FALSE)
T> {
T> TimerRunning = TRUE;
T> // если этот вызов таймера убрать - все ОК
T> TimerID = SetTimer (NULL, 0, 100, TimerProc);
T> }
T> }
T>
T> return CallNextHookEx(hHook, nCode, wParam, lParam);
T>}
[ccode]
MouseHook вызывается в контексте того потока, в который пришло мышиное сообщение. Таймер устанавливается именно в этом потоке.
А здесь ты пытаешься снять таймер в потоке, который вызвал RemoveHook. Можешь проверить, KillTimer возвращает FALSE, потому что в этом потоке нет такого таймера. А в том потоке таймер продолжает работать, и когда DLL выгрузится вместе с хуком, происходит то, что происходит.