Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
375
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

DWORD dwExitCode;

HANDLE hThreatf = _beglntnread(...);

GetExitCodeThread(hThread &dwExitCode);

CloseHandle(hThread);

Весьма вероятно, что созданный поток отработает и завершится еще до того, как первый поток успеет вызвать функцию GetExitCodeThread. Если так и случится, значе ние в hThread окажется недействительным, потому что _endtbread уже закрыла опи сатель нового потока. И, естественно, вызов CloseHandle дает ошибку.

Новая функция _endthreadex, не закрывает описатель потока, поэтому фрагмент кода, приведенный выше, будет нормально работать (если мы, конечно, заменим вы зов _beginthread на вызов _beginthreadex) И в заключение, напомню еще раз: как толь ко функция потока возвращает управление, _beginthreadex самостоятельно вызывает

_endthreadex, a begtnthreadобращается к_endthread.

Как узнать о себе

Потоки часто обращаются к Windows-функциям, которые меняют срсду выполнения. Например, потоку может понадобиться изменить свой приоритет или приоритет процесса. (Приоритеты рассматриваются в главе 7.) И поскольку это не редкость, когда поток модифицирует среду (собственную или процесса), в Windows предусмот рены функции, позволяющие легко ссылаться на объекты ядра текущего процесса и потока:

HANDLE GetCurrentProcess();

HANDLE GetCurrentThread();

Обе эти функции возвращают псевдоописатсль объекта ядра "процесс" или "по ток" Они не создают новые описатели в таблице описателей, которая принадлежит вызывающему процессу, и не влияют на счетчики числа пользователей объектов ядра "процесс» и "поток" Поэтому, если вызвать CloseHandle и передать ей псевдоописа тель, она проигнорирует вызов и просто вернет FALSE

Псевдоописатели можно использовать при вызове функций, которым нужен опи сатель процесса Так, поток может запросить все временные показатели своего про цесса, вызвав

GetProcessTimes:

FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime; GetProcessTimes(GetCurrentProcess(), &ftCreationTime, &ftExirTime, &ftKernelTime, &ftUserTime);

Аналогичным образом поток может выяснить собственные временные показате ли, вызвав

GetThreadTimes:

FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime; GetThreadTimes(GetCurrentThread(), &ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime);

Некоторые Windows-функции позволяют указывать конкретный процесс или по ток no его уникальному в рамках всей системы идентификатору. Вот функции, с по мощью которых поток может выяснить такой идентификатор — собственный или своего процесса:

DWORD GetCurrentProcessId();

DWORD GelCurrentThreadId();

По сравнению с функциями, которые возвращают псевдоописатели, эти функции, как правило, не столь полезны, но когда-то и они могут пригодиться.

Преобразование псевдоописателя в настоящий описатель

Иногда бывает нужно выяснить настоящий, а не псевдоописатель потока. Под "насто ящим" я подразумеваю описатель, который однозначно идентифицирует уникальный поток Вдумайтесь в такой фрагмент кода:

DWORD WINAPI ParentThread(PVOID pvParam)

{

HANDLE hThreadParent = GetCurrentThread(); CreateThread(NULL, 0, ChildThread, (PVOID) hThreadParent, 0, MULL);

// далее следует какой-то код

}

DWORD WINAPI ChildThread(PVOID pvParam)

{

HANDLE hThreadParent = (HANDLE) pvParam;

FILETIME ftCreationTime, ftExitTime, ftKernelTime, ftUserTime;

GetTh readTimes(hThreadParent, &ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime);

// далее следует какой-ro код.

}

Вы заметили, чго здесь не все ладно. Идея была в том, чтобы родительский поток передавал дочернему свой описатель. Но он передает псевдо-, а не настоящий описа тель Начиная выполнение, дочерний поток передает этот псевдоописатель функции GetThreadTimes, и она вследствие этого возвращает временные показатели своего — а вовсе не родительского потока. Происходит так потому, что псевдоописатель яв ляется описателем текущего потока, т e. того, который вызывает эту функцию.

Чтобы исправить приведенный выше фрагмент кода, превратим псевдоописатель в настоящий через функцию DuplicateHandle (о ней я рассказывал в главе 3):

BOOL DuplicateHandle( HANDLE hSourceProcess, HANDLE hSource, HANDLE hTargetProcess, PHANDLE phTarget, DWORD fdwAccess, BOOL bInhentHandle, DWORD fdwOpfions),

Обычно она используется для создания нового "процессо-зависимого» описателя из описателя объекта ядра, значепие которого увязано с другим процессом. А мы вос пользуемся DuplicateHandle не совсем по назначению и скорректируем с ее помощью наш фрагмент кода так

DWORD WINAPI ParentThread(PVOID pvParam)

{

HANDLE hThreadParent;

DuplicateHandle(

GetCurrentProcebs(), // описатель процесса, к которому относится псевдоописатель потока,

GetCurrentThread(), // псевдоописатель родительского потока; GetCurrentProcess(), // описатель процесса, к которому относится новый, настоящий описатель потока

&hThreadParent, // даст новый настоящий описатель идентифицирующий родительский поток;

0, // игнорируется из-за DUPLICATE_SAME_ACCESS FALSE, новый описатель потока ненаследуемый, DUPLICATE_SAME_ACCESS); // новому описателю потока присваиваются те же атрибуты защиты, что и псевдоописателю

CreateThread(NULL, 0, ChildThread, (PVOID) hThreadParent, 0,

NULL) ;

// далее следует какой-то код

}

DWORD WINAPI ChildThread(PVOID pvParam)

{

HANDLE hThreadParent = (HANDLE) pvParam;

FILETIME ftCreaUonTime, ftExitTime, ftKernelTime, ftUserTime;

GetThreadTimes(hThreadParent, &ftCreationTime, &ftExitTime, &ftKernelTime, &ftUserTime);

CloseHandle(hThreadParent);

// далее следует какой-то код..

}

Тeпeрь родительский поток преобразует свой "двусмысленный» псевдоописатель в настоящий описатель, однозначно определяющий родительский поток, и передает его в CreateThread Когда дочерний поток начинает выполнение, его параметр pvParam содержит настоящий описатель потока. В итоге вызов какой-либо функции с этим описателем влияет не на дочерний, а на родительский поток

Поскольку DuplicateHandle увеличивает счетчик пользователей указанного объек та ядра, то, закончив работу с продублированным описателем объекта, очень важно не забыть уменьшить счетчик Сразу после обращения к GetThreadTimes дочерний поток вызывает CloseHandle, уменьшая тем самым счетчик пользователей объекта "ро дительский поток" на 1 В этом фрагменте кода я исходил из того, что дочерний по ток не вызывает других функций с передачей этого описателя Если же ему надо выз вать какие-то функции с передачей описателя родительского потока, то, естествен но, к CloseHandle следует обращаться только после тоoro, как необходимость в этом описателе у дочернего потока отпадет

Надо заметить, что DuphcateHandle позволяет преобразовать и псевдоописатель процесса. Вот как это сделать

HANDLE hProcess;

DuplicateHandle(

GetCurrentProcess(), // описатель процесса, к которому относится