C++ helper class to create a child process with redirected std in/out/error streams using the Windows API. Want to use it with .net?
It can be used to pass arbitrary binary input data to the child process via stdin and retrieve the result data via stdout. Errors can be received via stderr.
Currently the class can not be used for asynchronous communication (e.g. messages) to and from the child process.
The PipedProcess class provides a simple, exception-free public API that returns error codes for all operations.
// Basic execution
DWORD Run(const char* program, const char* arguments);
// Execution with abort capability
template<class T>
DWORD Run(const char* program, const char* arguments, T& abortEvent);
// Execution with user token (run as different user)
DWORD RunAs(const HANDLE& token, const char* program, const char* arguments);
template<class T>
DWORD RunAs(const HANDLE& token, const char* program, const char* arguments, T& abortEvent);// Set data to send to child process stdin
void SetStdInData(const char* pData, size_t len);
// Check if output data is available
bool HasStdOutData() const;
bool HasStdErrData() const;
// Retrieve output data (moves data out of internal buffers)
std::string FetchStdOutData();
std::string FetchStdErrData();// Control child process window visibility
enum class WindowMode { Visible = 0, Hidden = 1 };
void SetWindowMode(WindowMode mode); // Default is HiddenAll Run methods return DWORD error codes:
0= Success (child process completed successfully)ERROR_INVALID_PARAMETER= Null parameters providedERROR_FILE_NOT_FOUND= Program executable not foundERROR_PATH_NOT_FOUND= Empty program path- Other Windows error codes for various failure conditions
Error details are available via FetchStdErrData() when errors occur.
#include "PipedProcess/PipedProcess.h"
// Simple command execution
PipedProcess process;
DWORD exitCode = process.Run("cmd.exe", "/c echo Hello World");
if (exitCode == 0 && process.HasStdOutData()) {
std::string output = process.FetchStdOutData();
std::cout << "Output: " << output << std::endl;
}// Execute a program that reads from stdin
PipedProcess process;
std::string input = "Hello from parent process\n";
process.SetStdInData(input.c_str(), input.length());
DWORD exitCode = process.Run("sort.exe", "");
if (exitCode == 0) {
std::string output = process.FetchStdOutData();
std::cout << "Sorted output: " << output << std::endl;
}PipedProcess process;
DWORD exitCode = process.Run("nonexistent.exe", "");
if (exitCode != 0) {
std::cout << "Process failed with exit code: " << exitCode << std::endl;
if (process.HasStdErrData()) {
std::string errorMsg = process.FetchStdErrData();
std::cout << "Error details: " << errorMsg << std::endl;
}
}// Custom abort event
struct MyAbortEvent {
std::atomic<bool> shouldAbort{false};
bool IsSet() const { return shouldAbort.load(); }
void Set() { shouldAbort = true; }
};
PipedProcess process;
MyAbortEvent abortEvent;
// Start long-running process in another thread
auto future = std::async(std::launch::async, [&]() {
return process.Run("long-running-program.exe", "", abortEvent);
});
// Later, signal abort if needed
abortEvent.Set();
DWORD exitCode = future.get();Hidden vs Visible Windows
PipedProcess process;
// Show the child process window (useful for debugging)
process.SetWindowMode(PipedProcess::WindowMode::Visible);
// Or keep it hidden (default behavior)
process.SetWindowMode(PipedProcess::WindowMode::Hidden);
DWORD exitCode = process.Run("notepad.exe", "test.txt");Important: This library does not validate input parameters. It is the caller's responsibility to ensure:
- Program paths are trusted and do not contain malicious executable paths
- Arguments do not contain command injection vectors (e.g.,
|,&,;,>,<when used with shell commands) - Input data size is reasonable to prevent memory exhaustion
- Program execution is limited to trusted executables in secure environments
For applications handling untrusted input, implement validation before calling PipedProcess methods.
Feel free to use. See LICENSE file for further information.
The project is a Visual Studio 2022 solution. It should be possible to build it with other compilers, but I have not tested it.
- NuGet CLI or dotnet CLI
- Visual Studio 2022 (for building)
-
Build the solution to ensure all tests pass:
msbuild PipedProcess.sln -p:Configuration=Release -p:Platform=x64 -
Create the NuGet package using the modern nuspec file:
nuget pack PipedProcess.nuspecOr with dotnet CLI:
dotnet pack PipedProcess.nuspec -
Publish to NuGet.org (optional):
nuget push PipedProcess.{version}.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey {your-api-key}
The NuGet package includes:
- Headers:
PipedProcess.h,StdPipe.h,UniqueHandle.hininclude/PipedProcess/directory - MSBuild targets: Automatic include path configuration and preprocessor definitions
- Documentation: Inline comments and usage examples
After installing the package in a project, simply include the headers:
#include <PipedProcess/PipedProcess.h>
#include <PipedProcess/StdPipe.h>The MSBuild targets automatically configure include paths and preprocessor definitions.