Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 从管道中随机读取失败_C++_Windows - Fatal编程技术网

C++ 从管道中随机读取失败

C++ 从管道中随机读取失败,c++,windows,C++,Windows,我正在为命令行可执行文件编写集成测试驱动程序。我控制驱动程序和可执行文件,因此我可以保证它们的行为——例如,可执行文件从不从stdin读取数据,它只接受命令行参数,执行它的操作,然后将输出写入文件和stdout 我希望捕获流程的退出代码和标准输出以进行验证 以下是我正在使用的代码: #include <Windows.h> class Pipe { HANDLE ReadHandle; HANDLE writehandle; public: Pipe() {

我正在为命令行可执行文件编写集成测试驱动程序。我控制驱动程序和可执行文件,因此我可以保证它们的行为——例如,可执行文件从不从stdin读取数据,它只接受命令行参数,执行它的操作,然后将输出写入文件和stdout

我希望捕获流程的退出代码和标准输出以进行验证

以下是我正在使用的代码:

#include <Windows.h>

class Pipe {
    HANDLE ReadHandle;
    HANDLE writehandle;
public:
    Pipe() {
        SECURITY_ATTRIBUTES saAttr;
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        saAttr.bInheritHandle = TRUE;
        saAttr.lpSecurityDescriptor = NULL;
        CreatePipe(&ReadHandle, &writehandle, &saAttr, 0);
    }
    HANDLE WriteHandle() {
        return writehandle;
    }
    std::string Contents() {
        CloseHandle(writehandle);
        DWORD dwRead;
        CHAR chBuf[1024];
        BOOL bSuccess = FALSE;

        std::string result;
        for (;;)
        {
            bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
            if (!bSuccess) break;
            result += std::string(chBuf, chBuf + dwRead);
            if (dwRead < 1024)
                break;
        }
        return result;
    }
    ~Pipe() {
        CloseHandle(ReadHandle);
    }
};
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout)
{
    ProcessResult result;
    Pipe stdoutpipe;
    PROCESS_INFORMATION info = { 0 };
    STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
    std::string final_args = name;
    for (auto arg : args)
         final_args += " " + arg;
    startinfo.hStdOutput = stdoutpipe.WriteHandle();
    startinfo.hStdError = INVALID_HANDLE_VALUE;
    startinfo.hStdInput = INVALID_HANDLE_VALUE;
    startinfo.dwFlags |= STARTF_USESTDHANDLES;
    auto proc = CreateProcess(
        name.c_str(),
        &final_args[0],
        nullptr,
        nullptr,
        TRUE,
        NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
        nullptr,
        nullptr,
        &startinfo,
        &info
         );
    if (!proc) {
        DWORD dw = GetLastError();
        const char* message;
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, nullptr);
        std::string err = message;
        LocalFree((void*)message);
        throw std::runtime_error(err);        
    }
    if (timeout == 0)
        timeout = INFINITE;

    result.std_out = stdoutpipe.Contents();
    if (WaitForSingleObject(info.hProcess, timeout ? *timeout : INFINITE) == WAIT_TIMEOUT)
         TerminateProcess(info.hProcess, 1);

    DWORD exit_code;
    GetExitCodeProcess(info.hProcess, &exit_code);
    CloseHandle(info.hProcess);
    CloseHandle(info.hThread);
    result.exitcode = exit_code;
    if (exit_code != 0)
        return result;
    return result;
}
#包括
等级管道{
句柄读取句柄;
手柄可写手柄;
公众:
管道(){
安全属性saAttr;
saAttr.nLength=sizeof(安全属性);
saAttr.bInheritHandle=TRUE;
saAttr.lpSecurityDescriptor=NULL;
CreatePipe(&ReadHandle,&writehandle,&saAttr,0);
}
句柄WriteHandle(){
返回writehandle;
}
std::string Contents(){
闭合手柄(书写手柄);
德沃德·德雷德;
CHAR-chBuf[1024];
BOOL bsucces=假;
std::字符串结果;
对于(;;)
{
bSuccess=ReadFile(ReadHandle,chBuf,1024,&dwRead,NULL);
如果(!b成功)中断;
结果+=标准::字符串(chBuf,chBuf+dwRead);
如果(dwRead<1024)
打破
}
返回结果;
}
~Pipe(){
CloseHandle(ReadHandle);
}
};
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name,std::vector args,Util::可选超时)
{
处理结果;
管道标准管;
进程信息信息={0};
STARTUPINFO startinfo={sizeof(STARTUPINFO)};
std::string final_args=name;
用于(自动参数:参数)
最终参数+=“”+arg;
startinfo.hStdOutput=stdoutpipe.WriteHandle();
startinfo.hStdError=无效的\u句柄\u值;
startinfo.hStdInput=无效的\u句柄\u值;
startinfo.dwFlags |=STARTF_USESTDHANDLES;
auto proc=CreateProcess(
name.c_str(),
&最终参数[0],
nullptr,
nullptr,
是的,
正常_优先级|类|创建_否|窗口,
nullptr,
nullptr,
&startinfo,
&信息
);
如果(!proc){
DWORD dw=GetLastError();
const char*消息;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_Insert、,
nullptr,dw,MAKELANGID(LANG_中立,SUBLANG_默认),(LPTSTR)和message,0,nullptr);
std::string err=消息;
本地自由((void*)消息);
抛出std::运行时错误(err);
}
如果(超时==0)
超时=无限;
result.std_out=stdoutpipe.Contents();
if(WaitForSingleObject(info.hProcess,timeout?*timeout:INFINITE)=等待超时)
终止进程(info.hProcess,1);
DWORD出口代码;
GetExitCodeProcess(info.hProcess和exit_代码);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
result.exitcode=exit_代码;
如果(退出代码!=0)
返回结果;
返回结果;
}
我已经用这种方式运行了259个集成测试。有些人比其他人花的时间更长。当我运行套件时,大约有1-3个会失败-每次都会有不同的失败。我已经在调试器中查看了结果,并且标准输出在一半的时候被切断。如果我不尝试捕获标准输出,所有测试每次都会成功,因此我知道它是基于标准输出捕获的

虽然指定了超时时间,但这是一个非常宽泛的60秒——比测试正常运行所需的时间长得多。我为每个测试生成一个新流程

如何以更可靠的方式捕获标准输出,而不出现随机故障


最后,运行套件以捕获调试器中的故障需要很长时间,因此可能需要一段时间来处理任何有关进一步信息的请求。

我对此有一个理论,但我不能完全确定。关键是读取进程标准输出的循环条件

std::string result;
for (;;)
{
    bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
    if (!bSuccess) break;
    result += std::string(chBuf, chBuf + dwRead);
    if (dwRead < 1024)
        break;
}
return result;
它并没有说当写操作完成并且请求的字节数可用时,它将返回。事实上,它不会对写操作做出任何评论,使您请求的字节数尽可能多。因此,它的行为是半异步的,即使是同步调用也是如此

我将循环重写如下:

std::string result;
for (;;)
{
    bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
    if (!bSuccess || dwRead == 0) break;
    result += std::string(chBuf, chBuf + dwRead);
}
return result;
到目前为止,我无法用这个循环重现失败(测试完成得明显更快)。

是的,管道上的ReadFile()在到达相应WriteFile()的末尾时总是返回。除非写入行的长度与缓冲区大小一致,否则这意味着它返回的字节数将小于请求的字节数,即使管道尚未关闭,而且我认为即使以后的写入仍然有挂起的数据。
std::string result;
for (;;)
{
    bSuccess = ReadFile(ReadHandle, chBuf, 1024, &dwRead, NULL);
    if (!bSuccess || dwRead == 0) break;
    result += std::string(chBuf, chBuf + dwRead);
}
return result;