c - 进程完成时如何使Windows Pipe添加EOF
问题描述
我正在 Windows 上创建一个模拟管道的递归 C 函数。
到目前为止,这是我的功能:
static BOOL execute_in_pipe(char (*cmdline)[1024], char *env, int index,
int pipe_length, STARTUPINFO *old_suinfo, HANDLE proc_handles[])
{
//Create new startup info
STARTUPINFO suinfo;
memset(&suinfo, 0, sizeof(suinfo));
suinfo.cb = sizeof(suinfo);
//Create new process info
PROCESS_INFORMATION pinfo;
//Make pipe
HANDLE pipe_out_rd, pipe_out_wr;
if(index > 0) {
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (! CreatePipe(&pipe_out_rd, &pipe_out_wr, &sa, 0))
{
CloseHandle(pipe_out_wr);
CloseHandle(pipe_out_rd);
//res.tag = NATIVE_ERR_PIPE_FAILED;
//return res;
}
//Set output of last process to write handle
if((*old_suinfo).hStdInput == 0) {
(*old_suinfo).hStdInput = hIn;
}
(*old_suinfo).hStdOutput = pipe_out_wr;
(*old_suinfo).dwFlags = STARTF_USESTDHANDLES;
//Set input of this process to read handle
suinfo.hStdInput = pipe_out_rd;
if(index >= pipe_length-1) {
suinfo.hStdOutput = hOut;
}
suinfo.dwFlags = STARTF_USESTDHANDLES;
}
BOOL result = TRUE;
if(index < pipe_length-1) {
result = execute_in_pipe(cmdline, env, index+1, pipe_length, &suinfo, proc_handles);
}
//Return whether creating process succeeds
if(!CreateProcess(NULL, cmdline[index], NULL, NULL,
(pipe_length>1), 0, env, NULL, &suinfo, &pinfo))
{
return FALSE;
}
CloseHandle(pinfo.hThread);
proc_handles[index] = pinfo.hProcess;
return result;
}
我制作了两个 c 程序来测试管道:
种子.c
#include <stdio.h>
int main() {
printf("Hello");
}
附加.c
#include <stdio.h>
#include <string.h>
int main() {
char str[200];
scanf( "%s", str );
printf("%s World!", str);
}
我使用以下方法调用这些函数:
STARTUPINFO suinfo;
HANDLE proc_handles[2];
printf("Starting\n");
char cmdline[2][1024] = {"seed.exe", "append.exe"};
execute_in_pipe(cmdline, 0, 0, 2, &suinfo, proc_handles);
WaitForMultipleObjects(2, proc_handles, TRUE, INFINITE);
调用此函数时,控制台会打印出“Starting”,然后等待……永远……
我很确定原因是 scanf 正在尝试读取整行(直到某种分隔符)。当我将 seed.c 更新为 `printf("Hello\n") 时,它运行良好。
所以,是的,这只是一个规则吗?所有管道进程都应该使用换行符吗?
好吧,调用seed.exe | append.exe
命令提示符效果很好!即使没有换行符!我的代码缺少什么?
编辑:正如一些人所指出的,当所有管道都关闭时,Unix 管道会进入“流结束”(不是我最初认为的 EOF)。在测试 Unix 管道系统时,没有出现这个问题,所以 end-of-stream 就像一个分隔符。Windows会做类似的事情吗?如果是这样,我是否必须关闭某些东西才能使其结束流?
解决方案
您应该使用命名管道而不是未命名管道来执行 I/O 重定向。当服务器的管道句柄关闭时,客户端上的 scanf() 将知道它并停止等待换行符。
下面的代码是帕斯卡,但不是很难理解。
const
BUFSIZE:integer = 512;
Pipename:String = '\\.\pipe\devcpp_run';
InputWrite := CreateNamedPipe(
pAnsiChar(Pipename), // pipe name
PIPE_ACCESS_OUTBOUND, // read/write access
PIPE_TYPE_BYTE or // message type pipe
PIPE_READMODE_BYTE or // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
0, // client time-out
nil); // default security attribute
if InputWrite = INVALID_HANDLE_VALUE then begin
Exit;
end;
InputRead := CreateFile(
PAnsiChar(Pipename), // pipe name
GENERIC_READ,
0, // no sharing
@sa, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
0);
if InputRead = INVALID_HANDLE_VALUE then begin
Exit;
end;
StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESTDHANDLES;
StartupInfo.hStdInput := InputRead;
StartupInfo.hStdOutput := 7; // Undocumented Handle of Console Output
StartupInfo.hStdError := 11; // Undocumented Handle of Console Error Output
end;