CreateProcess, GetExitCodeProcess and application crash

Recently I’ve been working on a C++ app that needed to start a second app and catch its exit code.

The exit code is the number returned by main() or WinMain() at the application exit point.

The two apps I’m using are Starter.exe and WMLoader.exe (that gets executed by the first). WMLoader is a very basic console application that only returns an error code (for testing purposes)

#include “stdafx.h”

int _tmain(int argc, _TCHAR* argv[])
{
return 11;
}

To receive the exit code 11 in Starter.exe I need to:

1) call CreateProcess to start WMLoader.exe

2) WaitForSingleObject to wait until WMLoader terminates

3) call GetExitCodeProcess to get the exit code.

My inital code was:

DWORD                  code;
STARTUPINFO          si;
PROCESS_INFORMATION  pi;

ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);

if (!CreateProcess(NULL, L”WMLoader.exe”, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
printf(“CreateProcess() failed: %d\n”, GetLastError());
exit(1);
}

if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) {
printf(“WaitForSingleObject() failed: %d\n”, GetLastError());
exit(1);
}

if (!(GetExitCodeProcess(pi.hProcess, &code))) {
printf(“GetExitCodeProcess() failed: %d\n”, GetLastError());
exit(1);
}
printf(“code=%d”, code);

This resulted in Starter.exe crash with the following message:


After wasting a few minutes double checking everything, I found the problem. From MSDN, CreateProcess page:

pszCmdLine
[in, out] Pointer to a null-terminated string that specifies the command line to execute.
The system adds a null character to the command line, trimming the string if necessary, to indicate which file was used.

Meaning you can’t use a constant for pszCmdLine.

I’ve also modified the code to start the second process in SUSPENDED mode and resume it after the successful CreateProcess call:
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);

PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));

TCHAR szFile[MAX_PATH] = TEXT(“WMLoader.exe”);
if(!CreateProcess(
NULL, // No module name (use command line).
szFile,
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
FALSE, // Set handle inheritance to FALSE.
CREATE_SUSPENDED, // Create suspended.
NULL, // Use parent’s environment block.
NULL, // Use parent’s starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi)) // Pointer to PROCESS_INFORMATION structure.
{
// Handle error.
printf(“Createprocess failed (%d).\n”, GetLastError());
}
else
{
// Resume the external process thread.
DWORD resumeThreadResult = ResumeThread(pi.hThread);
// ResumeThread() returns 1 which is OK
// (it means that the thread was suspended but then restarted)

// Wait for the external process to finish.
DWORD waitForSingelObjectResult = WaitForSingleObject(pi.hProcess, INFINITE);
// WaitForSingleObject() returns 0 which is OK.

// Get the exit code of the external process.
DWORD exitCode;
if(!GetExitCodeProcess(pi.hProcess, &exitCode))
{
// Handle error.
printf(“GetExitCodeProcess failed (%d).\n”, GetLastError());
}
else
{
printf(“Application exitcode:%d.\n”, exitCode);
}
}

Leave a Reply