Доброго времени суток! Столкнулся со странным поведением объекта "событие" при установленном хуке WH_CBT в момент вставки флешки в USB порт. Глюк проявляется только при компиляции в VC6+SP6 и никак не хочет проявляться в Visual Studio 2008. Итак , код: Код (Text): #pragma data_seg(".shared") HHOOK g_hook=NULL; #pragma data_seg() #pragma comment(linker, "/SECTION:.shared,RWS") HINSTANCE g_hInstance; LRESULT CALLBACK HookFunc(int nCode, WPARAM wParam,LPARAM lParam){ return CallNextHookEx(g_hook, nCode, wParam, lParam); } extern "C" __declspec(dllexport) void InitHook(){ g_hook=SetWindowsHookEx(WH_CBT,HookFunc,g_hInstance,0); MSG msg; int ret; // ставим цикл обработки сообщений while((ret=GetMessage(&msg,0,0,0))!=0){ if(ret==-1){ } else { DispatchMessage(&msg); } } } unsigned WINAPI MyThread(void *p){ HANDLE hEvent=CreateEvent(NULL,false,false,"myevent"); DWORD dwErr=GetLastError(); if(dwErr==ERROR_ALREADY_EXISTS){ // если убрать эту ветку, то SetEvent(hEvent); // глюка Sleep(3000); // не происходит } WaitForSingleObject(hEvent,INFINITE); // ожидаем бесконечно CloseHandle(hEvent); ExitProcess(0); // завершаемся return 0; } BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if(ul_reason_for_call==DLL_PROCESS_ATTACH){ g_hInstance=(HINSTANCE)hModule; char szFileName[MAX_PATH]; GetModuleFileName(NULL,szFileName,MAX_PATH); strlwr(szFileName); BOOL bOk=false; if(strstr(szFileName,"notepad")!=NULL || strstr(szFileName,"rundll32")!=NULL) bOk=true; if(bOk){ UINT uid; HANDLE hTh=(HANDLE)_beginthreadex(NULL,0,MyThread,NULL,0,&uid); CloseHandle(hTh); } return bOk; } else if(ul_reason_for_call==DLL_PROCESS_DETACH){ UnhookWindowsHookEx(g_hook); } return TRUE; } Вкратце суть приложения: — это длл-ка, которая запускается через rundll32.exe и устанавливает хук WH_CBT хук; — чтобы процесс rundll32 не завершился, устанавливается цикл приема сообщений(GetMessage\DispatchMessage); — из DllMain порождается поток в котором создается некое событие, а после ставится бесконечное ожидание на нем; — при запуске блокнота, в процесс notepad.exe внедрится наша длл. Обратите внимание на эту часть кода функции потока: Код (Text): DWORD dwErr=GetLastError(); if(dwErr==ERROR_ALREADY_EXISTS){ // если убрать эту ветку, то SetEvent(hEvent); // глюка Sleep(3000); // не происходит } Не важно, какую смысловую нагрузку несет данный код. Важно то, что по логике при запуске единственного экземпляра rundll32 (событие с именем myevent больше нигде не открывается и не создается) функция GetLastError никак не может вернуть ERROR_ALREADY_EXISTS, поэтому два вызова — SetEvent и Sleep никогда не выполнятся. Так и есть, эти два вызова не происходят, выполнение сразу переходит на WaitForSingleObject. Здесь идет бесконечное ожидание. И вот ! Как только мы вставляем флешку в USB порт, каким-то невообразимым образом, событие hEvent стает сигнальным и вызов WaitForSingleObject завершается с кодом WAIT_OBJECT_0. В сущности, смысл выше приведенного участка прост: если запустили еще один rundll32, прогрузив туда нашу длл, то нужно завершить предыдущий — там ведь идет ожидание события hEvent, которое стает сигнальным, если GetLastError возвращает ERROR_ALREADY_EXISTS. После, уже в новом экземпляре rundll32 будет происходить бесконечное ожидание на событии hEvent c именем myevent до того момента, как кто-то снова не запустит rundll32, прогрузив нашу длл-ку — предыдущий завершится. И так далее. Самое забавное, что глюка при вставке флешки не происходит, если код откомпилировать в VS2008. Попробуйте компильнуть код в VC6 (у меня sp6) и после запустить длл-ку из командной строки: rundll32.exe c:\project\some\debug\prg.dll, InitHook И вставьте флешку и увидите, что процесс завершится. Как это пояснить с позиции логики, пока не пойму. Я так понимаю, что всему виной цикл обработки сообщений GetMessage\DispatchMessage, ведь именно GetMessage принимает событие с кодом 0x219 (WM_DEVICECHANGE) и параметром 7 (DBT_DEVNODES_CHANGED) в момент вставки флешки. Но выхода-то из цикла не происходит, GetMessage как бы даже не успевает возвратиться даже, как WaitForSingleObject завершает ожидание (событие hEvent стало сигнальным). Кстати если заменить цикл обработки Код (Text): while((ret=GetMessage(&msg,0,0,0))!=0){ if(ret==-1){ } else { DispatchMessage(&msg); } } на Код (Text): Sleep(INFINITE); Глюк будет тот же — при вставке флешки событие стает сигнальным и все тут! А теперь фокус. Убираем эту часть кода: Код (Text): DWORD dwErr=GetLastError(); if(dwErr==ERROR_ALREADY_EXISTS){ // если убрать эту ветку, то SetEvent(hEvent); // глюка Sleep(3000); // не происходит } И вуаля! Глюк исчезает. Получается, будто какой-то невидимый фантомный код запускает эту ветку постфактум (или функцию потока заново) — и, так как событие myevent уже есть, GetLastError возвратит ERROR_ALREADY_EXISTS и вызов SetEvent(hEvent) cделает свое черное дело. Кстати , если убрать этот вызов SetEvent(hEvent), то глюк исчезает. Также , если убрать хуки, то глюк исчезнет. Но как? Почему? В чем конфликт? Почему 2008 студия не подвержена этому? Система: Windows XP SP3 Заранее благодарен за ответы!
groomy Может быть ошибка кодогенерации? Проявляется всегда или только в debug (или release) режиме? Для отладки попробовать: 1) сбрасывать код ошибки через SetLastError(); 2) сгенерировать уникальное имя через UID.
К сожалению и в debug и в release версиях проявляется одинаково. По поводу имени - разные имена пробовал, тут дело не в этом. Код ошибки сбрасывал и перед вызовами и после. Наличие оптимизации никак не влияет тоже. В кодогенерации ошибка... даже не знаю как проверить. Пробовал по разному - пока результат одинаков. Если кому не тяжело, компильните выше приведенный код в VC6 , он дан полностью за исключением include. И гляньте, проявляется ли у вас такая проблема. Может, у меня с системой что-то не так...
Не пойму в чем суть проблемы. Вы вызываете SetEvent(hEvent) и потом удивляетесь почему WaitForSingleObject(hEvent) возвращает WAIT_OBJECT_0?
В том то и дело, что SetEvent находится на участке кода, который не получает управление - как этот участок может получить управление если GetLastError не возвращает ERROR_ALREADY_EXIST, потому как события с таким именем нет еще, мы его только что создали ведь. Но после вставки флешки этот участок почему-то получает управление, это абсурд просто! Гляньте на код, там все очень наглядно.
Это с какой стати? Вы же запустили rundll32 с параметрами Вашей DLL. При вставке флешки как раз таки объект уже существует и ветка выполнится.
простите, а с какой стати после вставки флешки управление попадает в мой поток? Есть процесс rundll32, в который прогружена моя длл. Она же , после прогрузки в этот процесс установила глобальный хук WH_CBT. Висит себе процесс, никого не трогает. Вставили флешку. SetEvent в моем потоке почему-то сработал и ожидание на hEvent завершилось, после чего процесс завершился (вызов ExitProcess после WaitForSingleObject). Выходит, после вставки флешки кто-то инициировал создание потока. Инициация создания потока возможна из DllMain при условии что процесс в который прогрузилась длл либо rundll32 либо notepad. Получается, что моя дллка снова попыталась прогрузится в rundll32 из-за установленого хука и был вызов DllMain c DLL_PROCESS_ATTACH? Но ведь она там есть уже (поток выполняется то наш, ожидание на событии висит), система же проверяет перед внедрением, нет ли в процессе уже такой. И если нет, то только тогда внедряет! Или в моих рассуждениях есть ошибка?
При вставке флешки инициализируется диалоговое окно автозапуска, которое, на сколько я помню, запускается с помощью rundll32. Т.е. создается новый процесс rundll32, создается новый поток MyThread, вызывается CreateEvent("myevent"). Естественно CreateEvent возвращает ERROR_ALREADY_EXIST, т.к. объект существует (Вы его создали при первом запуске rundll32). В итоге оба потока завершатся после WaitForSingleObject(hEvent), т.к. событие свершилось.
google Блин, 100 %. У меня просто это окно отключено, но rundll32 и правда вызывается , считываются параметры - показывать это окно или нет... Вам огромная благодарность за подсказку, чето не догадался сам Тему можно закрывать!
Для определения вставки флэхи давно уж нотификаторы [ч\з RegisterDeviceNotification] нужно использовать (читаем MSDN).