c++ - CreateEvent 初始状态已发信号不是发信号事件

标签 c++ windows ipc msdn io-redirection

我正在开发 IO 重定向程序,并且我成功地为其创建了 poc。该程序生成子进程并使用命名管道与其进行通信。每当管道上有数据时,我就使用事件对象来获取事件。我默认将事件设置为有信号状态,但我不是第一次收到该事件。为了获得事件,我必须在输入管道上写入。当我在输入管道上写入一些命令时,我得到事件并获取旧命令的输出,而不是当前命令(请查看输出)。

下面是工作代码。

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <thread>
#include <string>

using namespace std;

#define input_pipe_name L"\\\\.\\pipe\\input"
#define output_pipe_name L"\\\\.\\pipe\\output"
#define process_name L"cmd.exe"

HANDLE input_pipe_handle;
HANDLE output_pipe_handle;

HANDLE input_file_handle;
HANDLE output_file_handle;


OVERLAPPED output_overlapped = { 0 };

BOOL InitHandels()
{
    input_pipe_handle = CreateNamedPipe(input_pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 120000, 0);
    SetHandleInformation(input_pipe_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    if (input_pipe_handle == INVALID_HANDLE_VALUE)
    {
        cout << "pipe creation error: " << GetLastError() << endl;
        return FALSE;
    }

    output_pipe_handle = CreateNamedPipe(output_pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 120000, 0);
    SetHandleInformation(output_pipe_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    if (output_pipe_handle == INVALID_HANDLE_VALUE)
    {
        cout << "pipe creation error: " << GetLastError() << endl;
        return FALSE;
    }

    input_file_handle = CreateFile(input_pipe_name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if (input_file_handle == INVALID_HANDLE_VALUE)
    {
        cout << "file creation error: " << GetLastError() << endl;
        return FALSE;
    }

    output_file_handle = CreateFile(output_pipe_name, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if (output_file_handle == INVALID_HANDLE_VALUE)
    {
        cout << "file creation error: " << GetLastError() << endl;
        return FALSE;
    }

    output_overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    ConnectNamedPipe(output_pipe_handle, &output_overlapped);
}



void CreateChildProcess()
{
    TCHAR szCmdline[] = L"cmd.exe";
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;

    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = output_pipe_handle;
    siStartInfo.hStdOutput = output_pipe_handle;
    siStartInfo.hStdInput = input_pipe_handle;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    if (!CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
    {
        cout << "process creation error: " << GetLastError() << endl;
        //return FALSE;
    }
    else
    {
        HANDLE h_array[] = {output_overlapped.hEvent, piProcInfo.hProcess};

        for (;;)
        {
            DWORD result = WaitForMultipleObjects(2, h_array, FALSE, 1000);
            DWORD bwritten = 0, bread = 0;
            char buffer[4096];

            switch (result)
            {
                case WAIT_TIMEOUT:
                    //cout << "TimeOut" << endl;
                    break;

                case WAIT_OBJECT_0:
                    ReadFile(output_file_handle, buffer, sizeof(buffer), &bread, &output_overlapped);
                    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bread, &bwritten, 0);
                    ResetEvent(output_overlapped.hEvent);
                    break;

                case WAIT_OBJECT_0 + 1: 
                    break;
                    //return FALSE;
            }
        }
    }
}

int main()
{
    DWORD bwritten;
    InitHandels();
    //CreateChildProcess();
    std::thread t1(CreateChildProcess);
    for (;;Sleep(1000))
    {
        std::string mystring;
        std::cin >> mystring;
        mystring.append("\n");
        WriteFile(input_file_handle, mystring.c_str(), mystring.length(), &bwritten, &output_overlapped);
        //WriteFile(input_file_handle, "dir\n", 4, &bwritten, &output_overlapped);
    }
    t1.join();
    return 0;
}

我得到以下输出

dir
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

D:\Programming\VS\to_post_on_stack\to_post_on_stack>hello
dir
 Volume in drive D has no label.
 Volume Serial Number is 54FB-7A94

 Directory of D:\Programming\VS\to_post_on_stack\to_post_on_stack

01/13/2018  05:36 PM    <DIR>          .
01/13/2018  05:36 PM    <DIR>          ..
01/13/2018  05:36 PM    <DIR>          Debug
01/12/2018  08:54 PM               608 stdafx.cpp
01/12/2018  08:54 PM               642 stdafx.h
01/12/2018  08:54 PM               630 targetver.h
01/13/2018  05:36 PM             7,434 to_post_on_stack.cpp
01/12/2018  08:54 PM             8,038 to_post_on_stack.vcxproj
01/12/2018  08:54 PM             1,277 to_post_on_stack.vcxproj.filters
               6 File(s)         18,629 bytes
               3 Dir(s)  39,347,019,776 bytes free

D:\Programming\VS\to_post_on_stack\to_post_on_stack>dir
hello
'hello' is not recognized as an internal or external command,
operable program or batch file.

D:\Programming\VS\to_post_on_stack\to_post_on_stack>dir

正如您在输出中看到的那样,当我发送 dir 命令时,我得到了旧的输出。当我发送 hello 时,我得到了在 hello 之前执行的 dir 命令的输出。

任何人都可以指出为什么我第一次没有收到信号的错误。为什么输出没有按顺序排列?

最佳答案

充满严重错误的代码示例:

第一个也是主要的:

If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect:

The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, otherwise the function can incorrectly report that the io operation is complete.

io operation resets the event specified by the hEvent member of the OVERLAPPED structure to a nonsignaled state when it begins the I/O operation. Therefore, the caller does not need to do that.

当 io 操作完成时 - io 子系统写入操作的最终状态、传输的字节数,如果它包含事件 - 将此事件设置为信号状态。如果您在并发中使用相同的lpOverlapped - 它们会互相覆盖结果,并且您永远不知道 - 哪个操作真正完成 - 事件是一个,常见!,如果您之前使用事件 - 系统重置事件开始 io - 结果 - 一个操作可以完成并设置事件,然后另一个操作在此之后重置它

并发调用 2 个线程:

WriteFile(input_file_handle, ..&output_overlapped);
ReadFile(output_file_handle, .. &output_overlapped);

这样你就已经有了 UB,因为并发中使用了相同的 &output_overlapped。我们需要为每个操作分配唯一的重叠。如果您使用事件来检测完成情况 - 您需要创建多个事件 - 这根本不是好方法。在这里使用 iocp 完成效果更好 - 我们不需要创建事件,我们不需要创建单独的线程。

ReadFile(output_file_handle, buffer, sizeof(buffer), &bread, &output_overlapped);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bread, &bwritten, 0);
ResetEvent(output_overlapped.hEvent);

首先,ReadFile 在开始 I/O 时将 OVERLAPPED 结构的 hEvent 成员指定的事件重置为无信号状态手术。因此,调用者不需要这样做。等等 - 当您调用 ResetEvent 时 - 操作可能已经完成 - 因此您重置了已发出信号的事件 - 结果您丢失了完成信号。如果调用ReasetEvent,则需要之前执行io操作(具体情况下为ReadFile),但不需要之后 - 这是错误。但是我们不需要在之前这样做 - 因为 io 子系统无论如何都会这样做。

还有一个严重错误 - 在异步调用ReadFile之后,我们无法在WriteFile中使用buffer, bread > - 调用尚未完成。和缓冲区的上下文尚未定义。

&bread 在异步调用中始终未定义,根本不能使用

The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to get the actual number of bytes read. If the hFile parameter is associated with an I/O completion port, you can also get the number of bytes read by calling the GetQueuedCompletionStatus function.

还有一个非常常见的错误 - 我们创建 2 个管道对(input_pipe_handleoutput_file_handle) - 这绝对不需要 - 我们可以使用1管道对。

SetHandleInformation的调用过多 - 我们只需通过SECURITY_ATTRIBUTES创建具有继承属性的句柄。

代码示例:

//#define _XP_SUPPORT_

struct IO_COUNT 
{
    HANDLE _hFile;
    HANDLE _hEvent;
    LONG _dwIoCount;

    IO_COUNT()
    {
        _dwIoCount = 1;
        _hEvent = 0;
    }

    ~IO_COUNT()
    {
        if (_hEvent)
        {
            CloseHandle(_hEvent);
        }
    }

    ULONG Create(HANDLE hFile);

    void BeginIo()
    {
        InterlockedIncrement(&_dwIoCount);
    }

    void EndIo()
    {
        if (!InterlockedDecrement(&_dwIoCount))
        {
            SetEvent(_hEvent);
        }
    }

    void Wait()
    {
        WaitForSingleObject(_hEvent, INFINITE);
    }
};

class U_IRP : OVERLAPPED 
{
    enum { connect, read, write  };

    IO_COUNT* _pIoObject;
    ULONG _code;
    LONG _dwRef;
    char _buffer[256];

    ~U_IRP()
    {
        _pIoObject->EndIo();
    }

    ULONG Read()
    {
        _code = read;

        AddRef();

        return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this));
    }

    ULONG CheckIoResult(BOOL fOk)
    {
        if (fOk)
        {
#ifndef _XP_SUPPORT_
            OnIoComplete(NOERROR, InternalHigh);
#endif
            return NOERROR;
        }

        ULONG dwErrorCode = GetLastError();

        if (dwErrorCode != ERROR_IO_PENDING)
        {
            OnIoComplete(dwErrorCode, 0);
        }

        return dwErrorCode;
    }

    VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
    {
        switch (_code)
        {
        case connect:
            switch (dwErrorCode)
            {
            case ERROR_PIPE_CONNECTED:
            case ERROR_NO_DATA:
                dwErrorCode = NOERROR;
            case NOERROR:
                Read();
            }
            break;

        case read:
            if (dwErrorCode == NOERROR)
            {
                if (dwNumberOfBytesTransfered)
                {
                    if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0))
                    {
                        PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));

                        if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar))
                        {
                            if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
                            {
                                PSTR sz = (PSTR)alloca(cbMultiByte);

                                if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
                                {
                                    DbgPrint("%.*s", cbMultiByte, sz);
                                }
                            }
                        }
                    }
                }
                Read();
            }
            break;
        case write:
            break;
        default:
            __debugbreak();
        }

        Release();

        if (dwErrorCode)
        {
            DbgPrint("[%u]: error=%u\n", _code, dwErrorCode);
        }
    }

    static VOID WINAPI _OnIoComplete(
        DWORD dwErrorCode,
        DWORD dwNumberOfBytesTransfered,
        LPOVERLAPPED lpOverlapped
        )
    {
        static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
    }

public:

    void AddRef()
    {
        InterlockedIncrement(&_dwRef);
    }

    void Release()
    {
        if (!InterlockedDecrement(&_dwRef)) delete this;
    }

    U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
    {
        _dwRef = 1;
        pIoObject->BeginIo();
        RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
    }

    ULONG Write(const void* pvBuffer, ULONG cbBuffer)
    {
        _code = write;

        AddRef();

        return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
    }

    ULONG Connect()
    {
        _code = connect;

        AddRef();

        return CheckIoResult(ConnectNamedPipe(_pIoObject->_hFile, this));
    }

    static ULONG Bind(HANDLE hFile)
    {
        return BindIoCompletionCallback(hFile, U_IRP::_OnIoComplete, 0)  
#ifndef _XP_SUPPORT_
            && SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)
#endif

        ? NOERROR : GetLastError();
    }
};

ULONG IO_COUNT::Create(HANDLE hFile)
{
    _hFile = hFile;
    if (_hEvent = CreateEvent(0, TRUE, FALSE, 0))
    {
        return U_IRP::Bind(hFile);
    }
    return GetLastError();
}

void ChildTest()
{
    static const WCHAR name[] = L"\\\\?\\pipe\\somename";

    HANDLE hFile = CreateNamedPipeW(name, 
        PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED, 
        PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        IO_COUNT obj;

        if (obj.Create(hFile) == NOERROR)
        {
            BOOL fOk = FALSE;

            static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };

            STARTUPINFOW si = { sizeof(si) };
            PROCESS_INFORMATION pi;

            si.dwFlags = STARTF_USESTDHANDLES;

            si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 
                FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);

            if (si.hStdError != INVALID_HANDLE_VALUE)
            {
                si.hStdInput = si.hStdOutput = si.hStdError;

                WCHAR ApplicationName[MAX_PATH];
                if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
                {
                    if (CreateProcessW(ApplicationName , 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
                    {
                        CloseHandle(pi.hThread);
                        CloseHandle(pi.hProcess);
                        fOk = TRUE;
                    }
                }

                CloseHandle(si.hStdError);
            }

            if (fOk)
            {
                U_IRP* p;

                if (p = new U_IRP(&obj))
                {
                    p->Connect();
                    p->Release();
                }

                obj.EndIo();

                //++ simulate user commands
                static PCSTR commands[] = { "dir\r\n", "ver\r\n", "exit\r\n" };
                ULONG n = RTL_NUMBER_OF(commands);
                PCSTR* psz = commands;
                do 
                {
                    if (p = new U_IRP(&obj))
                    {
                        PCSTR command = *psz++;
                        p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
                        p->Release();
                    }

                } while (--n);
                //--

                obj.Wait();
            }
        }

        CloseHandle(hFile);
    }
}

关于c++ - CreateEvent 初始状态已发信号不是发信号事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48239819/

相关文章:

c++ - 扩展 std::thread 的 ref 对象传递范围

c++ - 将实例数组实现为不同类型的好方法是什么

SQL*Plus 命令行参数在 Windows 中被覆盖

Windows 和 IIS 上的 PHP 5 和 Zend MVC

php - 在 Windows 上安装 PHP 扩展

c++ - 关于Boost共享内存的问题

C++ BOOST 单元测试 : Invert BOOST_CHECK_EQUAL for not equal

c - 将字符串存储在 mmap 共享数组中? (C)

multithreading - 在 Delphi 中使用 CreateRemoteThread 将代码注入(inject)另一个进程的正确方法是什么?

c++ - 查找字符串中最长的子串