【问题标题】:CreateEvent initial state signaled is not signaling eventCreateEvent 初始状态信号不是信号事件
【发布时间】:2018-01-13 12:21:07
【问题描述】:

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

下面是工作代码。

#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 时,我得到了dir 命令的输出,该命令在hello 之前执行。

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

【问题讨论】:

  • 您根本不需要任何单独的事件。您不需要 2 个管道对 - 但只需要一个。你不需要调用SetHandleInformation - 只需创建继承的文件句柄
  • WriteFile 刚刚在异步调用之后 ReadFile 不正确 - 数据可能尚未准备好 - 所有代码从头到尾都是错误的
  • 您一次在 2 个 api 调用中使用相同的 output_overlappedWriteFile(input_file_handleReadFile(output_file_handle) - 这也是严重的错误 - 即使 io 调用也必须使用它自己的重叠 - 再次 - 所有代码错了
  • @RbMm 我是初学者。您能否更正错误并将其发布为答案?请帮忙
  • @RbMm 上面的哪一个 bug 真正引起了问题?

标签: c++ windows ipc msdn io-redirection


【解决方案1】:

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

第一个也是主要的:

如果 hFile 是用 FILE_FLAG_OVERLAPPED 打开的,则如下 条件生效:

lpOverlapped 参数必须指向一个有效且唯一 OVERLAPPED结构,否则函数会报错 表示io操作完成。

io 操作重置事件hEvent 的成员指定 OVERLAPPED 结构在开始 I/O 时变为非信号状态 手术。因此,调用者不需要这样做。

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

你同时调用 2 个线程:

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

有了这个,你已经有了 UB,因为在并发中使用了相同的 &amp;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,则需要执行before io 操作(具体情况下为ReadFile),而不是after - 这是错误的。但是我们不需要这样做和之前 - 因为 io 子系统无论如何都会这样做。

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

&amp;bread 在异步调用中未定义总是,根本不能使用

lpNumberOfBytesRead 参数应设置为 NULL。使用 GetOverlappedResult 函数获取实际读取的字节数。 如果 hFile 参数与 I/O 完成端口相关联, 您还可以通过调用 GetQueuedCompletionStatus 函数。

还有一个非常常见的错误——我们创建了 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);
    }
}

【讨论】:

    猜你喜欢
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    相关资源
    最近更新 更多