Exit Values on Windows

Hi cyberpals. Today I was messing around with some Windows utilities and realized that I didn’t fully understand how to handle exit values.

On *nix systems, you can do something like this:

daniel@disstrack ~ % cat exit.c
#include <stdlib.h>

int main(int argc, char *argv[]) {
    if (argc > 1)
        return atoi(argv[1]);
    return 0;
}
daniel@disstrack ~ % gcc -o exit exit.c
daniel@disstrack ~ % ./exit
daniel@disstrack ~ % echo $?
0

The exit value can be up to 255. It rolls over:

daniel@disstrack ~ % ./exit 256
daniel@disstrack ~ % echo $?
0
daniel@disstrack ~ % ./exit 7000
daniel@disstrack ~ % echo $?
88                                    # Why is this 88???
daniel@disstrack ~ % python3
Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 7000 % 256
88                                    # That's why.

Checking exit values can be useful if a utility’s author made them meaningful. Some programs such as curl have several dozen exit codes. See it’s man page for more details. Other utilities such as cp return a 0 if it was successful, or a 1 if it was not. This comes in handy when you’re writing shell scripts.

It turns out, Windows programs also have exit values. Powershell supports the $? method outlined above, but will show True or False rather than the value. To see the actual value, use $lastexitcode:

PS C:\Users\dmfr\lolwindows> type exit.c
#include <stdlib.h>

int main(int argc, char *argv[]) {
    if (argc > 1)
        return atoi(argv[1]);
    return 0;
}
PS C:\Users\dmfr\lolwindows> C:\MinGW\bin\gcc -o exit.exe exit.c
PS C:\Users\dmfr\lolwindows> ./exit.exe
PS C:\Users\dmfr\lolwindows> $?
True
PS C:\Users\dmfr\lolwindows> .\exit.exe 1
PS C:\Users\dmfr\lolwindows> $?
False
PS C:\Users\dmfr\lolwindows> $lastexitcode
1
PS C:\Users\dmfr\lolwindows> .\exit.exe 7000
PS C:\Users\dmfr\lolwindows> $lastexitcode
7000

Neat. It looks like Windows supports values greater than 255 for exit value. After a bit of Googling, I found that these are stored as 32 bit signed integers, and that there are thousands of different error conditions defined in WinError.h: https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes–0-499-

Here’s an example showing that this is in fact a 32 bit integer:

PS C:\Users\dmfr\lolwindows> .\exit.exe 2147483647
PS C:\Users\dmfr\lolwindows> $lastexitcode
2147483647
PS C:\Users\dmfr\lolwindows> .\exit.exe 2147483648
PS C:\Users\dmfr\lolwindows> $lastexitcode
-2147483648

cmd is a different beast. It does not support $?. Use %ERRORLEVEL% instead:

C:\Users\dmfr\lolwindows>exit.exe

C:\Users\dmfr\lolwindows>echo %errorlevel%
0

C:\Users\dmfr\lolwindows>exit.exe 7000

C:\Users\dmfr\lolwindows>echo %errorlevel%
7000

I hope you enjoyed this nerdy blog post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s