From 6bafbac1c6cd1b506aae2647136f3e7c151ee99d Mon Sep 17 00:00:00 2001 From: ImJotaM Date: Mon, 8 Sep 2025 17:42:45 -0300 Subject: [PATCH 1/3] Refactor: Isolate WinAPI declarations. This commit replaces the direct inclusion of `` with a minimal set of manual forward declarations. This is a deliberate architectural change to: - **Improve Compilation Speed:** Avoids parsing the notoriously large Windows header. - **Eliminate Naming Pollution:** Prevents name clashes with common names like `min`/`max` which conflict with the C++ standard library. - **Enhance Encapsulation:** Makes the library more self-contained by not exposing the entire Windows API. --- cpp-subprocess/subprocess.hpp | 125 +++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/cpp-subprocess/subprocess.hpp b/cpp-subprocess/subprocess.hpp index 216540a..f334e81 100644 --- a/cpp-subprocess/subprocess.hpp +++ b/cpp-subprocess/subprocess.hpp @@ -60,8 +60,129 @@ Documentation for C++ subprocessing library. #endif extern "C" { -#ifdef __USING_WINDOWS__ - #include + + /* + * == Windows API Declarations == + * + * Manually declare only the Windows API functions we need instead of + * including the full to: + * + * 1. Keep compile times fast. + * 2. Prevent name collisions with common names like min/max. + * 3. Make the library self-contained and clean. + */ + + #ifdef __USING_WINDOWS__ + + #define CONST const + #define WINAPI __stdcall + + #define TRUE 1 + #define FALSE 0 + + #define INFINITE 0xFFFFFFFF + + #define STATUS_WAIT_0 ((unsigned long)0x00000000L) + #define WAIT_OBJECT_0 ((STATUS_WAIT_0) + 0) + + #define HANDLE_FLAG_INHERIT 0x00000001 + + #define STARTF_USESTDHANDLES 0x00000100 + + #define CREATE_NO_WINDOW 0x08000000 + #define CREATE_UNICODE_ENVIRONMENT 0x00000400 + + #define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100 + #define FORMAT_MESSAGE_FROM_SYSTEM 0x00001000 + #define FORMAT_MESSAGE_IGNORE_INSERTS 0x00000200 + #define FORMAT_MESSAGE_MAX_WIDTH_MASK 0x000000FF + + #define LANG_NEUTRAL 0x00 + #define SUBLANG_DEFAULT 0x01 + + #ifdef _M_CEE_PURE + typedef System::ArgIterator va_list; + #else + typedef char* va_list; + #endif + + typedef void VOID; + typedef char CHAR; + typedef wchar_t WCHAR; + + typedef int BOOL; + typedef unsigned int UINT; + typedef unsigned short USHORT; + typedef unsigned short WORD; + typedef unsigned long DWORD; + + typedef size_t SIZE_T; + + typedef void* PVOID; + typedef void* LPVOID; + typedef const void* LPCVOID; + + typedef void* HANDLE; + typedef HANDLE HLOCAL; + typedef HANDLE* PHANDLE; + + typedef DWORD* LPDWORD; + + typedef CHAR* LPSTR; + typedef WCHAR* LPWCH; + typedef WCHAR* LPWSTR; + typedef CONST WCHAR* LPCWSTR; + + typedef struct _SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; + } SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; + + typedef struct _PROCESS_INFORMATION { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; + } PROCESS_INFORMATION, *LPPROCESS_INFORMATION; + + typedef struct _STARTUPINFOW { + DWORD cb; + LPWSTR lpReserved; + LPWSTR lpDesktop; + LPWSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + unsigned char* lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; + } STARTUPINFOW, * LPSTARTUPINFOW; + + BOOL WINAPI CloseHandle(HANDLE); + BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD); + BOOL WINAPI CreateProcessW(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); + BOOL WINAPI FreeEnvironmentStringsW(LPWCH); + BOOL WINAPI GetExitCodeProcess(HANDLE, LPDWORD); + BOOL WINAPI SetHandleInformation(HANDLE, DWORD, DWORD); + BOOL WINAPI TerminateProcess(HANDLE, UINT); + DWORD WINAPI FormatMessageA(DWORD, LPCVOID, DWORD, DWORD, LPSTR, DWORD, va_list*); + DWORD WINAPI GetLastError(VOID); + DWORD WINAPI WaitForSingleObject(HANDLE, DWORD); + HLOCAL WINAPI LocalFree(HLOCAL); + LPWSTR WINAPI GetEnvironmentStringsW(VOID); + + #define ZeroMemory(Destination,Length) memset((Destination),0,(Length)) + #define MAKELANGID(p, s) ((((WORD)(s)) << 10) | (WORD)(p)) + #include #include #else From 73bb6662f44e80df76116db3cb14d45127d53de6 Mon Sep 17 00:00:00 2001 From: ImJotaM Date: Thu, 11 Sep 2025 17:24:27 -0300 Subject: [PATCH 2/3] fix(windows): Resolve header conflicts and update tests Previously, including both `subprocess.hpp` and `` in the same file would cause compilation errors due to redefinitions of Windows API types and functions. This commit resolves these conflicts by wrapping the manual API declarations in `subprocess.hpp` with an `#ifndef _WINDEF_` guard. This ensures the library's lightweight declarations are only used if the official Windows headers haven't been included, making it safe to use both. The `test_ret_code.cc` unit test has been updated to include `` directly for the `Sleep` function, which also serves to validate this fix. --- cpp-subprocess/subprocess.hpp | 79 ++++++++++++++++++++++------------- test/test_ret_code.cc | 1 + 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/cpp-subprocess/subprocess.hpp b/cpp-subprocess/subprocess.hpp index f334e81..dbb8381 100644 --- a/cpp-subprocess/subprocess.hpp +++ b/cpp-subprocess/subprocess.hpp @@ -74,6 +74,8 @@ extern "C" { #ifdef __USING_WINDOWS__ + #ifndef _WINDEF_ + #define CONST const #define WINAPI __stdcall @@ -133,20 +135,24 @@ extern "C" { typedef WCHAR* LPWSTR; typedef CONST WCHAR* LPCWSTR; - typedef struct _SECURITY_ATTRIBUTES { + typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES, * LPSECURITY_ATTRIBUTES; + typedef struct _PROCESS_INFORMATION PROCESS_INFORMATION, * LPPROCESS_INFORMATION; + typedef struct _STARTUPINFOW STARTUPINFOW, * LPSTARTUPINFOW; + + typedef struct _SP_SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; - } SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; + } SP_SECURITY_ATTRIBUTES, * SP_LPSECURITY_ATTRIBUTES; - typedef struct _PROCESS_INFORMATION { + typedef struct _SP_PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; - } PROCESS_INFORMATION, *LPPROCESS_INFORMATION; + } SP_PROCESS_INFORMATION, * SP_LPPROCESS_INFORMATION; - typedef struct _STARTUPINFOW { + typedef struct _SP_STARTUPINFOW { DWORD cb; LPWSTR lpReserved; LPWSTR lpDesktop; @@ -165,24 +171,32 @@ extern "C" { HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; - } STARTUPINFOW, * LPSTARTUPINFOW; + } SP_STARTUPINFOW, * SP_LPSTARTUPINFOW; - BOOL WINAPI CloseHandle(HANDLE); - BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD); - BOOL WINAPI CreateProcessW(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); - BOOL WINAPI FreeEnvironmentStringsW(LPWCH); - BOOL WINAPI GetExitCodeProcess(HANDLE, LPDWORD); - BOOL WINAPI SetHandleInformation(HANDLE, DWORD, DWORD); - BOOL WINAPI TerminateProcess(HANDLE, UINT); - DWORD WINAPI FormatMessageA(DWORD, LPCVOID, DWORD, DWORD, LPSTR, DWORD, va_list*); - DWORD WINAPI GetLastError(VOID); - DWORD WINAPI WaitForSingleObject(HANDLE, DWORD); - HLOCAL WINAPI LocalFree(HLOCAL); - LPWSTR WINAPI GetEnvironmentStringsW(VOID); + __declspec(dllimport) BOOL WINAPI CloseHandle(HANDLE); + __declspec(dllimport) BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD); + __declspec(dllimport) BOOL WINAPI CreateProcessW(LPCWSTR, LPWSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION); + __declspec(dllimport) BOOL WINAPI FreeEnvironmentStringsW(LPWCH); + __declspec(dllimport) BOOL WINAPI GetExitCodeProcess(HANDLE, LPDWORD); + __declspec(dllimport) BOOL WINAPI SetHandleInformation(HANDLE, DWORD, DWORD); + __declspec(dllimport) BOOL WINAPI TerminateProcess(HANDLE, UINT); + __declspec(dllimport) DWORD WINAPI FormatMessageA(DWORD, LPCVOID, DWORD, DWORD, LPSTR, DWORD, va_list*); + __declspec(dllimport) DWORD WINAPI GetLastError(VOID); + __declspec(dllimport) DWORD WINAPI WaitForSingleObject(HANDLE, DWORD); + __declspec(dllimport) HLOCAL WINAPI LocalFree(HLOCAL); + __declspec(dllimport) LPWSTR WINAPI GetEnvironmentStringsW(VOID); #define ZeroMemory(Destination,Length) memset((Destination),0,(Length)) #define MAKELANGID(p, s) ((((WORD)(s)) << 10) | (WORD)(p)) + #else + + #define SP_SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES + #define SP_PROCESS_INFORMATION PROCESS_INFORMATION + #define SP_STARTUPINFOW STARTUPINFOW + + #endif + #include #include #else @@ -417,15 +431,15 @@ namespace util inline void configure_pipe(HANDLE* read_handle, HANDLE* write_handle, HANDLE* child_handle) { - SECURITY_ATTRIBUTES saAttr; + SP_SECURITY_ATTRIBUTES saAttr; // Set the bInheritHandle flag so pipe handles are inherited. - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.nLength = sizeof(SP_SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; // Create a pipe for the child process's STDIN. - if (!CreatePipe(read_handle, write_handle, &saAttr,0)) + if (!CreatePipe(read_handle, write_handle, reinterpret_cast(&saAttr), 0)) throw OSError("CreatePipe", 0); // Ensure the write handle to the pipe for STDIN is not inherited. @@ -1677,19 +1691,19 @@ inline void Popen::execute_process() noexcept(false) // CreateProcessW can modify szCmdLine so we allocate needed memory wchar_t *szCmdline = new wchar_t[command_line.size() + 1]; wcscpy_s(szCmdline, command_line.size() + 1, command_line.c_str()); - PROCESS_INFORMATION piProcInfo; - STARTUPINFOW siStartInfo; + SP_PROCESS_INFORMATION piProcInfo; + SP_STARTUPINFOW siStartInfo; BOOL bSuccess = FALSE; DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW; // Set up members of the PROCESS_INFORMATION structure. - ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&piProcInfo, sizeof(SP_PROCESS_INFORMATION)); // Set up members of the STARTUPINFOW structure. // This structure specifies the STDIN and STDOUT handles for redirection. - ZeroMemory(&siStartInfo, sizeof(STARTUPINFOW)); - siStartInfo.cb = sizeof(STARTUPINFOW); + ZeroMemory(&siStartInfo, sizeof(SP_STARTUPINFOW)); + siStartInfo.cb = sizeof(SP_STARTUPINFOW); siStartInfo.hStdError = this->stream_.g_hChildStd_ERR_Wr; siStartInfo.hStdOutput = this->stream_.g_hChildStd_OUT_Wr; @@ -1708,8 +1722,8 @@ inline void Popen::execute_process() noexcept(false) creation_flags, // creation flags environment_string_table_ptr, // use provided environment cwd_arg, // use provided current directory - &siStartInfo, // STARTUPINFOW pointer - &piProcInfo); // receives PROCESS_INFORMATION + reinterpret_cast(&siStartInfo), // STARTUPINFOW pointer + reinterpret_cast(&piProcInfo)); // receives PROCESS_INFORMATION // If an error occurs, exit the application. if (!bSuccess) { @@ -2263,4 +2277,13 @@ OutBuffer pipeline(Args&&... args) } +#ifdef __USING_WINDOWS__ + #ifndef _WINDEF_ + #undef ZeroMemory + #undef MAKELANGID + #undef STATUS_WAIT_0 + #undef WAIT_OBJECT_0 + #endif +#endif + #endif // SUBPROCESS_HPP diff --git a/test/test_ret_code.cc b/test/test_ret_code.cc index 332079b..3c73243 100644 --- a/test/test_ret_code.cc +++ b/test/test_ret_code.cc @@ -1,5 +1,6 @@ #include #include +#include namespace sp = subprocess; From 13be8bc579a0971a85f17a80005da481ebc9a898 Mon Sep 17 00:00:00 2001 From: ImJotaM Date: Thu, 11 Sep 2025 17:36:07 -0300 Subject: [PATCH 3/3] fix(windows): Add platform-specific include for Windows.h Wraps the Windows.h include in a platform guard to prevent breaking non-Windows builds. --- test/test_ret_code.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_ret_code.cc b/test/test_ret_code.cc index 3c73243..e494bff 100644 --- a/test/test_ret_code.cc +++ b/test/test_ret_code.cc @@ -1,6 +1,9 @@ #include #include + +#ifdef __USING_WINDOWS__ #include +#endif namespace sp = subprocess;