CreateThread создаёт несколько трэдов, хэндлы сохраняются в сухом и прохладном месте. Далее, в какой-то момент, любой из трэдов вызавает код (реально это UnhandledExceptionFilter), который читает хэндлы из сухого и прохладного места, и для каждого вызывает TerminateThread. Тут-то и случается страшное Манипуляции с DuplicateHandle как у Рихтера не подходят - возвращается другой хэндл. Как я успел понять (официальных) путей получения именно того хэндла, который возвращает CreateThread нет ? В Windows Server 2003 появилась GetThreadId, можно сравнивать возвращаемое значение с GetCurrentThreadId. Но хочется, что бы код работал так же и в 95-м мастдае :-( Манипулировать с tls или pvArbitrary ( dword FS:[14h]) ? Пока что придумал - GetThreadContext и сравнивать CONTEXT.Esp на предмет попадания в диапазон FS:[4h] и FS:[8h] Код (Text): __declspec(naked) int __fastcall safe_terminate_thread(void * thread) { static CONTEXT context; __asm { // ecx = handle to thread mov context.ContextFlags, CONTEXT_CONTROL push 0 // для TerminateThread push ecx // для TerminateThread push offset context push ecx call dword ptr GetThreadContext xor edx, edx mov eax, context.Esp cmp dword ptr fs:[edx+4], eax // Top of user stack setb cl cmp dword ptr fs:[edx+8], eax // Base of user stack setae ch test cx, cx jz current_thread call dword ptr TerminateThread ret current_thread: pop ecx // убираем параметры TerminateThread pop ecx ret } } Есть ли какие-нибудь другие варианты?
Можно запоминать ThreadID при создании потока: Код (Text): #include <windows.h> struct ThreadInfo { DWORD ThreadID; HANDLE ThreadHandle; }; #define NUM_OF_THREADS 8 ThreadInfo ti[NUM_OF_THREADS]; ULONG __stdcall ThreadProc(LPVOID); void TerminateAll(); HANDLE hEvent; int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int) { hEvent = CreateEvent(0, TRUE, FALSE, 0); for(int i = 0; i < NUM_OF_THREADS; i++) ti[i].ThreadHandle = CreateThread(0, 0, ThreadProc, 0, 0, &ti[i].ThreadID); WaitForSingleObject(hEvent, -1); return 0; } ULONG __stdcall ThreadProc(LPVOID) { Sleep((GetTickCount() & 7) * 1000); TerminateAll(); SetEvent(hEvent); return 0; } void TerminateAll() { DWORD currentID = GetCurrentThreadId(); for(int i = 0; i < NUM_OF_THREADS; i++) if(currentID != ti[i].ThreadID) TerminateThread(ti[i].ThreadHandle, 0); }
Не понял. Ты хочеш чтоб один поток не мог убить другой чтоли?. Вообще то DuplicateHandle используется для совместного использования объектов ядра несколькими процессами. Как это возвращается другой описатель
S_T_A_S_ Можно при создании потока передать ему указатель на ячейку где будет сохранён его хэндл, или сразу же после создания отправить созданному потоку сообщение содержащее этот хендл, а он его уже извлечёт их своей очереди.
AFAIK, handle полученный в одном потоке, валиден в любом другом того же процесса. за исключением pseudo handle конечно.
vinnie_pooh > Это слишком простое решение, воины дзена не ищут лёгких путей Я немного упростил ситуацию, на самом деле трэды завершаются не в одном месте (цикле): При нормальном ходе работы первичный трэд вызывает всякие "деструкторы", а в тех код освобождения ресурсов и т.п. + TerminateTread. Появление фильтра исключений "немного" портит эту картину. Поэтому при твоём подходе нужно будет переделывать все эти места в проге, код создания трэдов + добавлять переменные. А я хочу "глобальное решение" вроде такой строки в начале программы: #define TerminateTread(X,Y) safe_terminate_thread(X) В общем-то я его нашел, просто оно немного через э-э-э.. ну, короче, контекст =) linker > См. сабж. Мне нужно что бы произвольный трэд закрывая все остальные "случайно" не завершил свою работу - как узнать, что некоторый хэндл принадлежит конкретному потоку? > Классический пример - у Рихтера - "дублируется" псевдохендл возвращаемый GetCurrentThread - получается хэндл, которым может оперировать другой поток. Но этот хэндл (численно) не равен тому, что возвращает CreateThread. green Но как я узнаю, что какойто хэндл - именно текущего потока? В филтр может попасть произвольный поток!
Похоже, я плохо объяснил: трэд А создаёт трэды B и C. программа работает трэд А завершает трэды B и C. ------------------------------------- Но может возникнуть exeption! тогда либо трэд A, либо B, либо C попадыют в UnhandledExceptionFilter и либо трэд A, либо B, либо C пытаются завершить трэды B и C Какова вероятность, трэды B и C дойдут до этого места?!
ок, я просто не понял проблемы... ну может тогда "в сухом и прохладном месте" сохранять не хэндлы а id потоков ? правда в фильтре придется делать OpenThread но такой фильтр ведь должен срабатывать редко ?
можно конечно, vinnie_pooh так и предлагал - но IMHO это в решение более сложное, чем то, которое уже есть. Проще же залезть в TIB, чем переделывать прогу
да, наверно... только напрямую лезть в TIB, мне кажется, не обязательно тем более что он недокументирован. можно определить что хэндл является хэндлом текущего треда, сравнивая fs из его контекста с текущим значением fs.
FS не будет работать. вот что пишет Питрек: Фактически, я и делаю подобное сравнение.. каждый трэд имеет свой стэк, следовательно esp можно рассматривать как его "уникальный" идентификатор.. теоретически, эта схама не полностью надёжна, но работает на практике. Просто подумал, может есть ещё какие-то "зацепки"
опс, не знал... хотя оно и логично: кол. потоков может перевалить за макс. кол. дескрипторов сегментов. а линк на эту статью Питрека можешь дать ?
Что-то я в замешательстве: если esp выходит за пределы FS:[8] ... FS:[4] то safe_terminate_thread благополучно завершит трэд.. С другой стороны, в таком случае фильтр исключений всё равно не будет вызван. Т.е. если делать: xor esp, esp push esp система молча прибивает процесс. Получается, что ошибка в методе есть, но сама система не даёт ей проявляться! Хотя правильнее видимо делать GetThreadSelectorEntry для хэндла трэда и для GetCurrentThread, и потом сравнивать LDT_ENTRY структуры..
т.е переводить в линейный адрес через GetThreadSelectorEntry.. ну это можно, но телодвижений больше, чем уже есть. На всякий случай, значения fs из контекстов для разных систем: Код (Text): #include <windows.h> #include <stdio.h> unsigned long __stdcall thread(void * foo) { Sleep(-1); return 0; } void main() { CONTEXT context; for( int i = 5; i; --i ) { unsigned long id; void * handle = CreateThread(0, 0, thread, 0, 0, &id); Sleep(2); // для мастдая context.ContextFlags = CONTEXT_SEGMENTS | CONTEXT_CONTROL; GetThreadContext(handle, &context); printf("Thread #%08X : CONTEXT.SegFs = %08X\tCONTEXT.Esp = %08X\n", handle, context.SegFs, context.Esp); } } А вот результаты работы: Код (Text): 98se: Thread #00000014 : CONTEXT.SegFs = 0000252F CONTEXT.Esp = 0096FF90 Thread #00000018 : CONTEXT.SegFs = 0000377F CONTEXT.Esp = 00A8FF90 Thread #0000001C : CONTEXT.SegFs = 000037CF CONTEXT.Esp = 00BAFF90 Thread #00000020 : CONTEXT.SegFs = 00003767 CONTEXT.Esp = 00CCFF90 Thread #00000024 : CONTEXT.SegFs = 000037D7 CONTEXT.Esp = 00DEFF90 XP: Thread #000007E8 : CONTEXT.SegFs = 0000003B CONTEXT.Esp = 0050FF40 Thread #000007F4 : CONTEXT.SegFs = 0000003B CONTEXT.Esp = 0060FF40 Thread #000007DC : CONTEXT.SegFs = 0000003B CONTEXT.Esp = 0070FF40 Thread #000007D8 : CONTEXT.SegFs = 0000003B CONTEXT.Esp = 0080FF40 Thread #000007D4 : CONTEXT.SegFs = 0000003B CONTEXT.Esp = 0090FF40 esp можно сразу использовать