如何使用C语言传递输入并将输出检索到子进程

我在Windows中有一个exe程序,终端的工作原理如下

> program.exe parameter01 file entry01 (user types entry01) output01 entry02 (user types entry02) output02 ... until the combination Ctrl+D is pressed. 

我需要用C语言创建一个“子进程”,它能够运行程序并将条目发送到“子进程”并以char []或字符串forms接收输出。

我知道我必须使用CreateProcess方法,但我不知道如何传递输入之类的条目并检索输出,我该怎么做?

我用Java看过这个,但我需要用C语言实现这个function。

您可以尝试使用重定向输入和输出创建子进程,我调整了此处的代码

 #include  #include  #include  #include  #define BUFSIZE 4096 /* child process's STDIN is the user input or data that you enter into the child process - READ */ HANDLE g_hChildStd_IN_Rd = NULL; /* child process's STDIN is the user input or data that you enter into the child process - WRITE */ HANDLE g_hChildStd_IN_Wr = NULL; /* child process's STDOUT is the program output or data that child process returns - READ */ HANDLE g_hChildStd_OUT_Rd = NULL; /* child process's STDOUT is the program output or data that child process returns - WRITE */ HANDLE g_hChildStd_OUT_Wr = NULL; void CreateChildProcess(void); void WriteToPipe(CHAR chBuf[]); void ReadFromPipe(void); void ErrorExit(PTSTR); 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; //child process's STDOUT is the program output or data that child process returns // 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")); //child process's STDIN is the user input or data that you enter into the child process // 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. CreateChildProcess(); /* variables */ char FAR *lpsz; int cch; CHAR chBuf[BUFSIZE]; DWORD dwRead = strlen(chBuf); HANDLE hStdin; BOOL bSuccess; hStdin = GetStdHandle(STD_INPUT_HANDLE); if (hStdin == INVALID_HANDLE_VALUE) ExitProcess(1); for (;;) { // Read from standard input and stop on error or no data. bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL); if (!bSuccess || dwRead == 0) break; lpsz = &chBuf[0]; // Write to the pipe that is the standard input for a child process. // Data is written to the pipe's buffers, so it is not necessary to wait // until the child process is running before writing data. WriteToPipe(lpsz); printf("\n->Contents of %s written to child STDIN pipe.\n", argv[1]); // Read from pipe that is the standard output for child process. printf("\n->Contents of child process STDOUT:\n\n", argv[1]); ReadFromPipe(); printf("\n->End of parent execution.\n"); // The remaining open handles are cleaned up when this process terminates. // To avoid resource leaks in a larger application, close handles explicitly. } return 0; } void CreateChildProcess() // Create a child process that uses the previously created pipes for STDIN and STDOUT. { TCHAR szCmdline[] = TEXT("cmd.exe /c \"C:\\path\\to\\exe\\program.exe -parameter C:\\path\\to\\file\\file.txt\""); 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, szCmdline, // 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); } } void WriteToPipe(CHAR chBuf[]) // Read from a file and write its contents to the pipe for the child's STDIN. // Stop when there is no more data. { DWORD dwRead, dwWritten; // CHAR chBuf[] = "hola\n"; dwRead = strlen(chBuf); BOOL bSuccess = FALSE; bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL); if (!bSuccess) ErrorExit(TEXT("StdInWr Cannot write into child process.")); /* // Close the pipe handle so the child process stops reading. if (!CloseHandle(g_hChildStd_IN_Wr)) ErrorExit(TEXT("StdInWr CloseHandle")); */ } void ReadFromPipe(void) // Read output from the child process's pipe for STDOUT // and write to the parent process's pipe for STDOUT. // Stop when there is no more data. { DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; BOOL bSuccess = FALSE; HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); WORD wResult = 0; bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); if (!bSuccess || dwRead == 0) ErrorExit(TEXT("StdOutRd Cannot read child process's output.")); if (chBuf[0] == '+' && chBuf[1] == '?') { printf("It's misspelled."); } else { printf("It's spelled correctly."); } // bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL); // if (!bSuccess) ErrorExit(TEXT("StdOutWr Cannot write into parent process's output.")); } 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); } 

使用STARTUPINFO结构

您必须设置属性hStdInput

越来越少,这是你需要的(它是C ++代码,它可能无法编译,但你会得到这个想法):

 std::string GetProcessOutput(HANDLE hStdOutProcess) { std::stringstream strStream; char lpBuffer[2] = {0}; DWORD nBytesRead; while(true){ BOOL bResult = ReadFile(hStdOutProcess, lpBuffer, sizeof(char), &nBytesRead, NULL); if (bResult && nBytesRead) { strStream << lpBuffer; } else { break; } } return strStream.str(); } void RunAndGetOutout() { HANDLE hProcessStdOutRead = NULL; HANDLE hProcessStdOutWrite = NULL; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; // allow to inherit handles from parent process saAttr.lpSecurityDescriptor = NULL; if(!CreatePipe(&hProcessStdOutRead, &hProcessStdOutWrite, &saAttr, 0)) { return nResult; } if(!SetHandleInformation(hProcessStdOutRead, HANDLE_FLAG_INHERIT, 0)) { return nResult; } STARTUPINFO startInfo; PROCESS_INFORMATION processInfo; char cmdLine[ MAX_PATH*2 +40] = {0}; char currentDir[MAX_PATH] = {0}; ZeroMemory(&startInfo, sizeof(startInfo)); startInfo.cb = sizeof(startInfo); startInfo.hStdOutput = hProcessStdOutWrite; // set the handle startInfo.dwFlags |= STARTF_USESTDHANDLES; // attention with this one ZeroMemory(&processInfo, sizeof(processInfo)); GetCurrentDirectory(MAX_PATH, currentDir); sprintf(cmdLine, "\"%s\" %s", (const char*)m_path2Process, (const char*)m_processArgs); if(CreateProcess( NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, currentDir, &startInfo, &processInfo)) { cout << GetProcessOutput(hProcessStdOutRead) << endl; CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); } CloseHandle(hProcessStdOutRead); CloseHandle(hProcessStdOutWrite) } 

一个简单的例子:

 #include  void SigQuit_Handle(int sig){ exit(1); } int main(int argc, char *argv[]){ char buffer[1024]; signal( SIGQUIT, SigQuit_Handle ); signal( SIGINT, SIG_IGN ); // If you want to ignore Ctrl + C while ( true ){ fgets(buffer, sizeof(buffer), INPUT_BUFFER); } return 0; } 

编辑:如果处理线程,您可能需要包含sys / types.h。

基本上,您需要在Win32平台上创建进程间通信系统。

您可以通过几种不同的方式实现:管道,共享内存,IPC,WinSock,DDE ……

所有这些function都竞争为您提供最疯狂的API,包括大量不一致和无用的参数,非标准化的返回码和不连贯的函数名称。 除此之外,还有大约1995年的半独特的血腥尴尬处理。

这是一个带命名管道的示例。

 #include  #include  #include  // name of our glorious pipe #define PIPE_NAME L"\\\\.\\pipe\\whatever" // bloody unicode string // exit on fatal error void panic(const char * msg) { fprintf(stderr, "***PANIC*** %s\n", msg); exit(-1); } // father process void father(const char * own_name) // name of our own executable to launch a copy of ourselve { printf("Father process starting\n"); // create a monodirectional father->child named pipe HANDLE pipe = CreateNamedPipe ( PIPE_NAME, // name of the pipe PIPE_ACCESS_OUTBOUND, // send only PIPE_TYPE_BYTE, // send data as a byte stream 1, // only one instance 0, 0, 0, NULL); // default junk if (pipe == INVALID_HANDLE_VALUE) panic("could not create pipe"); // spawn child process { STARTUPINFOA si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessA( // using ASCII variant to be compatible with argv own_name, // executable name (ourself) "child", // command line. This will be seen as argv[0] NULL, NULL, FALSE, // default junk CREATE_NEW_CONSOLE, // launch in another console window NULL, NULL, // more junk &si, &pi)) // final useless junk panic("could not create child process"); } // connect to child process BOOL result = ConnectNamedPipe(pipe, NULL); if (!result) panic("could not connect to child process"); // talk to child for (;;) { // read an input line char line[100]; printf("Say something >"); if (fgets(line, sizeof(line), stdin) == NULL) panic("could not read from standard input"); // exit on an empty line if (!strcmp(line, "\n")) break; // send the line to the child DWORD written = 0; if (!WriteFile( pipe, line, // sent data strlen(line), // data length &written, // bytes actually written NULL)) panic("could not write to pipe"); } // close the pipe CloseHandle(pipe); } void child(void) { printf("Child process starting\n"); // retrieve communication pipe HANDLE pipe = CreateFile( PIPE_NAME, // name of the pipe GENERIC_READ, // read ONLY access (or else the call will fail) 0, NULL, // junk OPEN_EXISTING, // opens existing pipe 0, NULL); // more junk if (pipe == INVALID_HANDLE_VALUE) panic("could not connect to the pipe"); // read father's input for (;;) { char buffer[80]; DWORD read = 0; if (!ReadFile( pipe, buffer, // read data sizeof(buffer)-1, // max length (leave room for terminator) &read, // bytes actually read NULL)) break; // exit if the pipe has closed // display what our father said buffer[read] = '\0'; // make sure what we just read will be displayable as a string printf("Father said: %s", buffer); } // close pipe CloseHandle(pipe); } int main(int argc, char *argv[]) { // wait for a  keypress on exit atexit(getchar); // decide whether we are the father or the child if (!strcmp (argv[0], "child")) child(); else father(argv[0]); printf("Done\n"); return 0; } 

对不起,我找不到使用Ctrl-D作为退出信号的优雅方式。 我想到实现这一目标的唯一方法就是需要另外几个非常繁琐的系统调用,我被前景吓倒了。

因此父亲将在用户输入空行时终止。

通过关闭管道,父亲将触发读取错误,该错误也会使孩子摆脱痛苦(即,当孩子在管道上获得读取错误时,孩子会突破读取循环并死亡)。 您可以轻松让孩子对任何消息做出反应,如果这样可以更好地取悦您的老师。

我已经添加了额外的等待另一个按键,以防你从IDE运行它,以避免太突然关闭窗口。 如果你不想要,只需删除main()开头的atexit