Why can’t you trap TerminateProcess?


If a user fires up Task Manager and clicks "End Task" on your program, Windows first tries to shut down your program nicely, by sending WM_CLOSE messages to GUI programs and CTRL_CLOSE_EVENT events to console programs. But you don't get a chance to intercept TerminateProcess. Why not?

TerminateProcess is the low-level process killing function. It bypasses DLL_PROCESS_DETACH and anything else in the process. Once you kill with TerminateProcess, no more user-mode code will run in that process. It's gone. Do not pass go. Do not collect $200.

If you could intercept TerminateProcess, then you would be escalating the arms race between programs and users. Suppose you could intercept it. Well, then if you wanted to make your program unkillable, you would just hang in your TerminateProcess handler!

And then people would ask for "a way to kill a process that is refusing to be killed with TerminateProcess," and we'd be back to where we started.

Tomorrow: About those processes that don't go away even though you've killed them. They're really dead, but they won't go away.

Comments (25)
  1. Anonymous says:

    This blog reminds me of good TV night when I was a kid! Thanks Raymond.

  2. Anonymous says:

    Could you run a service that attaches to your process as a debugger, thereby making TerminateProccess fail, or does TaskManager refuse to kill debugged processes as a courtesy?

  3. Anonymous says:

    I’ve seen situations where an application would popup a dialog box (e.g., would you like to save your document…) when told to terminate. And as long as that modal dialog was open, the process couldn’t be killed. Similar often happens on my work machine with Outlook when I do a shutdown. If I don’t hit ok to the "Are you sure you want to destroy your deleted mail" box, it sits there forever (I hate that box BTW). It’s worse when the weekly weekend department level workstation reboots (to push out patches requiring reboots) fail for the same reason. I don’t think we’ve managed to hire enough monkeys to run around and hit OK for everyone’s outlook.

    Either way, there are way too many times when I get the message "process could not be terminated" from task manager.

    Kill on the other hand is ruthless. It will take out a service without asking any questions. Too bad I can’t use "kill -9 -1" to log out like in the good old unix days (it was the last step in my logout script, after apps were told to shutdown nice)

  4. Anonymous says:

    Pete: you can go to "Tools/Options/Other" and uncheck "Empty the Deleted Items folder upon exiting". If you don’t like emptying the folder by hand, you can also set up auto-archiving to delete old stuff automatically (go to folder properties for that).

    Surely, though, TerminateProcess would work even when a modal dialog is up. That’s what Raymond seems to be saying here: do not pass go etc. Maybe you should try the "Processes" tab in taskmgr.exe instead of the "Applications" tab (as per my comment above).

    Machine shutdown is a completely different story. What tool are you using to reboot your machines automatically? See InitiateSystemShutdown in msdn. It has a bForceAppsClosed parameter.

  5. Anonymous says:

    Even if you click End Task on the Applications page, Task Manager will check afterwards whether the application has in fact exited. If the app is displaying a dialog box, Task Manager will time out and ask you if you want to kill it.

  6. Anonymous says:

    Stéphane: Yes, I am referring to the "End Task" button on the Applications tab. (The button on the Processes tab is called "End Process", which terminates without warning.)

    Injecting an ExitProcess would likely make things worse since there is now code running inside the process that it was not expecting.

  7. Anonymous says:

    How are processes killed on system shutdown?

    During a shutdown, apps get WM_QUERYENDSESSION followed by WM_ENDSESSION. After handling the latter, you don’t get a WM_CLOSE, which is a good thing, since many apps display modal dialogs in WM_CLOSE processing. I would have figured apps are supposed to quit when done processing WM_ENDSESSION, but the docs say that’s not necessary. (I’m curious, is there any harm if I PostQuitMessage()? Will further message processing even occur?)

    So I’m guessing the OS uses TerminateProcess, but that doesn’t clean up very well. What if the user is merely logging off rather than shutting down?

    Does the OS have some way other than TerminateProcess?

  8. Anonymous says:

    About injecting a remote process in the thread that should be terminated:

    I remember that I read something about that and that Windows actually DOES that.

    Please, can someone from MS tell about this whether it is done or not?

    BTW: I often happens that I can’t kill a process and that Process Explorer von Sysinternals will show me that it has no more handles open, but it is still around. These are things that make Windows not tooo robust and fail. Usually after this I have to restart.

    I’m waiting for your tomorrow-Post!

  9. Anonymous says:

    If one right clicks a Process on the Process tab there is two options – End and End Process. The button on the Tab is labled End Process. All three ways (2 ways) seem to work ok. Is there a difference?

    I seem to remember an article on writing a VB Task Manager in MSDN. But I can’t find it in October 2001 (the last MSDN with a usuable interface which in itself was a downgrade on the version before) or google but did find,

    A Simple VBCE Task Manager

    Matt Woodward

    Microsoft Corporation

    July 1999

  10. Anonymous says:

    This applies to NT-based systems and is probably not accurate for 9x-based systems.

    A process that has been terminated but doesn’t die is likely stuck in kernel mode due to a buggy driver.

    Terminating a process or thread is actually implemented as a kernel mode APC, and most drivers disable kernel mode APCs while doing processing, delaying the termination request until kernel APCs are re-enableed.

    Older versions of ZoneAlarm and all current versions of Hauppauge’s TVR drivers are fairly good examples of drivers that get stuck with APCs disabled, preventing processes from being killed properly.

    During normal system shutdown, CSRSS tells processes that the system is shutting down or the user is logging off by sending them a window message or invoking their ctrl-c handler. Depending on the shutdown type, a program that doesn’t respond in time can either block the shutdown with a dialog informing the user that xyz program isn’t responding, or be terminated ungracefully (by CSRSS).

    Christian: You probably have a poorly written driver on your system that exhibits some of the problems I described above. Blame the driver author, not Microsoft.

  11. Anonymous says:

    And don’t forget mini view mode that was in Sys Mon in 3.1 and still in TM in XP. Although the MMC people seem to have broken free of mini mode.

  12. Anonymous says:

    Waiting with bated breath for tomorrow. I have this exact situation, a process that can’t be killed with TerminateProcess when the application shuts its threads down in a particular way (not sure what that way is yet.) Of course this happens only in Windows 9x, not the NT/2k/XP, so it’s been quite difficult to figure out what’s going on!

  13. Anonymous says:

    Allow me to add a small clarification:

    Taskmgr.exe will only send the WM_CLOSE/CTRL_CLOSE_EVENT message if you use the "Applications" tab to end the task.

    Using the "Processes" tab, it will call TerminateProcess directly.

    I suppose it could try to enumerate top-level windows to find the process’ main window for a "cleaner" shutdown but the user can already do that on the other tab so I guess simplicity wins out.

    Does that mean there’s no easy way to cleanly shut down a process from the outside if it doesn’t have a window to send WM_CLOSE messages to? Would it be reasonable to use CreateRemoteThread with ExitProcess as the thread procedure? It sure doesn’t *sound* reasonable, but what do I know? :)

  14. Anonymous says:

    What I wonder about is that XP shell dialog that pops up if the WM_CLOSE message times out. You know the one… "Application not responding. click cancel to wait or End Now to loose your work". I always click End Now and nothing ever happens! Every single time, taskman is required to kill it.

    Of course, by that point the VM is violently thrashing and it takes a good 20 seconds just to get taskman open. Why oh why doesn’t "End Now" just terminate it as advertised?

    And don’t get me started on the lack of working set quotas causing every other app and the entire filesystem cache to get paged out. Talk about the crash that keeps on crashing.

  15. Anonymous says:

    I always shutdown my computer each day and sometimes after heavy use of visual studio 6, it seems it still is loaded in memory even though the window isn’t shown any more. When the computer goes to shut off, msdev.exe crashes. If you’ve turned off the monitor and left the room, not monitoring the shutdown process, your computer will still be displaying the crash dialog the next day, waiting for you to click ‘OK’ and wait for the computer to turn off.

  16. Anonymous says:

    Interesting, that OS beginning to nanny user more and more. For example, I begin to see actual non-killable processes. My short conversation with OS folks revealed that oif thread is stuck in the kernel, even kill -f won’t terminate it. However, logoff for some reason will. But I guess this is tomorrow’s topic :-)

  17. Anonymous says:

    7/22/2004 10:54 PM Mikhail Arkhipov (MSFT):

    > My short conversation with OS folks revealed

    > that oif thread is stuck in the kernel, even

    > kill -f won’t terminate it. However, logoff

    > for some reason will.

    Sometimes logoff will. Sometimes shutdown will. In Windows 2000 and Windows XP I’ve had cases where the only way out was to hold the power switch for 4 seconds. I don’t recall ever having that problem in Windows NT4. (Of course Windows 98 and 95 had it frequently.)

  18. Anonymous says:

    Well, you can actually have a process that cannot be terminated using TerminateProcess or for that matter any other means in a safe fashion (i.e. without crashing the machine).

    If you have a process in which one or more thread is waiting on a DeviceIoControl, won’t be terminated even by TerminateProcess.

    Even the sysinternals way of killing a process, which I suspect is CreateRemoteThread and ExitProess may not work in this case.

    I wonder if there are other cases when a process cannot be killed.

    – amjoshi at gmail

  19. Anonymous says:

    I believe you’ve written a very similar post a while back. A couple of months ago I believe.

  20. Anonymous says:

    If anyone wants to "escalate the arms war" all they need do is take a leaf out of the Robin Hood/Friar Tuck book:

    http://catb.org/~esr/jargon/html/meaning-of-hack.html

  21. Anonymous says:

    If anyone wants to "escalate the arms war" all they need do is take a leaf out of the Robin Hood/Friar Tuck book

    pause, pause, whack, whack. All gone.

  22. Anonymous says:

    http://www.rootkit.com/newsread.php?newsid=139

    provides a nice bit of code on proving the opposite of this…

  23. Anonymous says:

    All it proves is that if you allow your kernel to become compromised then all rules become moot.

  24. Anonymous says:

    qwerty: on XP, by default, ‘End Now’ first runs dumprep.exe, which generates a dump report to send back to Microsoft. The amount of time it takes seems to be in exponential proportion to how much virtual memory the hung process was using. I’ve had eMbedded Visual C++ 3.0 go mad and eat 300MB, then dumprep eat 500MB to write the report, leading to an 800MB memory load and horrible swapping.

    I think you can turn this off by going to Control Panel > System > Advanced tab > Error Reporting and unchecking the Programs box. Or, Disable Error Reporting. Or, click Choose Programs and change from All Programs to All Programs In This List. Etcetera.

  25. Anonymous says:

    About injecting a remote process

    > in the thread that should be terminated:

    > I remember that I read something about

    > that and that Windows actually DOES that.

    I don’t think this is true. CreateRemoteThread is a very unreliable API. One of the problems with it is that it will hang if the loader lock in the target process is held.

    This kind of deadlock is so common that debuggers that use CreateRemoteThread to inject a DebugBreak() call into the debuggee usually implement a timeout and if the remote thread seems to be hung they simply suspend the process and switch to a special mode where you can examine memory but can’t step/set breakpoints etc.

Comments are closed.