A process can be
This section discusses all four
You should always design an application so that its process terminates only when your primary thread's entry-point function returns. This is the only way to guarantee that all your primary thread's resources are cleaned up properly.
Having your primary thread's entry-point function return ensures the following:
A process terminates when one of the threads in the process calls ExitProcess :
VOIDExitProcess(UINTfuExitCode); |
This function terminates the process and sets the exit code of the process to fuExitCode . ExitProcess doesn't return a value because the process has terminated. If you include any code following the call to ExitProcess , that code will never execute.
When your primary thread's entry-point function ( WinMain, wWinMain, main, or wmain ) returns, it returns to the C/C++ run-time startup code, which properly cleans up all the C run-time resources used by the process. After the C run-time resources have been freed, the C run-time startup code explicitly calls ExitProcess , passing it the value returned from your entry-point function. This explains why simply returning from your primary thread's entry-point function terminates the entire process. Note that any other threads running in the process terminate along with the process.
The
Note that calling
ExitProcess
or
ExitThread
causes a process or thread to die while inside a function. As far the operating system is
#include<windows.h>
#include<stdio.h>
classCSomeObj{
public:
CSomeObj(){printf("Constructor\r\n");}
~CSomeObj(){printf("Destructor\r\n");}
};
CSomeObjg_GlobalObj;
voidmain(){
CSomeObjLocalObj;
ExitProcess(0); //Thisshouldn'tbehere
//Attheendofthisfunction,thecompilerautomaticallyadded
//thecodenecessarytocallLocalObj'sdestructor.
//ExitProcesspreventsitfromexecuting.
}
|
When the code above executes, you'll see:
Constructor Constructor |
Two objects are being
As I said, you should never call ExitProcess explicitly. If I remove the call to ExitProcess in the code above, running the program yields this:
Constructor Constructor Destructor Destructor |
By simply allowing the primary thread's entry point function to return, the C/C++ run time can perform its cleanup and properly destruct any and all C++ objects. By the way, this discussion does not apply only to C++ objects. The C/C++ run time does many things on
NOTE
Making explicit calls to ExitProcess and ExitThread is a common problem that causes an application to not clean itself up properly. In the case of ExitThread , the process continues to run but can leak memory or other resources.
A call to TerminateProcess also ends a process:
BOOLTerminateProcess(HANDLEhProcess, UINTfuExitCode); |
This function is different from ExitProcess in one major way: any thread can call TerminateProcess to terminate another process or its own process. The hProcess parameter identifies the handle of the process to be terminated. When the process terminates, its exit code becomes the value you passed as the fuExitCode parameter.
You should use
TerminateProcess
only if you can't force a process to exit by using another method. The process being terminated is given
While it is true that the process will not have a chance to do its own cleanup, the operating system does clean up completely after the process so that no operating system resources
Once a process terminates (no matter how), the system
NOTE
The TerminateProcess function is asynchronous—that is, ittells the system that you want the process to terminate but the process is notguaranteed to be killed by the time the function returns. So you might want to call WaitForSingleObject (described in Chapter 9) or a similar function, passing the handle of the process if you need to know for sure that the process has terminated.
If all the threads in a process die (either because they've all called ExitThread or because they've been terminated with TerminateThread ), the operating system assumes that there is no reason to keep the process's address space around. This is a fair assumption, since there are no more threads executing any code in the address space. When the system detects that no threads are running any more, it terminates the process. When this happens, the process's exit code is set to the same exit code as the last thread that died.
When a process terminates, the following actions are set in motion:
Note that a process's kernel object always lives at least as long as the process itself. However, the process kernel object might live well beyond its process. When a process terminates, the system automatically decrements the usage count of its kernel object. If the count goes to 0, no other process has an open handle to the object and the object is destroyed when the process is destroyed.
However, the process kernel object's count will not go to 0 if another process in the system has an open handle to the dying process's kernel object. This usually happens when parent processes forget to close their handle to a child process. This is a feature, not a bug. Remember that the process kernel object maintains statistical information about the process. This information can be useful even after the process has terminated. For example, you might want to know how much CPU time the process required. Or, more likely, you might want to obtain the now-
BOOLGetExitCodeProcess(HANDLEhProcess, PDWORDpdwExitCode); |
This function looks into the process kernel object (identified by the
hProcess
parameter) and
You can call this function at any time. If the process hasn't terminated when GetExitCodeProcess is called, the function fills the DWORD with the STILL_ACTIVE identifier (defined as 0x103). If the process has terminated, the actual exit code value is returned.
You might think that you can write code to determine whether a process has terminated by calling GetExitCodeProcess periodically and checking the exit code. This would work in many situations, but it would be inefficient. I'll explain the proper way to determine when a process has terminated in the next section.
Once again, let me