Здравствуйте, Inversion, Вы писали:
I>Сразу мысль.
Оффтоп: сразу мысль: нельзя так циклиться (по несколько недель) на одном затыке. Это вредно для соображалки
Наскоко я понял, консольные окна, типа окна от cmd.exe, совсем какие-то "не такие" как обычные окна. К примеру, на них не поставишь полупрозрачность, с них не снимешь информацию о локали. Возможно, с фуками та же хигня.
I> То есть если батник уже был запущен и стоял на ожидании, когда устанавливается глобальный хук, то хук к этому батнику не цепляется вообще.
Какой именно хук? И как проводилось зацепление? Т.е. хук — функция в цепочке обработки сообщений. Если сообщений нету — хук не сработает(и не подвесится). Висящее на паузе окно cmd.exe возможно вообще в плане оконных сообщений ни на что не реагирует. Это надо спаем проверить, опять же, если получится
Или имелось ввиду что SetWindowsHookEx ноль вернул?
I>1) Сначала хуки ставились и освобождались только в dll, а с exe только вызывал нужные функции setHook/clearHook. Результаты: WH_KEYBOARD, WH_MOUSE — снимаются легко и без проблем, WH_CALLWNDPROC — залипает когда приложение прибить из TaskManager.
Всё верно. Первые двое — глобальные хуки, т.е функция перехвата не внедряется, а просто ставится в очередь. Третий же копируется в область памяти чужого процесса. Поэтому после убивания родительского процесса, dll-ка спокойно продолжает жить в чужом (наша месть Чужим!).
I>> Практика показывает, что как только завершается нить, вызывавшая SetWindowsHookEx, так dll с хуком и выгружается.
Не буду спорить, но мне кажется, всё зависит от реализации. Сама dll-ка, внедрившись в чужой код, может сама "застолбить" хэндл на саму себя и тогда выгружена не будет. Я так делал, когда надо было повесить хук только для внедрения в код, тогда хук является только точкой входа в чужой код, и, после внедрения, dll-ка из родительского процесса выгружалась, а хук удалалься. В описанной выше ситуции скорее всего происходит следующее: пока хук в цепочке, любые движения в системе будут вызывать реакцию dll-ки с функцией хука, весь вопрос в том, как долго она будет работать, от этого зависит время освобождения чужих процессов от нашего хука.
I>Но это не помогло для cmd… таймер даже не функционировал под таким процессом.
Тут полезно почитать про консольные приложения, и как они себя ведут со своим консольным окном. У меня такой подозр, что функция обработки сообщений окна (на него же вешался таймер поди?) совсем примитивная и в отсутсвии активной деятельности стоит колом.
Советую позаводить таймер, _не_привязанный_ к окну, т.е. с указание в четвёртом параметре функции обработки таймера.
I>Есть ещё мысли, о debug-хуках, так как о них пишет, что ними можно делать что-то с другими хуками.
Стоя в бронескафандре за километровой свинцовой стеной
Возможно, я криворук, но у меня попытки похучить хуки вытекли в краш системы
I>А можно ли как-то сказать глобальному хуку не цепляться к определённым процессам? Сделать фильтр какой-то. По сути на этих cmd-процесах мне этот хук не нужен (замечено, что он и не работает на них, но это уже пусть).
Вот! Вот с этого и надо было начинать
Цепанувшись за процесс, можно его проверить, например, на путь, через OpenProcess(...GetCurrentProcessId()...), не помню, и так далее. Или узнать, консольное ли я приложение. Не знаю, как, но знаю, что можно.… 1сек, вот, GetConsoleWindow, нашёл сходу.
После того, как определили, что надо выгрузиться, нужно обязательно закрыть свой хук, вызвать цепочку хуков, потом выгрузить себя полностью из процесса:
FreeLibraryAndExitThread(hModule, 0);
hModule — это хендл себя (dll-ки) из которой была вызвана функция хука.
Я такой фокус напрямую в функции хука не делал, а делал в thread-е, который создавала функция хука, поэтому у меня она отрабатывала до конца сама.
Проблема в том, что хук опять повесится. Но тут надо поэкспериментировать. Если это будет не накладно, то можно так и оставить, чтобы он сам автоматически снимался. Если накладно, тогда надо будет в родном приложении заводить не один хук, а семейство хуков с жёстко прописанными идентификаторами thread-ов, на которые вешаемся. Но тут надо будет следить за вновь прибывшими (и за ушедшими) процессами, детектить их консольную сущность и если они не консольные — добавлять их в список(удалять из списка). Геморно. Так что получается, первый вариант, с автоотпиныванием, самый простой
Re[2]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
Здравствуйте, Inversion, Вы писали:
I>Сразу мысль. I>А можно ли как-то сказать глобальному хуку не цепляться к определённым процессам? Сделать фильтр какой-то. По сути на этих cmd-процесах мне этот хук не нужен (замечено, что он и не работает на них, но это уже пусть).
Попробуй в DllMain возвращать FALSE в ответ на DLL_PROCESS_ATTACH. Процесса-владельца опознавать по GetModuleFileName(NULL,...); она в kernel32, её можно звать из dllmain.
Re[2]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
I>>Нужно хоть каким-то способом убрать WH_CBT/WH_CALLWNDPROC хуки из подобных cmd-процессов при завершении программы. Метод может быть самый фантастический, лишь бы работал и не был ощутим по производительности (даже те таймеры, которые я пробовал не были заметны в системе).
Кё>Могу дать такую наводку — хуки снимаются где-то внутри GetMessage/PostMessage, и до тех пор, пока эти функции не будут вызваны, он не будет выгружен. Возможно что cmd.exe как-то виснет в ожидании чего-то, или у него просто нет нормального цикла обработки сообщений. Может поможет его потоку отправить PostThreadMessage, или завести свой и там вызвать GetMessage?
Консольное приложение обычно вообще не имеет цикла выборки. А окна для него создает и менеджит csrss. Так что хуки если и смогуть попасть — то туда. При этом dll может загрузить и консольное приложение, при некоторых из вызовов user32 такое, я полагаю, возможно. Судя по описанию, так и именно и происходит. Да, предложенное решение http://www.rsdn.ru/forum/winapi/3544366.1.aspx
Здравствуйте, Inversion, Вы писали:
I>Нужно хоть каким-то способом убрать WH_CBT/WH_CALLWNDPROC хуки из подобных cmd-процессов при завершении программы. Метод может быть самый фантастический, лишь бы работал и не был ощутим по производительности (даже те таймеры, которые я пробовал не были заметны в системе).
Могу дать такую наводку — хуки снимаются где-то внутри GetMessage/PostMessage, и до тех пор, пока эти функции не будут вызваны, он не будет выгружен. Возможно что cmd.exe как-то виснет в ожидании чего-то, или у него просто нет нормального цикла обработки сообщений. Может поможет его потоку отправить PostThreadMessage, или завести свой и там вызвать GetMessage?
Re[4]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
Здравствуйте, Inversion, Вы писали:
I>> То есть если батник уже был запущен и стоял на ожидании, когда устанавливается глобальный хук, то хук к этому батнику не цепляется вообще. CEM>>Какой именно хук? И как проводилось зацепление? I>Глобальный типа WH_CALLWNDPROC, через SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)dllHookProc, g_hDLL, 0) из .exe
Ну да, выходит, функция обработки в паузе. (могу врать).
CEM>>Это надо спаем проверить, опять же, если получится I>«спаем проверить» — это как?
Это ms spy++ или любое приложение, которое может отлавливать сообщения. Кстати да, если спай не заработает на консольном, то: 1. или нет сообщений, 2. или нельзя просто так хуком цепануться на консольное приложение.
I>SetTimer(NULL, t_watchForMainApp, 4000, (TIMERPROC)tf_watchForMainApp) I>Вроде так и делал (ставил NULL вместо окна), иначе таймер и не хотел работать.
Ого. Вот это уже интереснее Т.е. даже такой таймер не работает? На вский случай спрошу, в курсе, что оконные и неоконные таймеры различаются по параметрам, возвращаемым значениям и принципу запуска при одной и той же сигнатуре SetTimer?
I>А я почитал, что они служат для того, чтобы позволять определённому хуку срабатывать или нет. Об управлении установкой хуков речи не нашел.
Ну я читал, что это хуки для отладки хуков.
I>Попробовал в DLL_PROCESS_ATTACH. I>И FreeLibraryAndExitThread(hDllInst, 0) и FreeLibrary(hDllInst) завершают тот процесс, из которого вызываются Не подходит.
Хмм... не уверен, но если хэндл получить из GetModuleHandle ("<тут_имя_dll>"); ?
Т.е. я делал Free на хэндл, который получал сам, на не тот который в DllMain (hDllInst — это же про него речь?)
CEM>>Проблема в том, что хук опять повесится. Но тут надо поэкспериментировать. Если это будет не накладно, то можно так и оставить, чтобы он сам автоматически снимался. I>Попробовал — накладно, то есть ощутимо (Пробовал на процессе Total Commander'a, так он постоянно продолжал пробовать подцепиться и при этом как-то подтормаживал)
Так Тотал Коммандер же не консольное окно. Там событий сыпется тысячи штук в секунду. Вся заточка на отцепление именно на _консольность_ с надеждой, что они меньше крутят функцию обработки окна.
... блин, где знающие люди, которые подскажут про консольное окно сюда нам?...
I>Тоже думал об таком… очень геморно
Так, а какая задача вообще стоит, если не секрет? А то может и не нужны хуки вовсе?
PS. Посмотрел spy++, он вообще отказывается включать перехват на консольных окнах!
Re[4]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
неработоспособно ввиду того, что процесс может как иметь консоль, так и окна с нормальным циклом выборки сообщений.
I>Как неработоспособно? Я уже им пользуюсь вот с сегодняшнего сутра. Ведь для описанной ситуации с батником это как раз оно.
Аргументация супер
Перечитайте что отквотировано выше. Такой проверкой вы отсекаете не только cmd. А проверять имя — не только cmd может вести себя сходно с ним — любое другое консольное приложение, не имеющие своих окон и цикла выборки. Соответственно, в этом направлении работоспособного решения не будет.
Я долго уже сижу над этой проблемой, многое пробовал и думал, что обойдусь своими силами, и не придётся просить о помощи, но я больше не могу, я потратил уже несколько недель…
Помогите справиться с проблемой, если это вообще возможно.
Проблема заключается в том, что некий глобальный хук не снимается ни через UnhookWindowsHookEx, ни при завершение основного процесса с выгрузкой dll, с некоторых процессов, а именно с «Windows Command Processor», то есть с процесса стандартного cmd.exe. Но не просто процесса cmd.exe, а только если это был запущен батник, который запускал другой процесс (в моём случае запускался на исполнение и успешно завершался Ruby-скрипт) и этот батник не закрывается в конце работы, а ждёт команды, или висит на pause. И ещё важным моментом есть то, что это происходит только, если такой батник запустить после запуска программы с установкой глобального хука. То есть если батник уже был запущен и стоял на ожидании, когда устанавливается глобальный хук, то хук к этому батнику не цепляется вообще. Напрашивается вывод, что сыграл роль тот процесс, которых был запущен батником, отработал и выгрузился.
За хуком я слежу через Unlocker (открываю ним dll-файл). Вот как это выглядит:
Во время работы:
После завершения процесса и снятия хуков:
Осталось 3 батника :(
Через TaskInfo видно, что у каждого присутствует моя dll в списке прикреплённых модулей. И эти процессы сами никак не освобождаются от dll. Мой dll-файл при этом не доступен для файловых операций, а мне его как раз надо перемещать в процессе обновления после завершения процесса, почему я парюсь с этой проблемой уже столько времени.
Помогает только закрытие каждого батника, или разблокировка через Unlocker.
Я в тупике :(
Я имею дело с 3 разными хуками: WH_KEYBOARD, WH_MOUSE, WH_CALLWNDPROC и WH_CBT
Что я уже пробовал:
1) Сначала хуки ставились и освобождались только в dll, а с exe только вызывал нужные функции setHook/clearHook.
Результаты: WH_KEYBOARD, WH_MOUSE — снимаются легко и без проблем, WH_CALLWNDPROC — залипает когда приложение прибить из TaskManager. Со временем потом некоторые приложения сами освобождаются (через несколько секунд/минут), а некоторые, которые работают на низком уровне, типа программ управляющих тачпадом или глобальными гарячими клавишами, не хотят освобождаться сами, даже после длительного ожидания. WH_CBT тогда ещё не пробовал.
> Что будет если снять задачу с приложения, которое поставило хук? Хук останеться в системе?
>> Практика показывает, что как только завершается нить, вызывавшая SetWindowsHookEx, так dll с хуком и выгружается.
У меня же SetWindowsHookEx находится в dll. Возникла мысль, что нужно перенести установку хука в exe, так как именно он точно выгружается, и всё будет ок.
Но когда я так всё сделал (помогло обсуждение в этой нити
), то получилось так: WH_KEYBOARD, WH_MOUSE — снимаются легко и без проблем, WH_CALLWNDPROC и WH_CBT — тоже снимаются даже при ручном завершении процесса, но не снимаются с описанного cmd процесса, даже при нормальном завершении процесса, который их ставил.
3) Потом я уже пробовал извращаться и запускал таймер внутри каждого хукнутого процесса, и в нём следил, запущен ли основной процесс, и если нет — вызывал UnhookWindowsHookEx. Оказалось, что, действительно, такие таймеры работают, даже когда основной процесс прибить. И это сработало даже в комбинации с вариантом 1): хуки, те, что залипали при ручном завершении процесса, таким таймером освобождались (я даже лог из этого таймера вёл и следил за каждым из процессов). Но это не помогло для cmd… таймер даже не функционировал под таким процессом.
У меня всё…
Я не понимаю, чем эта ситуация с cmd такая уникальная, что возникает такой аффект, но я с этим справиться не могу :(
Есть ещё мысли, о debug-хуках, так как о них пишет, что ними можно делать что-то с другими хуками, но мне нужен хоть лучик надежды, чтобы продолжить попытки…
Помогите пожалуйста, побороть эту проблему, если это возможно.
Нужно хоть каким-то способом убрать WH_CBT/WH_CALLWNDPROC хуки из подобных cmd-процессов при завершении программы. Метод может быть самый фантастический, лишь бы работал и не был ощутим по производительности (даже те таймеры, которые я пробовал не были заметны в системе).
Код приводить не вижу смысла, так как всё работает и я много раз сверялся и следовал примерам из разных нитей этого форума. Тут дело в чём-то непонятном…
Сразу мысль.
А можно ли как-то сказать глобальному хуку не цепляться к определённым процессам? Сделать фильтр какой-то. По сути на этих cmd-процесах мне этот хук не нужен (замечено, что он и не работает на них, но это уже пусть).
Re[3]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
Здравствуйте, quodum, Вы писали:
Q>Попробуй в DllMain возвращать FALSE в ответ на DLL_PROCESS_ATTACH.
Как раз ещё вчера такое попробовал (просто поставил return false) и получил, что глобальный хук постоянно снова и снова пробует прицепицца к каждому процессу. Машина при этом очень сильно тормозит у меня (у меня Celeron слабенький на ноуте). Попробую ещё сделать сейчас проверку на cmd.exe, может не будет так ощутимо.
Re[3]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
Здравствуйте, CEMb, Вы писали:
I>> То есть если батник уже был запущен и стоял на ожидании, когда устанавливается глобальный хук, то хук к этому батнику не цепляется вообще.
CEM>Какой именно хук? И как проводилось зацепление?
Глобальный типа WH_CALLWNDPROC, через SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)dllHookProc, g_hDLL, 0) из .exe
CEM>Т.е. хук — функция в цепочке обработки сообщений. Если сообщений нету — хук не сработает(и не подвесится). Висящее на паузе окно cmd.exe возможно вообще в плане оконных сообщений ни на что не реагирует.
Возможно, действительно у батника, который запускается, происходит некое сообщение и хук цепляется, а у того, что висит — нет.
CEM>Это надо спаем проверить, опять же, если получится :)
«спаем проверить» — это как?
CEM>Или имелось ввиду что SetWindowsHookEx ноль вернул?
Нет, SetWindowsHookEx нормально срабатывает.
I>>> Практика показывает, что как только завершается нить, вызывавшая SetWindowsHookEx, так dll с хуком и выгружается. CEM>Не буду спорить, но мне кажется, всё зависит от реализации.
По моему опыту получилось, что dll действительно освобождается лучше, чем при установке хука из dll.
CEM>Советую позаводить таймер, _не_привязанный_ к окну, т.е. с указание в четвёртом параметре функции обработки таймера.
SetTimer(NULL, t_watchForMainApp, 4000, (TIMERPROC)tf_watchForMainApp)
Вроде так и делал (ставил NULL вместо окна), иначе таймер и не хотел работать.
I>>Есть ещё мысли, о debug-хуках, так как о них пишет, что ними можно делать что-то с другими хуками. CEM>Стоя в бронескафандре за километровой свинцовой стеной :) CEM>Возможно, я криворук, но у меня попытки похучить хуки вытекли в краш системы :)
А я почитал, что они служат для того, чтобы позволять определённому хуку срабатывать или нет. Об управлении установкой хуков речи не нашел.
I>>А можно ли как-то сказать глобальному хуку не цепляться к определённым процессам? Сделать фильтр какой-то. По сути на этих cmd-процесах мне этот хук не нужен (замечено, что он и не работает на них, но это уже пусть).
CEM>Вот! Вот с этого и надо было начинать :) CEM>Цепанувшись за процесс, можно его проверить, например, на путь, через OpenProcess(...GetCurrentProcessId()...), не помню, и так далее. Или узнать, консольное ли я приложение. Не знаю, как, но знаю, что можно.… 1сек, вот, GetConsoleWindow, нашёл сходу. CEM>После того, как определили, что надо выгрузиться, нужно обязательно закрыть свой хук, вызвать цепочку хуков, потом выгрузить себя полностью из процесса:
CEM>
CEM>FreeLibraryAndExitThread(hModule, 0);
CEM>
CEM>hModule — это хендл себя (dll-ки) из которой была вызвана функция хука. CEM>Я такой фокус напрямую в функции хука не делал, а делал в thread-е, который создавала функция хука, поэтому у меня она отрабатывала до конца сама.
Попробовал в DLL_PROCESS_ATTACH.
И FreeLibraryAndExitThread(hDllInst, 0) и FreeLibrary(hDllInst) завершают тот процесс, из которого вызываются :( Не подходит.
CEM>Проблема в том, что хук опять повесится. Но тут надо поэкспериментировать. Если это будет не накладно, то можно так и оставить, чтобы он сам автоматически снимался.
Попробовал — накладно, то есть ощутимо (Пробовал на процессе Total Commander'a, так он постоянно продолжал пробовать подцепиться и при этом как-то подтормаживал)
CEM>Если накладно, тогда надо будет в родном приложении заводить не один хук, а семейство хуков с жёстко прописанными идентификаторами thread-ов, на которые вешаемся. Но тут надо будет следить за вновь прибывшими (и за ушедшими) процессами, детектить их консольную сущность и если они не консольные — добавлять их в список(удалять из списка). Геморно.
Тоже думал об таком… очень геморно :(
Кажется, есть решение.
Использовал совет от quodum и CEMb в комбинации :)
Вот что я сделал в dll:
case DLL_PROCESS_ATTACH:
if (GetConsoleWindow()) return false;
…
return true
}
Оказалось, что в такой ситуации cmd.exe ведет себя тоже не так, как все :) А именно: после такого трюка, глобальных хук ещё несколько раз пытается подцепиться к процессу и оставляет это дело после 2-6 попыток (проследил логом). Для других же процессов это могло продолжаться пока работает основная программа, и это приводило к тормозам системы.
Мою проблему это решило.
Большое вам спасибо за помощь. Кодёнку тоже спасибо за совет, правда к нему так дело и не дошло.
Re[5]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
Проблема то уже решена, см. моё предыдущее сообщение :)
Но на вопросы обязательно отвечу.
CEM>Это ms spy++ или любое приложение, которое может отлавливать сообщения.
Понял. У меня Winspector. Попробовал: для cmd он не видит никаких сообщений вообще :(
I>>SetTimer(NULL, t_watchForMainApp, 4000, (TIMERPROC)tf_watchForMainApp) I>>Вроде так и делал (ставил NULL вместо окна), иначе таймер и не хотел работать. CEM>Ого. Вот это уже интереснее :) Т.е. даже такой таймер не работает? На вский случай спрошу, в курсе, что оконные и неоконные таймеры различаются по параметрам, возвращаемым значениям и принципу запуска при одной и той же сигнатуре SetTimer?
Не, как раз такой таймер и заработал, но о разных сигнатурах я и не подумал, так как оно заработало и я оставил так как есть.
I>>Попробовал в DLL_PROCESS_ATTACH. I>>И FreeLibraryAndExitThread(hDllInst, 0) и FreeLibrary(hDllInst) завершают тот процесс, из которого вызываются :( Не подходит. CEM>Хмм... не уверен, но если хэндл получить из GetModuleHandle ("<тут_имя_dll>"); ? CEM>Т.е. я делал Free на хэндл, который получал сам, на не тот который в DllMain (hDllInst — это же про него речь?)
Да hDllInst брался с параметров и процесс у меня завершался.
CEM>Так Тотал Коммандер же не консольное окно. Там событий сыпется тысячи штук в секунду. Вся заточка на отцепление именно на _консольность_ с надеждой, что они меньше крутят функцию обработки окна.
Ага, именно это и сыграло в решении :)
Ещё раз спасибо за участие в разборе проблемы.
Re[6]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
От:
Аноним
Дата:
22.09.09 17:35
Оценка:
Здравствуйте, Inversion, Вы писали:
I>Ещё раз спасибо за участие в разборе проблемы.
А чем всё кончилось-то в результате?
Re[7]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
AS>Такой проверкой вы отсекаете не только cmd. А проверять имя — не только cmd может вести себя сходно с ним — любое другое консольное приложение, не имеющие своих окон и цикла выборки.
Но, скорее всего, с такими процессами будет такая же проблема с залипанием, против которой я и борюсь.
Re[6]: Детектив под названием «Залипание DLL-хуков». Я сдаюс
AS>>Такой проверкой вы отсекаете не только cmd. А проверять имя — не только cmd может вести себя сходно с ним — любое другое консольное приложение, не имеющие своих окон и цикла выборки.
I>Но, скорее всего, с такими процессами будет такая же проблема с залипанием, против которой я и борюсь.
Вы события от них получать не будете, если у них кроме консоли есть еще и цикл выборки.