Creating Processes (With Code Example)
The CreateProcess() function creates a new process, which runs independently of the creating process. However, for simplicity, the relationship is referred to as a parent-child relationship. The following code example demonstrates how to create a process.
Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.
Then, add the source file and give it a suitable name.
Next, add the following source code.
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
#include <wchar.h>
// Prototype
void ErrorHandler(LPTSTR lpszFunction);
// This wmain() is the main or parent process
int wmain(int argc, WCHAR *argv[])
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD Ret = 0, dwPID = 0, dwTID = 0, dwPver = 0;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Validate the argument
if(argc != 2)
{
wprintf(LUsage: %s [cmdline]\n, argv[0]);
return 1;
}
// Get parent process and...
dwPID = GetCurrentProcessId();
wprintf(Lwmain() process ID is %u\n, dwPID);
// What about the current thread?
dwTID = GetCurrentThreadId();
wprintf(Lwmain() thread ID is %u\n, dwTID);
// More info...
wprintf(LCommand line: %s\n, GetCommandLine());
// Other info
dwPver = GetProcessVersion(dwPID);
// or dwPver = GetProcessVersion(0);
wprintf(LProcess version: %u\n, dwPver);
// Start the child process
wprintf(LStarting another process i.e. child process...\n);
if(!CreateProcess(NULL, // No module name (so use command line - [cmdline])
argv[1], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
ErrorHandler(LCreateProcess());
return 1;
}
else
{
wprintf(LCreateProcess() - child process was created successfully!\n);
wprintf(LProcess ID is %u or %u\n, pi.dwProcessId, GetProcessId(pi.hProcess));
wprintf(LThread ID is %u\n, pi.dwThreadId);
// Minimum is Vista, Windows 2003 for the following code
// wprintf(L Thread ID is %u\n, GetThreadId(pi.hThread));
}
// Wait until child process exits.
wprintf(LWaiting the child process exits...\n);
// The time-out interval, in milliseconds. If a nonzero value is specified,
// the function waits until the object is signaled or the interval elapses.
// If dwMilliseconds is zero, the function does not enter a wait state if
// the object is not signaled; it always returns immediately. If dwMilliseconds is INFINITE,
// the function will return only when the object is signaled.
Ret = WaitForSingleObject( pi.hProcess, INFINITE );
// WAIT_ABANDONED - 0x00000080L, WAIT_OBJECT_0 - 0x00000000L,
// WAIT_TIMEOUT - 0x00000102L, WAIT_FAILED - (DWORD)0xFFFFFFFF
wprintf(LThe WaitForSingleObject() return value is 0X%.8X\n, Ret);
// Close process and thread handles
wprintf(LClosing the process and thread handles...\n);
if(CloseHandle(pi.hProcess) != 0)
wprintf(Lpi.hProcess handle was closed successfully!\n);
else
ErrorHandler(LCloseHandle(pi.hProcess));
if(CloseHandle(pi.hThread) != 0)
wprintf(Lpi.hThread handle was closed successfully!\n);
else
ErrorHandler(LCloseHandle(pi.hThread));
return 0;
}
// This just redundant, you can use the GetLastError() instead
void ErrorHandler(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR) lpMsgBuf) + lstrlen((LPCTSTR) lpszFunction) + 40) * sizeof(WCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(WCHAR), L%s failed with error %u: %s, lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, LError, MB_OK);
// Free error-handling buffer allocations
if(LocalFree(lpMsgBuf) == NULL)
wprintf(LlpMsgBuf buffer freed!\n);
else
wprintf(LFailed to free lpMsgBuf buffer, error %u\n, GetLastError());
if(LocalFree(lpDisplayBuf) == NULL)
wprintf(LlpDisplayBuf buffer freed!\n);
else
wprintf(LFailed to free lpDisplayBuf buffer, error %u\n, GetLastError());
}
Build and run the project. The following screenshot is a sample output when we use the calc (calculator) as the argument. The Windows calculator program should be launched.
The following is the Windows calculator.
When we see through Windows Task Manager, we can see both processes and their threads respectively.
When we close the calc program, the following screenshot shows the complete output.
If CreateProcess() succeeds, it returns a PROCESS_INFORMATION structure containing handles and identifiers for the new process and its primary thread. The thread and process handles are created with full access rights, although access can be restricted if you specify security descriptors. When you no longer need these handles, close them by using the CloseHandle() function. You can also create a process using the CreateProcessAsUser() or CreateProcessWithLogonW() function. This allows you to specify the security context of the user account in which the process will execute.