最近写本地判题程序,有一步需要实现重定向子进程控制台程序的输入输出来与子进程交互,即让程序A向子进程B的stdin写入数据,并从子进程的stdout读出数据。
本以为只要用点>>
和<
之类重定向符号就能解决,百度一看还挺复杂,在参考Creating a Child Process with Redirected Input and Output - Win32 apps | Microsoft Docs和重定向子进程控制台程序的输入输出 - 绿色的麦田 - 博客园,我写出了下面的程序:
其中程序b负责从控制台读入一个n,和n个整数,并输出累加结果。
程序fb启动时需要指定一个参数作为子进程。运行时会向子进程的stdin
写如一个5
,然后写入5
个整数,然后获取子进程的stdout
并输出到自己的stdout
。如果你需要自己用的话只需要在IOWithChild()
写自己的逻辑代码,并调用ReadLineFromChild()
和WriteToChild()
进行输入输出操作即可。
我的编译器为Mingw-w64 g++ 8.1,会有警告信息但可以正常运行。
b.cpp
#include <cstdio>
using namespace std;
int main(){
int n;
scanf("%d",&n);
int ans=0;
for(int i=0;i<n;i++){
int x;
scanf("%d",&x);
ans+=x;
}
printf("sum is : %d",ans);
return 0;
}
fb.cpp
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#include <string>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(TCHAR filename[]);
void ErrorExit(PTSTR);
DWORD WriteLineToChild(const CHAR buf[],const DWORD numberOfBytesToWrite);
DWORD ReadFromChild(CHAR buf[]);
void IOWithChild(void){
/*
让子进程做累加运算,并获取计算结果
*/
int n=5;
int value[10]={1,3,5,7,9};
std::string sBuf;
int len;
sBuf=std::to_string(n);
printf("\n->Write to child:\n%s\n",sBuf.c_str());
WriteLineToChild(sBuf.c_str(),sBuf.length());
sBuf="";
for(int i=0;i<n;i++){
sBuf+=std::to_string(value[i]);
if(i!=n-1) sBuf+=' ';
}
printf("\n->Write to child:\n%s\n",sBuf.c_str());
WriteLineToChild(sBuf.c_str(),sBuf.length());
char cBuf[BUFSIZE];
ReadFromChild(cBuf);
printf("\n->read from child:\n%s\n", cBuf);
return;
}
DWORD WriteLineToChild(const CHAR buf[],const DWORD numberOfBytesToWrite){
BOOL bSuccess = FALSE;
CHAR chBuf[BUFSIZE];
DWORD dwWrittenChild;
int i;
for(i=0; i<numberOfBytesToWrite; i++){
chBuf[i]=buf[i];
}
chBuf[i++]='\r';
chBuf[i++]='\n';
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, i, &dwWrittenChild, NULL);
if ( ! bSuccess ) ErrorExit(TEXT("Write To Child Failed\n"));
return dwWrittenChild;
}
DWORD ReadFromChild(CHAR buf[]){
BOOL bSuccess = FALSE;
DWORD dwReadChild;
bSuccess = ReadFile( g_hChildStd_OUT_Rd, buf, BUFSIZE, &dwReadChild, NULL);
if( ! bSuccess) ErrorExit(TEXT("Read From Child Failed\n"));
return dwReadChild;
}
int _tmain(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.
if (argc == 1)
ErrorExit(TEXT("Please specify an child program.\n"));
CreateChildProcess(argv[1]);
IOWithChild();
// Close the pipe handle so the child process stops reading.
if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT("StdInWr CloseHandle"));
return 0;
}
void CreateChildProcess(TCHAR filename[])
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
filename, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if ( ! bSuccess )
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
}
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
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 );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}