shy_potato
shy_potato

Reputation: 147

C++ Windows return vs ExitProcess

I am trying to create simple program on Windows without any CRT library.

I have two codes:

// compile without -lkernel32
int __stdcall _main() {
    return 5;
}

and

// compile with -lkernel32
#include <windows.h>

void __stdcall _main() {
    ExitProcess(5);
}

I am compiling them with MinGW-w64 7.1.0 with this bash script:

@echo off
del main.exe 2>nul
C:\Users\Michal\Downloads\mingw64\bin\g++ main.cpp -o main.exe -O3 -s -nostdlib -lkernel32
main.exe
echo %errorlevel%
pause

The output (exit code) is the same. (I am using Windows 7 Pro 64-bit)

What code is better? (Maybe better question is: Why does the "return" variant work?)

Edit:

Entrypoint to the program (defaultly pre-set by linker) is _main (normally CRT lib does some work and then calls the 'main' function and calls ExitProcess (or something like that) with value returned from main function).

In my code I am not using CRT library and _main is still the entry-point to the program (and is not calling 'main' function).

Upvotes: 5

Views: 6627

Answers (2)

RbMm
RbMm

Reputation: 33716

If you do not use CRT, you need to directly call ExitProcess. Without this your process cannot terminate at all. So the variant with only return is wrong. It will work only if your process has a single thread, otherwise the process will not terminate.


You need to understand How Processes are Terminated.

A process executes until one of the following events occurs:

  • Any thread of the process calls the ExitProcess function

  • The last thread of the process terminates

  • Any thread calls the TerminateProcess function with a handle to the process

When we use CRT, it internally calls ExitProcess. When we do not use CRT, main is the true entry point of your exe. After returning, you return directly to kernel32 code. kernel32 code calls ExitThread but not ExitProcess -- this is very important. So your process will be terminated in this case only if there are no other threads in your process. We can never assume this, however. Beginning with Windows 10, this is usually false. Therefore the only correct way to terminate a process without using CRT is to directly call ExitProcess.

Upvotes: 9

Ben Voigt
Ben Voigt

Reputation: 283634

You are correct that why the return version works is key to understanding which is better.

The important thing here is that main is not the entrypoint for a Windows console application. The entrypoint is in library code, which initializes memory, calls constructors on global variables, splits the command line into argc/argv format, and then calls main saving the return value.

If main does return, it returns to the library code which calls atexit-registered functions and destructors for static variables, then calls ExitProcess.

Therefore there's really only one way to set the exit value, by calling ExitProcess. But doing it yourself will skip the cleanup actions taken by the library -- if you call ExitProcess then destructors don't get called and you might end up with loss of data stuck in write buffers.

When you build without the standard library, then library actions of construction/destruction aren't relevant, and calling ExitProcess explicitly is nearly the same as returning. There still is a library-provided call frame which catches your return value and calls ExitThread if you don't, but it comes from the OS itself (kernel32.dll) when there is no CRT. As RmMb points out, this is an important difference, because other threads won't be killed; the process will exit if all threads exit.

Upvotes: 9

Related Questions