Windows程序设计——终止进程运行

来源:互联网 发布:怎样开情趣用品淘宝店 编辑:程序博客网 时间:2024/06/15 19:33

Windows程序设计——终止进程运行
  

    终止进程程的运行的四种方法:
    1.主线程的进入点函数返回。
    始终都应该这样来设计应用程序,即只有当主线程的进入点函数返回时,它的进程才终止运行。这是保证所有线程资源能够得到正确清除的唯一办法。
    让主线程的进入点函数返回,可以确保下列操作的实现:
    • 该线程创建的任何C + +对象将能使用它们的析构函数正确地撤消。
    • 操作系统将能正确地释放该线程的堆栈使用的内存。
    • 系统将进程的退出代码(在进程的内核对象中维护)设置为进入点函数的返回值。
    • 系统将进程内核对象的返回值递减1。
    2.进程中的一个线程调用ExitProcess函数。
    当进程中的一个线程调用E x i t P r o c e s s函数时,进程便终止运行:  

       VOID ExitProcess(UINT fuExitCode)

    该函数用于终止进程的运行,并将进程的退出代码设置为fuExitCode。ExitProcess函数并不返回任何值,因为进程已经终止运行。如果在调用ExitProcess之后又增加了什么代码,那么该代码将永远不会运行。
当主线程的进入点函数( Winmain、wWinmain、main或wmain)返回时,它将返回给C/C++运行期启动代码,它能正确地清除该进程使用的所有的C运行期资源。当C运行期资源被释放之后,C运行期启动代码就显式调用ExitProcess,并将进入点函数返回的值传递给它。这解释了为什么只需要主线程的进入点函数返回,就能够终止整个进程的运行。请注意,进程中运行的任何其他线程都随着进程而一道终止运行。
Windows Platform SDK文档声明,进程要等到所有线程终止运行之后才终止运行。就操作系统而言,这种说法是对的。但是, C/C++运行期对应用程序采用了不同的规则,通过调用ExitProcess,使得C/C++运行期启动代码能够确保主线程从它的进入点函数返回时,进程便终止运行,而不管进程中是否还有其他线程在运行。不过,如果在进入点函数中调用ExitThread,而不是调用ExitProcess或者仅仅是返回,那么应用程序的主线程将停止运行,但是,如果进程中至少有一个线程还在运行,该进程将不会终止运行。注意,调用ExitProcess或ExitThread可使进程或线程在函数中就终止运行。就操作系统而言,这很好,进程或线程的所有操作系统资源都将被全部清除。但是, C/C++应用程序应该避免调用这些函数,因为C/C++运行期也许无法正确地清除。请看下面的代码:
  

#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);
}

    将上面的代码运行,将会看到:
    Constructor
    Constructor
    它创建了两个对象,一个是全局对象,另一个是局部对象。不过决不会看到Destructor这个单词出现, C++对象没有被正确地撤消,因为ExitProcess函数强制进程在现场终止运行,C/C++运行期没有机会进行清除。
如前所述,决不应该显式调用ExitProcess函数。如果在上面的代码中删除了对ExitProcess的调用,那么运行该程序产生的结果如下:
    Constructor
    Constructor
    Destructor
    Destructor
    只要让主线程的进入点函数返回, C/C++运行期就能够执行它的清除操作,并且正确地撤消任何或所有的C++对象。顺便讲一下,这个说明不仅仅适用于C++对象。C++运行期能够代表进程执行许多操作,最好允许运行期正确地将它清除。
    注意:显式调用ExitProcessExitThread是导致应用程序不能正确地将自己清除的常见原因。在调用ExitThread时,进程将继续运行,但是可能会泄漏内存或其他资源。
    3.另一个进程中的线程调用TerminateProcess函数。
    调用TerminateProcess函数也能够终止进程的运行:   

   BOOL TerminateProcess(HANDLE hProcess,
         UINT fuExitCode);

    该函数与ExitProcess有一个很大的差别,那就是任何线程都可以调用TerminateProcess来终止另一个进程或它自己的进程的运行。hProcess参数用于标识要终止运行的进程的句柄。当进程终止运行时,它的退出代码将成为你作为fuExitCode参数来传递的值。
    只有当无法用另一种方法来迫使进程退出时,才应该使用TerminateProcess。终止运行的进程绝对得不到关于它将终止运行的任何通知,因为应用程序无法正确地清除,并且不能避免自己被撤消(除非通过正常的安全机制)。例如,进程无法将内存中它拥有的任何信息迅速送往磁盘。
    虽然进程确实没有机会执行自己的清除操作,但是操作系统可以在进程之后进行全面的清除,使得所有操作系统资源都不会保留下来。这意味着进程使用的所有内存均被释放,所有打开的文件全部关闭,所有内核对象的使用计数均被递减,同时所有的用户对象和GDI对象均被撤消。
    一旦进程终止运行(无论采用何种方法),系统将确保该进程不会将它的任何部分遗留下来。绝对没有办法知道该进程是否曾经运行过。进程一旦终止运行,它绝对不会留下任何蛛丝马迹。
    注意:TerminateProcess函数是个异步运行的函数,也就是说,它会告诉系统,你想要进程终止运行,但是当函数返回时,你无法保证该进程已经终止运行。因此,如果想要确切地了解进程是否已经终止运行,必须调用WaitForSingleObject()函数或者类似的函数,并传递进程的句柄。
    进程中的线程何时全部终止运行?
    如果进程中的所有线程全部终止运行(因为它们调用了ExitProcess函数,或者因为它们已经用TerminateProcess函数终止运行),操作系统就认为没有理由继续保留进程的地址空间。这很好,因为在地址空间中没有任何线程执行任何代码。当系统发现没有任何线程仍在运行时,它就终止进程的运行。出现这种情况时,进程的退出代码被设置为与终止运行的最后一个线程相同的退出代码。
    进程终止运行时出现的情况
    当进程终止运行时,下列操作将启动运行:
    1) 进程中剩余的所有线程全部终止运行。
    2) 进程指定的所有用户对象和G D I对象均被释放,所有内核对象均被关闭(如果没有其他
进程打开它们的句柄,那么这些内核对象将被撤消。但是,如果其他进程打开了它们的句柄,
内核对象将不会撤消)。
    3) 进程的退出代码将从STILL_ACTIVE改为传递给ExitProcess或TerminateProcess的代码。
    4) 进程内核对象的状态变成收到通知的状态,系统中的其他线程可以挂起,直到进程终止运行。
    5) 进程内核对象的使用计数递减1。
    注意:进程的内核对象的寿命至少可以达到进程本身那么长,但是进程内核对象的寿命可能大大超过它的进程寿命。当进程终止运行时,系统能够自动确定它的内核对象的使用计数。如果使用计数降为0,那么没有其他进程拥有该对象打开的句柄,当进程被撤消时,对象也被撤消。不过,如果系统中的另一个进程拥有正在被撤消的进程的内核对象的打开句柄,那么该进程内核对象的使用计数不会降为0。当父进程忘记关闭子进程的句柄时,往往就会发生这样的情况。这是个特性,而不是错误。记住,进程内核对象维护关于进程的统计信息。即使进程已经终止运行,该信息也是有用的。例如,你可能想要知道进程需要多少C P U时间,或者,你想通过调用GetExitCodeProcess来获得目前已经撤消的进程的退出代码:   

BOOL GetExitCodeProcess(
    HANDLE hProcess,
    PDWORD pwdExitCode);

    该函数查看进程的内核对象(由hProcess参数来标识),取出内核对象的数据结构中用于标识进程的退出代码的成员。该退出代码的值在pwdExitCode参数指向的DWORD中返回。
    可以随时调用该函数。如果调用GetExitCodeProcess函数时进程尚未终止运行,那么该函数就用STILL_ACTIVE标识符(定义为0x103)填入DWORD。如果进程已经终止运行,便返回数据的退出代码值。
也许你会认为,你可以编写代码,通过定期调用GetExitCodeProcess函数并且检查退出代码来确定进程是否已经终止运行。大多数情况下,这是可行的,但是效率不高。
    再一次提醒,应该通过调用CloseHandle函数,告诉系统你对进程的统计数据已经不再感兴趣。如果进程已经终止运行,CloseHandle将递减内核对象的使用计数,并将它释放。
    4.进程中的所有线程自行终止运行。
    这是最理想的情况,基本上是不会发生的。
 http://blog.chinaunix.net/u1/53730/showart_498278.html

原创粉丝点击