запуск таймера из хуковой процедуры
От: Tagus  
Дата: 17.03.02 16:41
Оценка:
Здравствуйте уважаемый All.

Это снова я и снова со странным вопросом :).

Имеется 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)

Забыл написать: проверено — проблема именно в этом
вызове. Если его убрать — хук опять начинает нормально
сниматься.
Re: запуск таймера из хуковой процедуры
От: Dr_Sh0ck Беларусь  
Дата: 17.03.02 18:29
Оценка:
Здравствуйте 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
не помогает.
Re: запуск таймера из хуковой процедуры
От: SergH Россия  
Дата: 17.03.02 21:59
Оценка:
Здравствуйте 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, загружать её при загрузке в новый процесс, а потом не выгружать.
Делай что должно, и будь что будет
Re[3]: запуск таймера из хуковой процедуры
От: Dr_Sh0ck Беларусь  
Дата: 18.03.02 05:29
Оценка:
Здравствуйте Аноним, Вы писали:

А>Таймерная процедура в той-же DLL. Ввызов KillTimer

А>не помогает.


Покажи код
Do not fake yourself ;)
ICQ#: 198114726
Re[4]: запуск таймера из хуковой процедуры
От: Tagus  
Дата: 18.03.02 10:46
Оценка:
Здравствуйте Dr_Sh0ck, Вы писали:

DS>Покажи код


Показываю.

Код DLL (все лишнее для простоты выкинуто, вызов таймера
сделан только 1 раз, в таком виде компилировалось, — точно
так-же не работает):

HHOOK hHook;
UINT TimerID;

BOOL TimerRunning = FALSE;

LRESULT CALLBACK MouseHook (int nCode, WPARAM wParam, LPARAM lParam)
{
    switch (wParam)
    {
        case WM_MOUSEMOVE:
            if (TimerRunning == FALSE)
            {
                TimerRunning = TRUE;
                // если этот вызов таймера убрать - все ОК
                TimerID = SetTimer (NULL, 0, 100, TimerProc);
            }
    }
        
    return CallNextHookEx(hHook, nCode, wParam, lParam); 
}

void SetHook (HINSTANCE hInstDLL)
{
    hHook = SetWindowsHookEx (WH_MOUSE, MouseHook, hInstDLL, 0);
}

void RemoveHook (void)
{
    KillTimer (NULL, TimerID); // можно выкинуть - результат не меняется
    UnhookWindowsHookEx (hHook);
}

VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{

}


Вызывающе-загружающий код в основной программе:

HINSTANCE _hInstDLL;  
void (* _HookSet) (HINSTANCE);
void (* _HookRemove) (void);

.............

_hInstDLL = LoadLibrary("mhook.dll"); 
_HookSet = GetProcAddress (_hInstDLL, "SetHook");
_HookRemove = GetProcAddress (_hInstDLL, "RemoveHook");

.............

_HookSet (_hInstDLL);

.............

_HookRemove ();

.............


Таймер к моменту снятия хука должен уже давно завершиться,
т.к. хук снимается по команде из меню основной программы.
Соответсвенно, почему KillTimer в данном случае не помогает
вполне понятно (по крайней мере кажется, что понятно ;) ).
Re[2]: запуск таймера из хуковой процедуры
От: Tagus  
Дата: 18.03.02 11:02
Оценка:
Здравствуйте SergH, Вы писали:

SH>Я думаю, проблема в том, что происходит попытка вызвать TimerProc, когда DLL уже выгружена. Почему не помогает KillTimer не знаю. Можно поместить TimerProc в другую dll, загружать её при загрузке в новый процесс, а потом не выгружать.


Вряд-ли. DLL не выгружается вызовом UnhookWindowsHookEx.
Для выгрузки DLL нужно вызвать FreeLibrary, чего не делается.
Кроме того, в случае глобального хука, который имеет место
быть, DLL не выгрузится даже после FreeLibrary, поскольку
глобальный хук привязывается ко всем запущенным в системе
процессам, соответственно, DLL должна висеть в памяти
пока все процессы не завершатся.
Re[3]: запуск таймера из хуковой процедуры
От: Dr_Sh0ck Беларусь  
Дата: 21.03.02 04:32
Оценка: +1
Здравствуйте 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), то никакого смысла держать в памяти ДЛЛ с хуковой процедурой нет и она (ДЛЛ) будет выгружена.
Do not fake yourself ;)
ICQ#: 198114726
Re[5]: запуск таймера из хуковой процедуры
От: Dr_Sh0ck Беларусь  
Дата: 21.03.02 04:38
Оценка:
Здравствуйте Tagus, Вы писали:

T>Показываю.




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.
Do not fake yourself ;)
ICQ#: 198114726
Re[4]: запуск таймера из хуковой процедуры
От: Dr_Sh0ck Беларусь  
Дата: 21.03.02 04:40
Оценка:
Здравствуйте Dr_Sh0ck, Вы писали:

DS> Дело в том, что ты ведь явно ДЛЛ не загружаешь by LoadLibrary. Как только ты ставишь хук (в частности на события от мыши), и когда такое событие происходит, система проверяет, не установлен ли хук. Если установлен, то далее система проверяет, находится ли ДЛЛ с нужной хуковой процедурой в памяти — если да, то она просто отображается на адресное пространство соответствующего процесса, если нет, то ДЛЛ в память загружается и затем отображается. Затем происходит вызов хуковой процедуры. Когда система определит, что хуков нет (UnhookWindowsHookEx), то никакого смысла держать в памяти ДЛЛ с хуковой процедурой нет и она (ДЛЛ) будет выгружена.


Сорри, просмотрел — ты ее действительно LoadLibrary загружаешь...
Do not fake yourself ;)
ICQ#: 198114726
Re[3]: запуск таймера из хуковой процедуры
От: Dr_Sh0ck Беларусь  
Дата: 21.03.02 17:44
Оценка:
Здравствуйте Tagus, Вы писали:

[skipped]

Подумал я над свои последним сообщением и понял — наврал позорно. Под "выгрузкой" ДЛЛ я понимал отмену ее проекции.
Ну да ладно. Подумал я еще и вот что мне в голову пришло:

HHOOK hHook;
UINT TimerID;

BOOL TimerRunning = FALSE;

LRESULT CALLBACK MouseHook (int nCode, WPARAM wParam, LPARAM lParam)
{
    switch (wParam)
    {
        case WM_MOUSEMOVE:
            if (TimerRunning == FALSE)
            {
                TimerRunning = TRUE;
                // если этот вызов таймера убрать - все ОК
                TimerID = SetTimer (NULL, 0, 100, TimerProc);
//проблема здесь!!!!
            }
    }
        
    return CallNextHookEx(hHook, nCode, wParam, lParam); 
}

void SetHook (HINSTANCE hInstDLL)
{
    hHook = SetWindowsHookEx (WH_MOUSE, MouseHook, hInstDLL, 0);
}

void RemoveHook (void)
{
    KillTimer (NULL, TimerID); // можно выкинуть - результат не меняется
    UnhookWindowsHookEx (hHook);
}

VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{

}

Проблема в том, что ты делаешь SetTimer, когда вызывается твоя хуковая процедура. Но ведь она вызывается в контексте совершенно неизвестного процесса. Следовательно адрес процедуры таймера — это адрес в виртуальном адресном пространстве ентого процесса. Пока существует проекция ДЛЛ с хукой процедурой все нормально — идут себе WM_TIMER'ы.
А вот KillTimer ты вызываешь в контексте _своего_ процесса и передаешь ему идентификатор таймера, который фактически создан в другом процессе. Естессно Таймер не убивается (такого идентификатора в твоем процесса просто _нет_). Затем, после вызова UnhookWindowsHookEx, хук убивается => проекция отменяется => после прихода очередного WM_TIMER'а происходит переход по адресу где _раньше_ _была_ таймерная процедура (из ДЛЛ). А там уже ею и не пахнет...
Тебе надо переделать механизм установки таймера так, чтобы он устанавливался в контексте твоего процесса.
И еще. По-моему у тебя еще косячок здесь
BOOL TimerRunning = FALSE;
...
...
if( TimerRunning == FALSE) ...

Дело в том, что твой проецирование ДЛЛ с хуковой процедурой на адресное пространство процессов будет происходить много раз (по разу для каждого нужного процесса). И каждый раз будет происходить инициализация TimerRunning = FALSE => и условие будет выполнятся неоднократно => и таймеров будет установлена куча (хотя там не одного быть не должно).
Так что по-моему лучше уэту переменную поместить в общую секцию.

P.S. Если кто-уверен, что я опять наврал, то дико извиняюсь. Тогда уж и мне заодно объясните в чем тут дело .
Do not fake yourself ;)
ICQ#: 198114726
Re[5]: запуск таймера из хуковой процедуры
От: Alex Fedotov США  
Дата: 21.03.02 19:26
Оценка:
Здравствуйте 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 вызывается в контексте того потока, в который пришло мышиное сообщение. Таймер устанавливается именно в этом потоке.

T>void SetHook (HINSTANCE hInstDLL)
T>{
T> hHook = SetWindowsHookEx (WH_MOUSE, MouseHook, hInstDLL, 0);
T>}

T>void RemoveHook (void)

T>{
T> KillTimer (NULL, TimerID); // можно выкинуть — результат не меняется
T> UnhookWindowsHookEx (hHook);
T>}
[/ccode]

А здесь ты пытаешься снять таймер в потоке, который вызвал RemoveHook. Можешь проверить, KillTimer возвращает FALSE, потому что в этом потоке нет такого таймера. А в том потоке таймер продолжает работать, и когда DLL выгрузится вместе с хуком, происходит то, что происходит.
-- Alex Fedotov
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.