1

I am writing a class (PipeReader) to handle named pipes on Windows. The class uses asynchronous IO to read from the pipe.

So far I have been using the class in a thread without an event loop and I had to wait on the IO to finish using SleepEx() and it worked.

Now I have a second thread with an event loop and a second instance of the PipeReader class, however the completion routine of the second instance is never called.

Call to CreateFile():

handle = CreateFile(fullServerName,
                         GENERIC_READ | GENERIC_WRITE,
                         0,
                         NULL,
                         OPEN_EXISTING,
                         FILE_FLAG_OVERLAPPED,
                         NULL);

Call to ReadFileEx():

BOOL status = ReadFileEx(m_handle, ptr, readSize, &m_overlapped, &PipeReader::readFileCompleted);

Code for waiting in thread 1, that works:

while (SleepEx(msecs < 0 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) {
    if (m_readFinished) // Set to true in the completion routine
        return true;

    msecs = // decrease msecs by the amount elapsed
    if (!msecs)
        break;
}

Code of the event loop in thread 2 where the completion routine is never called:

forever()
{
    forever()
    {
        bool haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);

        if (haveMessage)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            break; //No message leave loop
        }
    }


    HANDLE handles[1];
    handles[0] = m_hwnd;
    DWORD result = MsgWaitForMultipleObjectsEx(1, handles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE|MWMO_INPUTAVAILABLE);

}

EDIT:

If I replace the calle to MsgWaitForMultipleObjectsEx() by :

WaitForSingleObjectEx(m_hwnd, INFINITE, TRUE);

it still doesn't work. However if I use SleepEx() in the event loop it works.

Benjamin T
  • 8,120
  • 20
  • 37

1 Answers1

3

This is because m_hwnd is not a kernel handle. So WaitForSingleObjectEx fails with WAIT_FAILED and GetLastError returns ERROR_INVALID_HANDLE - as result the thread never enters alertable wait and APC is never executed. You need to have another message loop, for example:

  for ( ; ; ) {

    MSG msg;

    switch(MsgWaitForMultipleObjectsEx(0, 0, INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE| MWMO_ALERTABLE)) 
    {

    case STATUS_WAIT_0 : 

      while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {

        switch(msg.message) {
        case WM_QUIT: 
          return;
        default: 
          if (!IsDialogMessage(hwnd, &msg))
          {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
          }
      }
    }
    case WAIT_IO_COMPLETION :
    // apc called
    continue;
    default: return ;
    }
zett42
  • 25,437
  • 3
  • 35
  • 72
RbMm
  • 31,280
  • 3
  • 35
  • 56
  • Thanks it works. I had the wrong idea that I had to wait for the HWND to be able to wake it up using `PostMessage()`. – Benjamin T Jul 26 '17 at 15:39
  • @BenjaminT no, you not need wait on hwnd (and can not). and you need exactly first call `MsgWaitForMultipleObjectsEx` and only if it return `STATUS_WAIT_0` + n (where n count of handles) - call `PeekMessage` in loop while it return true – RbMm Jul 26 '17 at 15:44