Функция ExitProcess

Процесс завершается, когда один из его потоков вызывает ExitProcess:

VOID ExilProcess(UINT fuExitCode);

Эта функция завершает процесс и заносит в параметр fuExitCode код завершения процесса. Возвращаемого значения у ExitProcess нет, так как результат ее действия — завершение процесса. Если за вызовом этой функции в программе присутствует какой-нибудь код, он никогда не исполняется.

Когда входная функция (WinMain, wWinMain, main или wmain) в программе возвращает управление, оно передастся стартовому коду из библиотеки С/C++, и тот проводит очистку всех ресурсов, выделенных им процессу, а затем обращается к ExitProcess, передавая ей значение, возвращенное входной функцией. Вот почему возврат управления входной функцией первичного потока приводит к завершению всего процесса. При завершении процесса прекращается выполнение и всех других его потоков.

В документации из Platform SDK утверждается, что процесс не завершает ся до тех пор, пока не завершится выполнение всех его потоков. Это, конечно, верно, но тут есть одна тонкость. Стартовый код из библиотеки С/С++ обеспечивает завершение процесса, вызывая ExitProcess после того, как первичный поток приложения возвращается из входной функции. Однако, при вызове из нее функции ExitThread (вместо того чтобы вызвать ExitProcess или просто вернуть управление), произойдет завершение первичного потока, но не самого процесса — если в нем еще выполняется какой-то другой поток (или потоки).

Такой вызов ExitProcess или ExitThread приводит к уничтожению процесса или потока, когда выполнение функции еще не завершилось. Что касается операционной системы, то здесь все в порядке: она корректно очистит все ресурсы, выделенные процессу или потоку. Но в приложении, написанном на С/С++, следует избегать вызова этих функций, так как библиотеке С/С++ скорее всего не удастся провести должную очистку.

Далее приведен код, иллюстрирующий данную ситуацию:

#include <windows.h>
#include <stdio.h>

class CSomeObj {
public:
CSomeObj() { printf("Constructor\r\n"), }
~CSomeObj() { printf("Destructor\r\n"); }
};

CSomeObj g_GlobalObj;

void main () {
CSomeObj LocalObj;
ExitProcess(0); // этого здесь не должно быть

// в конце этой функции компилятор автоматически вставил код // для вызова деструктора LocalObj, но ExitProcess не дает его выполнить }

При его выполнении увидим:

Constructor
Constructor

Код конструирует два объекта: глобальный и локальный. Но Вы никогда не увидите строку Destructor С++-объекты не разрушаются должным образом из-за того, что ExitProcess форсирует уничтожение процесса и библиотека С/С++ не получает шанса на очистку.

Не следует никогда вызывать ExitProcess в явном виде. Если убрать из предыдущего примера вызов ExitProcess, программа выведет такие строки:

Constructor
Constructor

Destructor
Destructor

Простой возврат управления от входной функции первичного потока позволил библиотеке С/С++ провести нужную очистку и корректно разрушить С++-объекты. Все это относится не только к объектам, но и ко многим другим вещам, которые библиотека С/С++ делает для процесса.

Замечание:
Явные вызовы ExitProcess и ExitThread — распространенная ошибка, которая мешает правильной очистке ресурсов. В случае ExitThread процесс продолжает работать, но при этом весьма вероятна утечка памяти или других ресурсов.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: