Psychic debugging: Why does ExitProcess(1) produce an exit code of zero?

Here’s a question that came from a customer. By now, you should already have the necessary psychic powers to answer it.

Our program calls ExitProcess(1) to indicate that it exited unsuccessfully. The process that launched our program waits for the program to exit and then calls GetExitCodeProcess to retrieve the exit code. The function succeeds, but the exit code is zero! How can this be?

Hint: Read about how processes terminate on Windows XP.

Comments (18)
  1. Nathan_works says:

    From a cold reading of the MSDN, it would appear they are doing it "right"..

  2. pcooper says:

    Wild guess, based on reading the hint article: They see that the exit code is 0 in the debugger, because their debugger is creating a new thread during process termination to be able to debug the process. And the debug thread terminates successfully.

  3. Joel says:

    From MSDN

    "Terminating a process does not necessarily remove the process object from the OS. A process object is deleted when the last handle to the process is closed. "

    So, they are in a circular loop where they can’t retrieve the Exit Code until they close their handle?

    Maybe they should call DuplicateHandle with the DUPLICATE_CLOSE_SOURCE flag.

  4. Kujo says:

    (Especially in light of the provided hint article) I would guess they are waiting for the process to end in the wrong way… probably by waiting on a mutex.  Thus they race with the dying process to read the exit code.

  5. A. Skrobov says:

    A guess: a DLL their process is using calls ExitProcess(0) itself as part of its teardown.

  6. El Guapo says:

    If the process were still running, the exit code would be 259 and not 0.

    I have no clue what the problem is. They are doing everything right. If they are doing something incredibly stupid like calling ExitProcess from DLL_PROCESS_DETACH, we wouldn’t be requested to invoke psychic powers to debug that, that would just be plain dumb.

  7. Bryan says:

    I believe it’s involving DLL_PROCESS_DETACH – something they’re doing is pooching the exit code.

    That’s my guess anyway.

  8. Kujo says:

    Joel: You don’t want the OS to delete the process object, that’s where your exit code is stored.

    El Guapo: (rereads documentation) Ah, you’re right. d’oh!

    Calling RaiseException with 0 doesn’t seem like psychic debugging, either.

    Maybe we can get to ExitProcess(0) with a better story behind it… something like: DLL_PROCESS_DETACH handler tries to use a critical section, ends up reading something in an inconsistent state, faults, the exception filter runs and ends the process with 0.

    Would such an exception be handled by a try/catch block?  The thought of an exception unwinding the stack past ExitProcess is pretty scary.

  9. Greg D says:

    Perhaps the called program is exiting successfully!

  10. gilltots says:

    this is an easy one – it’s just an off-by-one error deep in the guts of windows :)

  11. Jack Mathews says:

    Yeah, a crash is probably happening in a DLL_PROCESS_DETACH, which is just causing TerminateProcess to be called on self and thus an exit code of 0.

  12. Brian says:

    I liked how in the referenced article, Raymond actually had FAITH in his readers to not nitpick!  Aaaahhhh Raymond, you sure were foolish in your younger free-wheeling days of a year ago.

    (Disclaimer:  Long time reader who was a few shades less upset with the nitpickers as Raymond had to have been.)

  13. John says:

    My psychic powers are weak; my guess is that some loaded DLL is either crashing or calling ExitProcess(0) during DLL_PROCESS_DETACH.

  14. Gwyn says:

    Maybe they aren’t checking the return value from GetExitCodeProcess? The process might be racing or hung in which case the return value would be STILL_ACTIVE (259), but they wouldn’t know that, since they didn’t check it?

  15. Kujo says:

    I am able to reproduce this with an UnhandledExceptionFilter (using ExitProcess again) and with structured exception handling (returning control to main.)  I imagine there are other permutations of this approach as well.

    Kudos to whoever made ExitProcess behave so well in these circumstances.

  16. Triangle says:

    Q: Why does ExitProcess(1) produce an exit code of zero?

    A: Welcome to windows programming.

  17. wtroost says:

    My guess is that they’re using ShellExecute to launch the program.  Then they’re passing the return value of ShellExecute to GetExitCodeProcess.  But since ShellExecute doesn’t return a process handle this causes GetExitCodeProcess to return 0 for error.

  18. Last week I've resolved a simple "debugging" case by phone, and figured that it might benefit

Comments are closed.