【发布时间】:2019-10-08 21:19:16
【问题描述】:
我正在尝试读取从 CreateProcess 开始的外部进程 (pgdump) 的标准输出。我使用匿名管道进行了此操作,但随后输出被阻止,并且不像我通过命令行执行它时那样(缺少最终输出)。我已经阅读了很多帖子,发现我需要 CreateNamedPipes 和 WaitForSingleObject 但我似乎无法让它工作。这是我使用匿名管道的工作代码,但被阻止了,我错过了输出的结尾
#include <QDebug>
#include <QString>
#include <windows.h>
#include <sstream>
#include <iostream>
#include <random>
int main()
{
#define BUFFERSIZE 256
std::string program = "\"C:\\Program Files (x86)\\PostgreSQL\\10\\bin\\pg_dump.exe\"" +
std::string( " --dbname=postgresql://postgresUser:PostGresql13@127.0.0.1:5432/employee -j1 -Fd -b -v -f "
"C:\\development\\myproject\\Build\\debug\\1-export.psql");
HANDLE hReadStdOut = NULL;
HANDLE hWriteStdOut = NULL;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
ZeroMemory( &saAttr, sizeof( SECURITY_ATTRIBUTES ));
ZeroMemory( &piProcInfo, sizeof( PROCESS_INFORMATION ));
ZeroMemory( &siStartInfo, sizeof( STARTUPINFO ));
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
OVERLAPPED sOverlap;
if( !CreatePipe(&hReadStdOut, &hWriteStdOut, &saAttr, 0) )
{
std::ostringstream os;
os << GetLastError();
qDebug() << "create pipe error : " << QString::fromStdString( os.str());
}
TCHAR* szCmdline = new TCHAR[ program.size() + 1];
szCmdline[ program.size()] = 0;
std::copy( program.begin(), program.end(), szCmdline );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hWriteStdOut;
siStartInfo.hStdOutput = hWriteStdOut;
siStartInfo.hStdInput = NULL;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
BOOL bSuccess = CreateProcess( NULL, szCmdline, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &siStartInfo,&piProcInfo );
if ( ! bSuccess )
{
std::ostringstream os;
os << GetLastError();
qDebug() << "create process error : " << QString::fromStdString( os.str());
}
else
{
CloseHandle( hWriteStdOut );
DWORD err;
DWORD nBytesRead;
char buf[BUFFERSIZE + 1];
int i(1);
for(;;)
{
std::cout << "iteration " << std::to_string( i ) << std::endl;
if( !ReadFile( hReadStdOut, buf, sizeof( buf), &nBytesRead, NULL) || !nBytesRead )
{}
if( GetLastError() == ERROR_SUCCESS )
{
}
std::cout.flush();
buf[nBytesRead] = '\0';
std::string string_ = buf;
std::cout << string_ << std::endl;
std::size_t found = string_.find("contents of");
if( !nBytesRead )
break;
i++;
}
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
return 0;
}
【问题讨论】:
-
siStartInfo.hStdError = pipe;//hWriteStdOut; siStartInfo.hStdOutput = pipe;//hWriteStdOut;- 您显示的代码中没有pipe变量。至于阻塞问题,您可以使用PeekNamedPipe()和匿名管道来检测有多少数据可供读取,然后再读取。在任何情况下,您都说您在使用命名管道时遇到了问题,但是这段代码中没有命名管道。看看Overlapped I/O on anonymous pipe -
@RemyLebeau,是的,你是对的我认为我的工作代码是一个好的开始,所以也许有人可以修改它,这样我就会明白我做错了什么
-
windows 中只有一种常见的管道类型。管道是否有名称 - 对他的行为没有影响。所以命名管道和匿名(未命名)管道之间没有任何区别。同步管道上的死锁经常是因为同步文件上的所有 i/o 操作都是序列化的 - 新的没有开始,直到旧的没有完成。因为这通常使用 2 个不同的管道对进行输入和输出,以避免死锁。但在你的情况下,你没有输入管道 - 不清楚它在哪里。异步管道永远不会出现死锁问题,因为 i/o 未序列化。但客户端几乎从未准备好使用异步管道
-
所以这里的解决方案可以在服务器端使用异步管道,在客户端使用同步端。在这种情况下,通常我们可以使用单管道对(双工管道)来代替 2. 但同样 - 因为您的客户端 pg_dump.exe 只能写入管道 - 不清楚死锁在哪里或什么
-
@RbMm,你能告诉我如何修改我现有的代码或我另一篇文章中提到的代码吗?