c++ - ReadFileEx,可变长度——几个问题

标签 c++ windows file-io 64-bit overlapped-io

我正在尝试从子进程的 stderr 中读取。数据是使用 sprintf(stderr, "some debug info\n") 创建的文本行。我将 ReadFileEx 与完成例程一起使用。我不知道有多少行文本或每行有多长。那么,我应该将什么作为 nNumberOfBytesToRead 参数?

我的猜测是我将缓冲区的最大大小设置为 4k;虽然我不知道这是否是最佳尺寸。我猜测如果写入 stderr 的行短于 4k,则不会触发完成例程。我猜测当达到 4k 但仍有更多数据时,我必须在完成例程中触发另一个 ReadFileEx。我知道是这种情况,因为 GetLastError 将返回 ERROR_MORE_DATA。我希望在缓冲区未满但子进程已退出时接到电话。我不确定子进程退出时我是否得到完成回调,因为我在创建子进程时将 stderr 写句柄传递给了它;也许我在 关闭该句柄时收到回调。当 child 对我的阅读 stderr 关闭 wrt 时是否存在任何竞争条件?

下面是如何创建进程和句柄的伪代码:

Attr.bInheritHandle = true
CreatePipe(&hr, &hw, &Attr, 0) and SetHandleInformation(hX, HANDLE_FLAG_INHERIT) on hX the child uses.
Si.hStdXXX = handles from CreatePipe that child uses
CreateProcess(inherit=true, &Si)

详细信息(Tx 扩展是一个抛出错误的包装器):

HANDLE Create() {
    STARTUPINFO SI, *pSI = NULL;
    bool fInherit = m_fInherit;
    if (m_fStdOut || m_fStdIn || m_fStdErr) {
        fInherit = true;
        SECURITY_ATTRIBUTES Attr;
        Attr.nLength = sizeof(SECURITY_ATTRIBUTES); 
        Attr.bInheritHandle = TRUE; 
        Attr.lpSecurityDescriptor = NULL;
        if (m_fStdOut) // Create a pipe for the child process's STDOUT. The child will use the write.
            CHandle::CreatePipe(m_hStdOutR, m_hStdOutW, &Attr, CP_INHERIT_WRITE);
        if (m_fStdErr) // Create a pipe for the child process's STDERR. The child will use the write.
            CHandle::CreatePipe(m_hStdErrR, m_hStdErrW, &Attr, CP_INHERIT_WRITE);
        if (m_fStdIn) // Create a pipe for the child process's STDIN. The child will use the read.
            CHandle::CreatePipe(m_hStdInR, m_hStdInW, &Attr, CP_INHERIT_READ);
        // Set up members of the STARTUPINFO structure. 
        // This structure specifies the STDIN and STDOUT handles for redirection.
        ZeroStruct(SI);
        SI.cb = sizeof(STARTUPINFO); 
        SI.hStdError = m_hStdErrW, SI.hStdOutput = m_hStdOutW, SI.hStdInput = m_hStdInR;
        SI.dwFlags |= STARTF_USESTDHANDLES;
        pSI = &SI;
    }
    //  m_fCpu, m_fNuma are masks to set affinity to cpus or numas
    CreateProcessTx(NULL, m_szCmdLine, fInherit, m_fFlags, pSI, &m_pi, m_fCpu, m_fNuma, 5);
    m_hProc = m_pi.hProcess;
    m_hThread = m_pi.hThread;
    if (!m_fThread)
        m_hThread.Close();
    return m_hProc;
}

static void CreatePipe(CHandle &hRead, CHandle &hWrite, SECURITY_ATTRIBUTES* pAttr, BYTE fInheritMask) {
    HANDLE hReadTmp = NULL, hWriteTmp = NULL;
    CreatePipeTx(hReadTmp, hWriteTmp, pAttr);
    SetHandleInformation(hReadTmp, HANDLE_FLAG_INHERIT, (fInheritMask&CP_INHERIT_READ) ? HANDLE_FLAG_INHERIT : 0); 
    SetHandleInformation(hWriteTmp, HANDLE_FLAG_INHERIT, (fInheritMask&CP_INHERIT_WRITE) ? HANDLE_FLAG_INHERIT : 0);
    hRead = hReadTmp;
    hWrite = hWriteTmp;
}

最佳答案

CreatePipe 创建的匿名管道不能异步使用。来自 Windows SDK 文档:

Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. This means that you cannot use the ReadFileEx and WriteFileEx functions with anonymous pipes. >In addition, the lpOverlapped parameter of ReadFile and WriteFile is ignored when these >functions are used with anonymous pipes.

基本上 CreatePipe 不接受 FILE_FLAG_OVERLAPPED 标志,异步 I/O 要求您在创建文件句柄时使用该标志。

您必须使用 CreateNamedPipe 来创建命名管道。问题Overlapped I/O on anonymous pipe有一个链接到您可以使用的替换函数 MyCreatePipeEx 的答案。

在尝试从另一端已关闭的管道读取后,您的完成端口应该会收到一个零长度读取事件。

要从客户端进程读取可变数量的数据,只需发出您认为方便的任何大小的读取请求,并准备好处理比您请求的更短的读取事件。不要将短但非零的长度解释为 EOF。继续发出读取请求,直到读取长度为零或出现错误。

此外,WaitForMultipleObjects 不会与完成例程一起使用,因为它们仅在线程处于 alterable state 中时被调用。 .使用 WaitForMultipleObjectEx 并将 bAlertable 参数设置为 true。此函数将在运行一个或多个完成例程后返回 WAIT_IO_COMPLETION。在这种情况下,您可能希望立即再次调用 WaitForMultipleObjectEx

关于c++ - ReadFileEx,可变长度——几个问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24916823/

相关文章:

c++ - 奇怪的重复模板 - 变体

C++多级继承

windows - VB6 程序使用的 32 位 dll 在 64 位 Win 7 中不起作用

windows - PSCP 可以与 PowerShell 一起使用吗?

c++ - ifstream 不会读取整数或其他任何与此相关的内容

c++ - 如何在不使用 cmath 的情况下返回数字的上限

c++ - 通过自定义语法使用 Boost Spirit 的流解析器

c - PsGetContextThread 返回 C0000005(ACCESS_VIOLATION)

java - 读取word文件并保存为odt

java file.renameTo() 重命名文件但返回 false。为什么?