问题描述
我正在使用 Windows 10。
这是我基于此文档的代码:
Creating a Child Process with Redirected Input and Output
这里是Parent进程的代码(我只修改了CreateChildProcess()
,其他都一样):
void CreateChildProcess()
// Create a child process that uses the prevIoUsly created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("ChildProcess");
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;
Security_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(Security_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create the child process.
//"C:\\Windows\\System32\\notepad.exe"
char(*exeName) = "C:\\Users\\kch79\\source\\repos\\MainProcess\\Debug\\ChildProcess.exe";
bSuccess = CreateProcess(exeName,szCmdline,// command line
NULL,// process security attributes
NULL,// primary thread security attributes
TRUE,// handles are inherited
CREATE_NEW_CONSOLE,// creation flags
NULL,// use parent's environment
NULL,// use parent's current directory
&siStartInfo,// STARTUPINFO pointer
&piProcInfo); // receives PROCESS_informatION
//WaitForInputIdle(piProcInfo.hProcess,INFINITE);
// 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.
WaitForSingleObject(piProcInfo.hProcess,INFINITE);
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);
}
}
#include <windows.h>
#include <stdio.h>
#pragma warning(disable:4996)
#define BUFSIZE 4096
int main(void)
{
CHAR chBuf[BUFSIZE];
DWORD dwRead,dwWritten;
HANDLE hStdin,hStdout;
BOOL bSuccess;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (
(hStdout == INVALID_HANDLE_VALUE) ||
(hStdin == INVALID_HANDLE_VALUE)
)
ExitProcess(1);
// Send something to this process's stdout using printf.
printf("\n ** This is a message from the child process. ** \n");
int number = 0;
scanf("%d",&number);
printf("%d\n",number);
//SetConsoleMode(hStdin,ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
//SetConsoleMode(hStdout,ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
// This simple algorithm uses the existence of the pipes to control execution.
// It relies on the pipe buffers to ensure that no data is lost.
// Larger applications would use more advanced process control.
for (;;)
{
// Read from standard input and stop on error or no data.
bSuccess = ReadFile(hStdin,chBuf,BUFSIZE,&dwRead,NULL);
if (!bSuccess || dwRead == 0)
break;
// Write to standard output and stop on error.
bSuccess = WriteFile(hStdout,dwRead,&dwWritten,NULL);
if (!bSuccess)
break;
}
return 0;
}
这些代码仅适用于 printf()
,但不适用于 scanf()
。
由 CreateProcess()
创建的新控制台窗口什么也不做。
我想控制控制台,但如何控制?
请告诉我我做错了什么。
解决方法
因为你通过如下代码将子进程的输入句柄重定向到管道:
CreatePipe(&g_hChildStd_IN_Rd,&g_hChildStd_IN_Wr,&saAttr,0)
......
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
所以你的子进程不能在新的控制台输入,而是需要通过父进程的管道输入。当然也可以将子进程的输入句柄设置为NULL
:
siStartInfo.hStdInput = NULL;
这样可以在子进程中输入,但同时父进程不能向子进程输入消息。
当然建议你把下面的代码放在main函数的最后,这样就可以观察到父进程得到子进程的输入。
WaitForSingleObject(piProcInfo.hProcess,INFINITE);
完整代码:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
PROCESS_INFORMATION piProcInfo;
int _tmain()
{
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,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"));
if (!SetHandleInformation(g_hChildStd_IN_Wr,0))
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// 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.
g_hInputFile = CreateFile(
L"D:\\test\\test.txt",GENERIC_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL);
if (g_hInputFile == INVALID_HANDLE_VALUE)
ErrorExit(TEXT("CreateFile"));
// 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();
printf("\n->Contents of %S written to child STDIN pipe.\n",L"D:\\test\\test.txt");
// Read from pipe that is the standard output for child process.
printf("\n->Contents of child process STDOUT:\n\n");
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.
WaitForSingleObject(piProcInfo.hProcess,INFINITE);
return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("ChildProcess");
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 = NULL;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create the child process.
TCHAR exeName[] = L"D:\\VS_test_projects\\Project2\\Debug\\c.exe";
bSuccess = CreateProcess(exeName,szCmdline,// command line
NULL,// process security attributes
NULL,// primary thread security attributes
TRUE,// handles are inherited
CREATE_NEW_CONSOLE,// 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"));
}
void WriteToPipe(void)
// 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[BUFSIZE] = "";
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile(g_hInputFile,chBuf,BUFSIZE,&dwRead,NULL);
if (!bSuccess || dwRead == 0) break;
bSuccess = WriteFile(g_hChildStd_IN_Wr,dwRead,&dwWritten,NULL);
if (!bSuccess) break;
}
// 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);
for (;;)
{
bSuccess = ReadFile(g_hChildStd_OUT_Rd,NULL);
if (!bSuccess || dwRead == 0) break;
bSuccess = WriteFile(hParentStdOut,NULL);
if (!bSuccess) break;
}
}
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,dw,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf,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,lpMsgBuf);
MessageBox(NULL,(LPCTSTR)lpDisplayBuf,TEXT("Error"),MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}