【发布时间】:2015-07-03 14:45:30
【问题描述】:
我正在拼命地尝试创建一个子进程并将其输出重定向到新管道并从这些管道中读取,但我就是无法让它工作。我是 Win32API 的新手,请善待我。 :)
在“正常”使用 Win32API 失败后,我创建了包装器以专注于查找 API 调用的逻辑和/或顺序中的错误。您可以在下面找到包装器的接口。由于大多数方法直接转换为 Win32API 调用,因此(希望)不会成为回答这个问题的障碍。
使用包装器类的行为与我最初体验过的相同。
我已经阅读了很多关于这个主题的在线资源,其中一个说的与另一个不同。到目前为止最有用的是https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx,尤其是这个信息(强调我的):
父进程使用这两个管道的相对端来写入子进程的输入并从子进程的输出中读取。正如 STARTUPINFO 结构中所指定的,这些句柄也是可继承的。但是,不得继承这些句柄。因此,在创建子进程之前,父进程使用SetHandleInformation函数来保证子进程标准输入的写句柄和子进程标准输入的读句柄不能被继承。有关详细信息,请参阅管道。
在我找到这个主题并从父进程端关闭我不使用的端之前,我在子进程的标准输出读取句柄上永远阻塞ReadFile()。现在,它总是立即返回管道已损坏。
这就是我创建管道和流程的方式:
Popen(const String& command, const String& args,
Bool use_current_pipes = false, Bool merge_stderr = true)
{
Bool ok = true;
_error = 0;
ZeroMemory(&_pi, sizeof(_pi));
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (!use_current_pipes) {
// Create pipes for standard input, output and error.
_stdin = Pipe(true);
_stdout = Pipe(true);
if (_stdout && merge_stderr)
_stderr = _stdout.Duplicate();
else
_stderr = Pipe(true);
if (_stdin && _stdout && _stderr) {
_stdin.w.SetInheritable(false);
_stderr.r.SetInheritable(false);
_stdout.r.SetInheritable(false);
si.hStdInput = _stdin.r.Get();
si.hStdOutput = _stdout.w.Get();
si.hStdError = _stderr.w.Get();
si.dwFlags |= STARTF_USESTDHANDLES;
}
else {
ok = false;
}
}
else {
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
}
// Create the process. Enclose the actual command in quotes.
ok = ok && CreateProcess(
nullptr, // command might contain whitespace, pass it quoted in arg 2 instead.
AutoString("\"" + command + "\" " + args),
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
true, // handles are inherited
0, // No creation flags
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&si, // Pointer to STARTUPINFO
&_pi); // Pointer to PROCESS_INFORMATION
// Something went wrong? Well, bad.
if (!ok) {
_error = GetLastError();
}
// Close the handles that have been inherited by the child process
// and to which we don't need access to, otherwise they will not
// close when the child exits.
_stdin.r.Close();
_stdout.w.Close();
_stderr.w.Close();
}
这就是我从标准输出 (_stdout.r) 读取的方式:
UInt Read(UInt num_bytes, char* buffer) {
if (!_stdout.r) return 0;
DWORD bytes_read = 0;
if (!ReadFile(_stdout.r.Get(), buffer, num_bytes - 1, &bytes_read, nullptr)) {
_error = GetLastError();
ConsoleOut("[ERROR]: ReadFile() : " + String::IntToString((Int32) _error));
if (_error == ERROR_BROKEN_PIPE) {
ConsoleOut("No Wait, the Pipe is just broken.");
_error = 0; // that's fine
}
return 0;
}
buffer[bytes_read] = '\0';
return bytes_read;
}
当我注释掉 Popen 构造函数的最后几行(关闭父进程中未使用的管道句柄)时,ReadFile() 将永远阻塞。启用这些行后,管道总是会立即中断(子进程很快退出)。
问题
- 有人能看出我的代码/逻辑有什么问题吗?
- 如果没有,如果有打开子进程并读取其输出的完整工作示例,我将不胜感激
包装接口
struct Handle {
HANDLE h;
explicit Handle();
explicit Handle(HANDLE h);
Handle(Handle&& other);
Handle& operator = (Handle&& other);
~Handle();
void Close();
HANDLE Get();
HANDLE Release();
Handle Duplicate(DWORD options = DUPLICATE_SAME_ACCESS, HANDLE src_proc = nullptr, HANDLE dst_proc = nullptr) const;
DWORD GetInfo() const; // uses GetHandleInformation
void SetInheritable(bool inheritable) const; // uses SetHandleInformation
bool GetInheritable() const;
operator bool() const;
explicit Handle(const Handle&) = delete;
Handle* operator = (const Handle&) = delete;
};
struct Pipe {
Handle r, w;
DWORD error;
explicit Pipe();
explicit Pipe(bool inheritable);
Pipe(Pipe&& other);
Pipe& operator = (Pipe&& other);
~Pipe();
void Close();
Pipe Duplicate(DWORD options = DUPLICATE_SAME_ACCESS, HANDLE src_proc = nullptr, HANDLE dst_proc = nullptr) const;
operator bool() const;
explicit Pipe(const Pipe&) = delete;
Pipe* operator = (const Pipe&) = delete;
};
【问题讨论】:
-
您确定子进程实际上正在写入标准输出吗?您是否尝试过使用命令行重定向
child.exe > output.txt来查看它是否有效? -
这几行可能有问题,有一个临时的
Pipe对象被立即销毁:_stdin = Pipe(true); _stdout = Pipe(true);但是你没有显示重要的代码。
标签: winapi pipe readfile createprocess