标签:com 必须 而不是 客户 and line 时间 标识 water
Windows程序设计:进程该函数用于终止进程的运行,并将进程的退出代码设置为参数f u E x i t C o d e的值。E x i t P r o c e s s函数并不返回任何值,这是因为进程已经被终止运行。如果在调用E x i t P r o c e s s之后又增加了什么代码,那么添加代码将不可能获得执行的机会。
当主线程的入口点函数( WinMain、wWinMain、main或wmain)返回时,它将返回给C/C++运行时启动代码,它能正确地清除该进程使用的所有的C运行时资源。当C运行时资源被释放之后,C运行时启动代码就显式调用E x i t P r o c e s s函数,并将入口点函数返回的值传递给它。因此,我们只需要在主线程入口点返回,就能终止当前进程。请注意,进程中运行的任何其他线程都随着进程的终止而终止。
Windows的SDK编程手册指出,进程要等到所有线程终止运行之后才终止运行。就操作系统而言,这是正确的。但是, C/C++运行时对应用程序采用了不同的规则,通过调用E x i t P r o c e s s,使得C/C++运行时启动代码能够确保主线程从它的入口点函数返回时,进程便终止运行,而不管进程中是否还有其他线程在运行。不过,如果在入口点函数中调用E x i t T h r e a d,而不是调用E x t i P r o c e s s或者仅仅是返回,那么应用程序的主线程将停止运行。此时,不难发现,ExitProcess仅仅退出了当前线程,而非当前进程。如果当前进程中至少有一个线程还在运行,该进程将不会终止运行。
在调用E x i t P r o c e s s或E x i t T h r e a d可使进程或线程在函数中就终止运行。就操作系统而言,这没有问题。进程或线程的所有操作系统资源都将被全部清除。但是, C/C++应用程序应该避免调用这些函数,因为C/C++运行时可能无法被全部清除,例如:
#include <windows.h>
#include <stdio.h>
class CMyObject
{
public:
CMyObject()
{
printf("Constructor\r\n");
}
~CMyObject()
{
printf("Destructor\r\n");
}
};
CMyObject g_GlobalObj;
void main()
{
CMyObject LocalObj;
ExitProcess(0);
}
接下来,我们转到对应的文件目录下面,在项目上右键弹出菜单,选中在文件资源管理器中打开文件夹,
获得文件的目录,
然后打开命令行工具,
使用cd 命令,切换到debug文件夹,运行exe文件,
我们将会看到:
Constructor
Constructor
这个应用创建了两个对象,一个是全局对象,另一个是局部对象。不过我们一定不会看到Destructor这个单词出现, C++对象没有被正确地撤消,因为E x i t P r o c e s s函数强制当前进程终止运行,C/C++运行时没有机会进行调用析构函数释放资源。
这告诉我们,当调用E x i t P r o c e s s函数必须慎重!如果在上面的代码中删除了对E x i t P r o c e s s(0)这行代码,那么再次运行程序,我们可以得到如下结果:
Constructor
Constructor
Destructor
Destructor
只要让主线程的入口点函数返回, C/C++运行时就能够执行清理工作,并且正确地撤消任何或所有的C++对象。
显式调用E x i t P r o c e s s和E x i t T h r e a d是导致应用程序不能正确清理的常见原因。在调用E x i t T h r e a d时,进程可能将继续运行,但是可能会泄漏内存或其他资源。
6进程终止后操作系统的工作
当进程被终止时,操作系统将完成下列工作:
1) 进程中剩余的所有线程将全部被终止运行。
2) 进程指定的所有用户对象和GDI对象均被释放,所有内核对象均被关闭(如果没有其他进程关联到这些句柄,那么这些内核对象将被撤消。但是,如果存在其他进程关联到这些句柄, 内核对象将不会撤消)。
3) 进程的退出代码将从S T I L L A C T I V E改为传递给E x i t P r o c e s s或Te r m i n a t e P r o c e s s的代码。
4) 进程内核对象的状态变成受信的状态。进程中的其他线程被挂起,直到进程终止运行。
5) 进程内核对象的使用计数递减1。
注意,进程的内核对象的寿命一定不会低于进程本身的寿命,进程内核对象的寿命却有可能大大超过它的进程寿命。当进程终止运行时,系统能够自动确定它的内核对象的使用计数。如果使用计数降为0,那么没有其他进程关联到该对象,当进程被撤消时,内核对象也被撤消。
不过,如果系统中的另一个进程关联到正在被撤消的进程的内核对象的时候,那么该进程内核对象的使用计数不会降为0。当父进程忘记关闭子进程的句柄时,就会造成这种情况。进程内核对象维护了进程的统计信息。即使进程已经终止运行,该信息也是有用的。例如,当我们想要知道进程需要多少C P U时间,或者,通过调用G e t E x i t C o d e P r o c e s s来获得目前已经撤消的进程的退出代码:
BOOL GetExitCodeProcess(HANDLE hProcess,
PDWORD pdwExitCode);
该函数就是通过查看进程的内核对象(由h P r o c e s s参数来标识),取出内核对象的数据结构中用于标识进程的退出代码的成员。该退出代码的值在p d w E x i t C o d e参数指向的D W O R D中返回。
当调用G e t E x i t C o d e P r o c e s s函数时,如果进程运行尚未终止,那么该函数就用S T I L L A C T I V E标识符(定义为0 x 1 0 3)填入D W O R D。如果进程已经终止运行,便返回数据的退出代码值。
因此,为了保证内核对象被正确关闭,应该及时调用C l o s e H a n d l e函数,告诉系统你对进程的统计数据已经不再感兴趣,以便操作系统及时回收内核对象。如果进程已经终止运行,C l o s e H a n d l e将递减内核对象的使用计数,并将它释放。
相关视频可以观看
http://edu.51cto.com/course/12840.html
标签:com 必须 而不是 客户 and line 时间 标识 water
原文地址:http://blog.51cto.com/xiacaojun/2322509