Привет!
Что-то странное происходит...
Работа ведется в MS VS 2008 на Visual C++. Сделал тестовое приложение (на MFC), к которому пристегнута DLL-ка. DLL-ка написана на чистом API. Она создает thread при старте и должна закрыть его при выгрузке. Мне бы не хотелось вручную закрывать поток из главного приложения (к примеру, в OnDestroy()), т.к. в разных DLL-ках могут появляться разные потоки. Некузяво как-то всех их отслеживать в главном приложении... Вот код запуска/выгрузки DLL-ки:
HANDLE hShutdownEvent = NULL;
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hShutdownEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
pThread = new CThread;
pThread->Start();
break;
case DLL_PROCESS_DETACH:
SetEvent(hShutdownEvent);
Sleep(10);
pThread->Stop();
SAFE_DELETE(pThread);
SAFE_CLOSE_HANDLE(hShutdownEvent);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
Для закрытия thread-а используется hShutdownEvent, а функция потока этот ивент ожидает. Когда ивент взводится, поток завершается. Функция потока проста, как дверь:
DWORD WINAPI Thread_func(LPVOID param)
{
CThread* p = (CThread*)param; // Pointer to Thread class
HANDLE hEventArray[2]; // First event must be close event
DWORD nEvent;
::OutputDebugString("Thread_func(): Start thread function\n");
hEventArray[0] = p->_h_shutdown_event; // highest priority shutdown event
hEventArray[1] = hShutdownEvent;
while(1) {
nEvent = WaitForMultipleObjects(2, hEventArray, FALSE, INFINITE);
if(nEvent == 0) { // Shutdown event
::OutputDebugString("Thread_func(): Shutdown event 1\n");
break;
}
else if(nEvent == 1) { // Second shutdown event
::OutputDebugString("Thread_func(): Shutdown event 2\n");
break;
}
else {
// Unknown event
} // end if
} // end while(1)
// Leave thread
::OutputDebugString("Thread_func(): Leave thread function\n");
SAFE_CLOSE_HANDLE(p->_h_thread);
return 666;
}
(Здесь _h_shutdown_event - это тоже ивент закрытия потока, только он создается внутри класса. Сначала я только его использовал, потом решил попробовать еще и внешний ивент. Убирать _h_shutdown_event пробовал - не помогает.)
А засада в том, что если поток закрывать из основной программы, то все прекрасно (я кнопочку завел для рестарта потока). Но если поток пытается закрыться перед выгрузкой DLL-ки, то это не работает. Хендл ивента нормальный, SetEvent(hShutdownEvent) отрабатывает без ошибки, а функция потока этого события не видит и, соответственно, из цикла не выходит. Вот отладочный лог:
...
'Thread_probe_main.exe': Loaded 'C:\WINDOWS\system32\imm32.dll'
'Thread_probe_main.exe': Loaded 'C:\WINDOWS\system32\winmm.dll'
'Thread_probe_main.exe': Loaded 'C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll'
Thread_func(): Start thread function <=== Поток запустился при старте DLL-ки
'Thread_probe_main.exe': Loaded 'C:\WINDOWS\system32\uxtheme.dll'
'Thread_probe_main.exe': Loaded 'C:\Program Files\Dexpot\hooxpot.dll'
'Thread_probe_main.exe': Loaded 'C:\WINDOWS\system32\MSCTF.dll'
Thread_func(): Shutdown event 1 <=== Поток закрылся при нажатии в приложении кнопки перезапуска и ...
Thread_func(): Leave thread function
The thread 'Win32 Thread' (0xa48) has exited with code 666 (0x29a).
Thread_func(): Start thread function <=== ... снова запустился
Thread_func(): Shutdown event 1
Thread_func(): Leave thread function
The thread 'Win32 Thread' (0xbd4) has exited with code 666 (0x29a).
Thread_func(): Start thread function
Thread_func(): Shutdown event 1
Thread_func(): Leave thread function
The thread 'Win32 Thread' (0xe2c) has exited with code 666 (0x29a).
Thread_func(): Start thread function
The thread 'Win32 Thread' (0x62c) has exited with code 0 (0x0). <=== А вот тут я закрыл приложение, SetEvent сработал, но поток этого не увидел
The program '[2208] Thread_probe_main.exe: Native' has exited with code 0 (0x0).
Что за странности? Ведь DLL-ка сама создала ивент, сама его взводит, а её родной поток собственный кровный ивент видеть перестает... Такое чувство, что закрытие основного приложения каким-то образом "выключает" механизм обработки событий...
Подскажите, пожалуйста, как это побороть? Уж часом не SECURITY_ATTRIBUTES-ли тут воду мутят?... А я с ними совсем "не дружу" и что делать - без понятия...
P.S. Не знаю, важно ли это: я создаю поток функцией CreateThread. Вот так:
// Create new thread
_h_thread = CreateThread(NULL,0,Thread_func,this,0,&_dw_thread_id);
P.P.S. Заметил еще одну странность. Когда приложение работает, то VS в отладочном окне Threads показывает два потока (останов в функции потока):
0 2608 Main Thread Main Thread CWnd::RunModalLoop Normal 0
0 > 2256 Worker Thread Thread_func Thread_func Normal 0
А когда я закрываю приложение, то остается только основной поток (останов в точке DLL_PROCESS_DETACH):
0 2608 Main Thread Main Thread CWnd::RunModalLoop Normal 0
Кто и почему закрыл мой Worker Thread?! Видимо именно здесь собака порылась, да?!