Windows APIs: Command-Line Execution
This section details the Windows APIs that facilitate the execution of commands and programs from the command line, allowing for programmatic control over processes and scriptable interactions with the operating system.
Introduction to Command-Line Execution APIs
Windows provides a rich set of Application Programming Interfaces (APIs) for managing and interacting with the command-line environment. These APIs are crucial for tasks such as launching external applications, redirecting input/output, and monitoring process execution.
Key Functions for Command Execution
CreateProcess()
The CreateProcess() function is the primary API for creating a new process and its primary thread. It provides extensive control over the new process's environment, security attributes, and startup information. This function is fundamental for launching any executable program from within another application or script.
Parameters:
lpApplicationName: The name of the module to be executed.lpCommandLine: The command line string to be executed.lpProcessAttributes: Security attributes for the new process.lpThreadAttributes: Security attributes for the new thread.bInheritHandles: Whether the child process inherits handles.dwCreationFlags: Flags that control the priority class and the creation of the process.lpEnvironment: Block of environment variables for the new process.lpCurrentDirectory: The current directory for the new process.lpStartupInfo: ASTARTUPINFOstructure that specifies the window station, standard handles, and appearance of the main window.lpProcessInformation: APROCESS_INFORMATIONstructure that receives identification information for the new process and its primary thread.
BOOL CreateProcess(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
ShellExecute() and ShellExecuteEx()
These functions provide a higher-level interface for executing applications, opening documents, or printing documents. They leverage the Windows shell to determine how to handle the request, often invoking the default associated application.
ShellExecute(): A simpler version that performs an action on a specified file or object.ShellExecuteEx(): A more powerful version that allows for more detailed control and retrieval of information about the executed process.
These functions are convenient for user-facing operations where the exact executable might not be known or when you want to rely on default system behavior.
CreateProcessAsUser()
This function creates a new process running in the security context of a specified user account. It requires appropriate privileges to impersonate the target user.
Input/Output Redirection
When launching processes, it's often necessary to capture their standard output, standard error, or provide them with standard input programmatically. This is achieved using pipes.
CreatePipe()
CreatePipe() creates an anonymous pipe, which is a unidirectional data stream. You typically create two pipes: one for standard output and one for standard input of the child process.
STARTUPINFO Structure
The STARTUPINFO structure, used with CreateProcess(), has members such as hStdInput, hStdOutput, and hStdError. These members are set to the handles of the pipe ends to redirect the child process's standard streams.
Important Note on Redirection
When redirecting standard output or error, remember to set the dwFlags member of the STARTUPINFO structure to STARTF_USESTDHANDLES and assign the write end of the pipe to hStdOutput and/or hStdError.
Example: Launching a Command and Capturing Output
The following conceptual example illustrates how to use CreateProcess() with I/O redirection to execute a command and read its output:
#include <windows.h>
#include <iostream>
#include <string>
int main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES sa;
// Set up the security attributes for the pipe
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
std::cerr << "Failed to create pipe." << std::endl;
return 1;
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0)) {
std::cerr << "Failed to set handle information for read pipe." << std::endl;
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
return 1;
}
// Set up the STARTUPINFO structure
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = hWritePipe; // Redirect STDERR to the same pipe as STDOUT
si.hStdOutput = hWritePipe; // Redirect STDOUT to the pipe
si.dwFlags |= STARTF_USESTDHANDLES;
// Command to execute (e.g., "dir" or "ipconfig")
char commandLine[] = "cmd.exe /c dir"; // Example command
// Create the child process
if (!CreateProcess(NULL, // No module name (use command line)
commandLine, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance to TRUE
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
std::cerr << "CreateProcess failed (" << GetLastError() << ")." << std::endl;
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
return 1;
}
// Close the write end of the pipe in the parent process,
// as it is not needed here.
CloseHandle(hWritePipe);
// Read output from the child process
char buffer[4096];
DWORD dwRead;
std::string result = "";
while (ReadFile(hReadPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) && dwRead > 0) {
buffer[dwRead] = '\0'; // Null-terminate the string
result += buffer;
}
std::cout << "--- Command Output ---" << std::endl;
std::cout << result << std::endl;
std::cout << "----------------------" << std::endl;
// Wait until child process exits
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hReadPipe); // Close the read pipe handle
return 0;
}
Process Management
Beyond creation, Windows APIs offer functions to manage existing processes:
ExitProcess(): Terminates the calling process.TerminateProcess(): Terminates a specified process.WaitForSingleObject(): Suspends the calling thread until the specified object (like a process handle) is in a signaled state.GetExitCodeProcess(): Retrieves the termination status of a specified process.
Command-Line Arguments
When a program is executed from the command line, it can receive arguments. The lpCommandLine parameter of CreateProcess() is where these are specified. Typically, the first argument is the program name itself, followed by its parameters.
Tip for Robustness
Always validate the return values of API functions and use GetLastError() to diagnose errors. Properly manage handle resources by closing them when they are no longer needed.
Related APIs
CreateProcessW()(Unicode version)CreateJobObject()(For managing groups of processes)DuplicateHandle()CommandLineToArgvW()(Utility function for parsing command-line arguments)