使用ExitProcess退出时为什么还会报运行时错误

通常情况下会以为ExitProcess就是直接结束并退出进程,其实不然,根据MSDN说明,调用ExitProcess至少在用户态下有这些动作

1)除了调用ExitProcess的线程之外,其他线程均被结束,但线程使用的DLL不会接收到DLL_THREAD_DETACH事件(正常结束线程或释放DLL则会有DLL_THREAD_DETACH事件)!

2)所有被结束的线程均设置为有信号,这样意味着如果在某些地方调用WaitForSingleObject等待线程结束的,则会马上获得信号并结束等待

3)所有被加载的DLL,均会调用入库函数并接收DLL_PROCESS_DETACH事件,这样意味这在Delphi里,所有单元的finalization的代码会被执行,同时也会清理运行时相关环境(大部分是system.pas,sysinit.pas里的)

4)结束当前线程和进程,这里会调用系统内部的功能代码去释放一些被系统认为是系统的资源(各种句柄和被系统管理的内存等)

5)进程的状态从STILL_ACTIVE变为ExitCode,完成退出进程(但该进程其实在系统中仍然有相关的资源存在的,不然其他进程怎么获取到这个ExitCode呢?只有再进程最后一个句柄关闭时,该进程的对象才会被释放)

另外:

A. 如果在步骤1中被结束的线程中,如果在释放相关的DLL时又调用ExitProcess,也就是除了当前调用ExitProcess的线程,有其他线程也调用了ExitProcess,则会导致死锁。

B. 如果不是调用ExitProcess来结束进程,而是直接调用TerminateProcess,则相关的DLL不会被正常通知释放,也就是DLL的入口函数不会被以DLL_PROCESS_DETACH事件调用,这样也意味着Delphi里,所有单元的finalization的代码不会被执行,运行时相关环境也不会被释放!

在DLL中调用ExitProcess很容易导致一些错误,因为逻辑上讲,调用ExitProcess的DLL本身也会被释放调用,一旦该DLL的运行环境被释放(相当于自己释放自己),那正在执行的释放过程会很容易出现内存溢出或导致系统资源未正常释放而报错。除非在调用ExitProcess前已经释放了其他DLL和自身的资源,这时候才能最大限度保证不出错,而一般较为复杂的源码结构所编译的DLL,想要正确释放资源也不是一件容易的事。

下面看一下ExitProcess的伪代码,帮助理解:

VOID
WINAPI
ExitProcess(
    UINT uExitCode
    )
{
    // 步骤 1和 步骤 2
    Status = NtTerminateProcess(NULL,(NTSTATUS)uExitCode);
    // 步骤 3
    LdrShutdownProcess(); 
    // 步骤 4,5....
    CsrClientCallServer();
    NtTerminateProcess(NtCurrentProcess(),(NTSTATUS)uExitCode);

}

此外可以调用ntdll的NtTerminateProcess函数结束进程,该函数直接进入内核态结束进程,会造成一些系统资源无法释放,用户态的资源更别想释放了,所以,如果是实在不想看到错误提示框,就调用该函数吧,谨慎使用!

原文地址:https://www.cnblogs.com/caibirdy1985/p/5801357.html