title: CoWaitForMultipleHandles API doesn't behave as documented
body: <p>This was triggered by <a href="https://stackoverflow.com/q/21211998/1768303">another question</a> I was looking at. It might be too long to read, so please bear with me.</p>
  
  <p><strong>Apparently, <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms680732(v=vs.85).aspx" rel="nofollow noreferrer"><code>CoWaitForMultipleHandles</code></a> does <em>not</em> behave as documented on MSDN.</strong></p>
  
  <p>The code below (based upon the original question) is a console app, which starts an STA thread with a test Win32 window and tries post and to pump some messages. It does three different tests on <code>CoWaitForMultipleHandles</code>, all <em>without</em> <code>COWAIT_WAITALL</code> flag. </p>
  
  <p><strong>Test #1</strong> is aimed to verify <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms693767(v=vs.85).aspx" rel="nofollow noreferrer">this</a>:</p>
  
  <blockquote>
    <p><strong>COWAIT_INPUTAVAILABLE</strong> If set, the call to CoWaitForMultipleHandles
    will return S_OK if input exists for the queue, even if the input has
    been seen (but not removed) using a call to another function, such as
    PeekMessage.</p>
  </blockquote>
  
  <p>This is not happening, <code>CoWaitForMultipleHandles</code> blocks and doesn't return until the wait handle is signalled. I do assume that any pending message should be treated as <em>input</em> (same as with <code>MWMO_INPUTAVAILABLE</code> of <code>MsgWaitForMultipleObjectsEx</code>, which works as expected).</p>
  
  <p><strong>Test #2</strong> is aimed to verify <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms693767(v=vs.85).aspx" rel="nofollow noreferrer">this</a>:</p>
  
  <blockquote>
    <p><strong>COWAIT_DISPATCH_WINDOW_MESSAGES</strong> Enables dispatch of window messages
    from CoWaitForMultipleHandles in an ASTA or STA. Default in ASTA is no
    window messages dispatched, default in STA is only a small set of
    special-cased messages dispatched. The value has no meaning in MTA and
    is ignored.</p>
  </blockquote>
  
  <p>This doesn't work either. When <code>CoWaitForMultipleHandles</code> is called with <code>COWAIT_DISPATCH_WINDOW_MESSAGES</code> flag alone, it instantly returns error <code>CO_E_NOT_SUPPORTED</code> (0x80004021). If it's a combination of <code>COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS</code>, the call blocks but <strong>doesn't pump any messages.</strong></p>
  
  <p><strong>Test #3</strong> demonstrates the only way I could make <code>CoWaitForMultipleHandles</code> pump the Windows message queue of the calling thread. It is a combination of <code>COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE</code>. <strong>This does pump and dispatch messages, although apparently it is an undocumented behaviour.</strong></p>
  
  <p><strong>The test code</strong> (a ready-to-run console app):</p>
  
  <pre><code>using System;
  using System.Runtime.InteropServices;
  using System.Threading;
  using System.Threading.Tasks;
  
  namespace ConsoleTestApp
  {
      static class Program
      {
          // Main 
          static void Main(string[] args)
          {
              Console.WriteLine("Starting an STA thread...");
              RunStaThread();
  
              Console.WriteLine("\nSTA thread finished.");
              Console.WriteLine("Press Enter to exit.");
              Console.ReadLine();
          }
  
          // start and run an STA thread
          static void RunStaThread()
          {
              var thread = new Thread(() =&gt;
              {
                  // create a simple Win32 window
                  IntPtr hwnd = CreateTestWindow();
  
                  // Post some WM_TEST messages
                  Console.WriteLine("Post some WM_TEST messages...");
                  NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(1), IntPtr.Zero);
                  NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(2), IntPtr.Zero);
                  NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(3), IntPtr.Zero);
  
                  // Test #1
                  Console.WriteLine("\nTest #1. CoWaitForMultipleHandles with COWAIT_INPUTAVAILABLE only, press Enter to stop...");
                  var task = ReadLineAsync();
  
                  uint index;
                  var result = NativeMethods.CoWaitForMultipleHandles(
                      NativeMethods.COWAIT_INPUTAVAILABLE,
                      NativeMethods.INFINITE,
                      1, new[] { task.AsUnmanagedHandle() },
                      out index);
                  Console.WriteLine("Result: " + result + ", pending messages in the queue: " + (NativeMethods.GetQueueStatus(0x1FF) &gt;&gt; 16 != 0));
  
                  // Test #2
                  Console.WriteLine("\nTest #2. CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, press Enter to stop...");
                  task = ReadLineAsync();
  
                  result = NativeMethods.CoWaitForMultipleHandles(
                      NativeMethods.COWAIT_DISPATCH_WINDOW_MESSAGES | 
                          NativeMethods.COWAIT_DISPATCH_CALLS,
                      NativeMethods.INFINITE, 
                      1, new[] { task.AsUnmanagedHandle() },
                      out index);
                  Console.WriteLine("Result: " + result + ", pending messages in the queue: " + (NativeMethods.GetQueueStatus(0x1FF) &gt;&gt; 16 != 0));
  
                  // Test #3
                  Console.WriteLine("\nTest #3. CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE, press Enter to stop...");
                  task = ReadLineAsync();
  
                  result = NativeMethods.CoWaitForMultipleHandles(
                      NativeMethods.COWAIT_DISPATCH_WINDOW_MESSAGES | 
                          NativeMethods.COWAIT_DISPATCH_CALLS | 
                          NativeMethods.COWAIT_INPUTAVAILABLE,
                      NativeMethods.INFINITE,
                      1, new[] { task.AsUnmanagedHandle() },
                      out index);
                  Console.WriteLine("Result: " + result + ", pending messages in the queue: " + (NativeMethods.GetQueueStatus(0x1FF) &gt;&gt; 16 != 0));
              });
  
              thread.SetApartmentState(ApartmentState.STA);
              thread.Start();
  
              thread.Join();
          }
  
          //
          // Helpers
          //
  
          // create a window to handle messages
          static IntPtr CreateTestWindow()
          {
              // Create a simple Win32 window 
              var hwndStatic = NativeMethods.CreateWindowEx(0, "Static", String.Empty, NativeMethods.WS_POPUP,
                  0, 0, 0, 0, NativeMethods.HWND_MESSAGE, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  
              // subclass it with a custom WndProc
              IntPtr prevWndProc = IntPtr.Zero;
  
              NativeMethods.WndProc newWndProc = (hwnd, msg, wParam, lParam) =&gt;
              {
                  if (msg == NativeMethods.WM_TEST)
                      Console.WriteLine("WM_TEST processed: " + wParam);
                  return NativeMethods.CallWindowProc(prevWndProc, hwnd, msg, wParam, lParam);
              };
  
              prevWndProc = NativeMethods.SetWindowLong(hwndStatic, NativeMethods.GWL_WNDPROC,
                  Marshal.GetFunctionPointerForDelegate(newWndProc));
              if (prevWndProc == IntPtr.Zero)
                  throw new ApplicationException();
  
              return hwndStatic;
          }
  
          // call Console.ReadLine on a pool thread
          static Task&lt;string&gt; ReadLineAsync()
          {
              return Task.Run(() =&gt; Console.ReadLine());
          }
  
          // get Win32 waitable handle of Task object
          static IntPtr AsUnmanagedHandle(this Task task)
          {
              return ((IAsyncResult)task).AsyncWaitHandle.SafeWaitHandle.DangerousGetHandle();
          }
      }
  
      // Interop
      static class NativeMethods
      {
          [DllImport("user32")]
          public static extern IntPtr SetWindowLong(IntPtr hwnd, int nIndex, IntPtr dwNewLong);
  
          [DllImport("user32")]
          public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
  
          [DllImport("user32.dll")]
          public static extern IntPtr CreateWindowEx(
              uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle, 
              int x, int y, int nWidth, int nHeight, 
              IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
  
          [DllImport("user32.dll")]
          public static extern bool PostMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
  
          [DllImport("user32.dll")]
          public static extern int MessageBox(IntPtr hwnd, string text, String caption, int options);
  
          [DllImport("ole32.dll", SetLastError = true)]
          public static extern uint CoWaitForMultipleHandles(uint dwFlags, uint dwTimeout,
             int cHandles, IntPtr[] pHandles, out uint lpdwindex);
  
          [DllImport("user32.dll")]
          public static extern uint GetQueueStatus(uint flags);
  
          [UnmanagedFunctionPointer(CallingConvention.StdCall)]
          public delegate IntPtr WndProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
  
          public static IntPtr HWND_MESSAGE = new IntPtr(-3);
  
          public const int GWL_WNDPROC = -4;
          public const uint WS_POPUP = 0x80000000;
  
          public const uint WM_USER = 0x0400;
          public const uint WM_TEST = WM_USER + 1;
  
          public const uint COWAIT_WAITALL = 1;
          public const uint COWAIT_ALERTABLE = 2;
          public const uint COWAIT_INPUTAVAILABLE = 4;
          public const uint COWAIT_DISPATCH_CALLS = 8;
          public const uint COWAIT_DISPATCH_WINDOW_MESSAGES = 0x10;
  
          public const uint RPC_S_CALLPENDING = 0x80010115;
  
          public const uint WAIT_TIMEOUT = 0x00000102;
          public const uint WAIT_FAILED = 0xFFFFFFFF;
          public const uint WAIT_OBJECT_0 = 0;
          public const uint WAIT_ABANDONED_0 = 0x00000080;
          public const uint WAIT_IO_COMPLETION = 0x000000C0;
  
          public const uint INFINITE = 0xFFFFFFFF;
      }
  }
  </code></pre>
  
  <p><strong>The output:</strong></p>
  
  <pre>
  Starting an STA thread...
  Post some WM_TEST messages...
  
  <b>Test #1.</b> CoWaitForMultipleHandles with COWAIT_INPUTAVAILABLE only, press Enter to stop...
  
  Result: 0, pending messages in the queue: True
  
  <b>Test #2.</b> CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, press Enter to stop...
  
  Result: 0, pending messages in the queue: True
  
  <b>Test #3</b>. CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE, press Enter to stop...
  WM_TEST processed: 1
  WM_TEST processed: 2
  WM_TEST processed: 3
  
  Result: 0, pending messages in the queue: False
  
  STA thread finished.
  Press Enter to exit.
  </pre>
  
  <p>All tests are done under Windows 8.1 Pro 64bit + NET v4.5.1.</p>
  
  <ul>
  <li><p>Am I misreading the docs or missing something else? </p></li>
  <li><p>Should I report this as a bug (at least, a bug in the docs)? </p></li>
  <li><p>Should <code>CoWaitForMultipleHandles</code> be avoided and replaced with a solution based on <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684245%28v=vs.85%29.aspx" rel="nofollow noreferrer"><code>MsgWaitForMultipleObjectsEx</code></a> (which behaves in accordance with the docs)?</p></li>
  </ul>
  
  <p><strong>[UPDATE]</strong> Under Windows 7, neither <code>COWAIT_DISPATCH_WINDOW_MESSAGES</code> nor <code>COWAIT_DISPATCH_CALLS</code> are supported, <code>CoWaitForMultipleHandles</code> fails with <code>E_INVALIDARG</code>  (0x80070057). When called with zero as flags, it blocks without pumping.  </p>