Windows下C程序:子进程stdout同时输出控制台与回传父进程
Windows C语言父子进程通信:子进程stdout同时输出控制台+回传父进程
核心思路
Windows系统中,标准输出句柄无法直接同时指向控制台和管道,因此需通过以下两种方式实现需求:
- 子进程主动分流输出:同时向控制台(stdout)和管道写入数据
- 父进程转发管道内容:将子进程重定向到管道的stdout内容,读取后转发到控制台并保存结果
方案1:子进程主动分流输出(灵活可控)
父进程创建单向管道用于子进程回传数据,子进程保持stdout默认指向控制台,同时在需要回传的逻辑中,既输出到控制台,又通过管道写入数据。
父进程代码
#include <windows.h> #include <stdio.h> int main() { HANDLE hReadPipe, hWritePipe; SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; // 创建单向管道:子进程写,父进程读 if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) { printf("CreatePipe 失败,错误码:%d\n", GetLastError()); return 1; } STARTUPINFO si = {sizeof(STARTUPINFO)}; PROCESS_INFORMATION pi; char cmdLine[256]; // 配置子进程启动信息:stdout/stderr保留控制台句柄 si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); si.hStdError = GetStdHandle(STD_ERROR_HANDLE); // 设置管道写句柄可被子进程继承 SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); // 拼接子进程启动命令,传递管道句柄(64位系统需用_atoi64) sprintf(cmdLine, "child.exe %d", (DWORD)hWritePipe); if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { printf("CreateProcess 失败,错误码:%d\n", GetLastError()); CloseHandle(hReadPipe); CloseHandle(hWritePipe); return 1; } // 父进程关闭不需要的写句柄(否则管道不会触发EOF) CloseHandle(hWritePipe); // 读取子进程回传的数据 char buffer[1024]; DWORD bytesRead; while (ReadFile(hReadPipe, buffer, sizeof(buffer)-1, &bytesRead, NULL) && bytesRead > 0) { buffer[bytesRead] = '\0'; printf("[父进程接收] %s\n", buffer); } // 等待子进程结束并清理资源 WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hReadPipe); return 0; }
子进程代码
#include <windows.h> #include <stdio.h> #include <string.h> int main(int argc, char* argv[]) { if (argc < 2) { fprintf(stdout, "[子进程] 缺少管道句柄参数\n"); return 1; } // 转换命令行参数为管道句柄(64位系统用_atoi64) HANDLE hWritePipe = (HANDLE)atoi(argv[1]); // 验证并复制句柄(避免权限问题) if (!DuplicateHandle(GetCurrentProcess(), hWritePipe, GetCurrentProcess(), &hWritePipe, 0, FALSE, DUPLICATE_SAME_ACCESS)) { fprintf(stdout, "[子进程] 复制句柄失败,错误码:%d\n", GetLastError()); return 1; } // 读取父进程传递的输入(示例:从stdin读取) char input[1024]; fprintf(stdout, "[子进程] 等待父进程输入...\n"); fgets(input, sizeof(input), stdin); fprintf(stdout, "[子进程] 收到输入:%s", input); // 处理数据(示例:转大写) for (int i = 0; input[i] != '\0'; i++) { if (input[i] >= 'a' && input[i] <= 'z') { input[i] -= 32; } } // 同时输出到控制台和管道 fprintf(stdout, "[子进程] 处理结果:%s", input); DWORD bytesWritten; WriteFile(hWritePipe, input, strlen(input), &bytesWritten, NULL); CloseHandle(hWritePipe); return 0; }
方案2:父进程转发管道内容(全量捕获)
将子进程的stdout重定向到管道,父进程读取管道内容后,既转发到控制台,又保存处理结果。此方案无需修改子进程的输出逻辑,适合需要捕获所有stdout内容的场景。
父进程关键代码调整
// 创建管道(同方案1) if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) { /* 错误处理 */ } STARTUPINFO si = {sizeof(STARTUPINFO)}; // 将子进程stdout重定向到管道,stderr保留控制台 si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = hWritePipe; si.hStdError = GetStdHandle(STD_ERROR_HANDLE); si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); // 创建子进程后关闭写句柄(同方案1) CloseHandle(hWritePipe); // 读取管道并转发到控制台 char buffer[1024]; DWORD bytesRead; while (ReadFile(hReadPipe, buffer, sizeof(buffer)-1, &bytesRead, NULL) && bytesRead > 0) { buffer[bytesRead] = '\0'; // 转发到控制台 fprintf(stdout, "%s", buffer); // 保存处理结果(按需实现) // ... }
子进程代码
无需额外修改,正常使用fprintf(stdout)输出即可,内容会通过管道被父进程捕获并转发。
常见问题排查
- 管道句柄继承问题:必须调用
SetHandleInformation设置管道句柄为可继承,否则子进程无法获取有效句柄 - 管道阻塞问题:父进程需及时关闭不需要的管道句柄,否则
ReadFile会一直阻塞等待EOF - 缓冲问题:子进程使用C标准库输出时,可调用
fflush(stdout)强制刷新缓冲区,避免内容滞留 - 64位系统适配:传递句柄时需用
_atoi64替代atoi,避免32位截断
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

