本文介绍了CoWaitForMultipleHandles API不表现为记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 这是触发另一个问题我一直在寻找。这可能是太长时间阅读,所以请多多包涵。This was triggered by another question I was looking at. It might be too long to read, so please bear with me. 显然,CoWaitForMultipleHandles做的没有的表现为记录在MSDN上。 Apparently, CoWaitForMultipleHandles does not behave as documented on MSDN.下面的code(基于原来的问题)是一个控制台应用程序,这将启动一个STA线程与测试的Win32窗口,并尝试后,并抽一些消息。它确实对 CoWaitForMultipleHandles 三种不同的测试,所有的没有的 COWAIT_WAITALL 标志。 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 CoWaitForMultipleHandles, all without COWAIT_WAITALL flag. 测试#1 的目的是验证this: COWAIT_INPUTAVAILABLE 如果设置,调用CoWaitForMultipleHandles 将返回S_OK如果输入存在的队列,即使输入具有 通过调用另一个函数,如被看到(但不删除) 的PeekMessage。这是不会发生, CoWaitForMultipleHandles 块,并且不返回,直到等待句柄发出信号。我不认为任何未决消息应被视为的输入的(同样与 MWMO_INPUTAVAILABLE 的 MsgWaitForMultipleObjectsEx ,它按预期工作)。This is not happening, CoWaitForMultipleHandles blocks and doesn't return until the wait handle is signalled. I do assume that any pending message should be treated as input (same as with MWMO_INPUTAVAILABLE of MsgWaitForMultipleObjectsEx, which works as expected). 测试#2 的目的是验证this: COWAIT_DISPATCH_WINDOW_MESSAGES 启用调度窗口消息 从一个ASTA或STA CoWaitForMultipleHandles。默认的ASTA没有 调度窗口消息,默认在STA为只有一小部分 特例信息调度。该值没有意义的MTA和 被忽略。这也不起作用。当 CoWaitForMultipleHandles 被称为与 COWAIT_DISPATCH_WINDOW_MESSAGES 单独标记,它会立即返回错误 CO_E_NOT_SUPPORTED (0x80004021)。如果是 COWAIT_DISPATCH_WINDOW_MESSAGES组合| COWAIT_DISPATCH_CALLS ,呼叫块,但不泵任何消息。 This doesn't work either. When CoWaitForMultipleHandles is called with COWAIT_DISPATCH_WINDOW_MESSAGES flag alone, it instantly returns error CO_E_NOT_SUPPORTED (0x80004021). If it's a combination of COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, the call blocks but doesn't pump any messages. 测试#3 展示我可以让 CoWaitForMultipleHandles 泵调用线程的windows消息队列的唯一途径。这是 COWAIT_DISPATCH_WINDOW_MESSAGES组合| COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE 。 这确实泵和调度消息,但显然这是一个无证的行为。 Test #3 demonstrates the only way I could make CoWaitForMultipleHandles pump the Windows message queue of the calling thread. It is a combination of COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE. This does pump and dispatch messages, although apparently it is an undocumented behaviour. 测试code (一个现成的运行控制台应用程序):The test code (a ready-to-run console app):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(() => { // 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) >> 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) >> 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) >> 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) => { 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<string> ReadLineAsync() { return Task.Run(() => 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; }} 输出: Starting an STA thread...Post some WM_TEST messages...Test #1. CoWaitForMultipleHandles with COWAIT_INPUTAVAILABLE only, press Enter to stop...Result: 0, pending messages in the queue: TrueTest #2. CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, press Enter to stop...Result: 0, pending messages in the queue: TrueTest #3. CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE, press Enter to stop...WM_TEST processed: 1WM_TEST processed: 2WM_TEST processed: 3Result: 0, pending messages in the queue: FalseSTA thread finished.Press Enter to exit.所有的测试都是在Windows下完成8.1专业版64位+ NET V4.5.1。All tests are done under Windows 8.1 Pro 64bit + NET v4.5.1. 我是误读的文档或丢失什么东西? Am I misreading the docs or missing something else? 我应该报告这个bug(至少,在文档中的错误)? Should I report this as a bug (at least, a bug in the docs)? 应该 CoWaitForMultipleHandles 避免和解决方案基于MsgWaitForMultipleObjectsEx (其行为符合文档)?Should CoWaitForMultipleHandles be avoided and replaced with a solution based on MsgWaitForMultipleObjectsEx (which behaves in accordance with the docs)? [更新] 在Windows 7,无论 COWAIT_DISPATCH_WINDOW_MESSAGES 和 COWAIT_DISPATCH_CALLS 支持, CoWaitForMultipleHandles 失败, E_INVALIDARG (80070057)。当零称为标志,它阻止不抽。 [UPDATE] Under Windows 7, neither COWAIT_DISPATCH_WINDOW_MESSAGES nor COWAIT_DISPATCH_CALLS are supported, CoWaitForMultipleHandles fails with E_INVALIDARG (0x80070057). When called with zero as flags, it blocks without pumping. 推荐答案 CoWaitForMultipleHandles 拟处理COM窗口消息(如跨单元编组)和其他一些(不要问我这)在STA,或者在MTA阻塞。在这篇博客,«被管阻塞»克里斯Brumme ,它说窗口消息的 CWFMH 进程适量。然而,由于它离开任何非COM张贴窗口消息队列中,该队列仍然可能填满,只是不与COM窗口消息CoWaitForMultipleHandles is intended to process COM window messages (e.g. cross-apartment marshalling) and a few others (don't ask me which) in STA, or simply block in MTA. In this blog post, «Managed blocking» by Chris Brumme, it says CWFMH processes "just the right amount" of window messages. However, since it leaves any non-COM posted window message in the queue, the queue may still fill up, just not with COM window messages.根据这个文件,«迁移的Windows 8消费者preVIEW应用到Windows 8发行preVIEW»,它说: CoWaitForMultipleHandles功能不再在Windows应用商店的应用程序支持。此外,下列CoWait_Flags已被删除: COWAIT_DISPATCH_CALLS COWAIT_DISPATCH_CALLS COWAIT_DISPATCH_WINDOW_MESSAGES COWAIT_DISPATCH_WINDOW_MESSAGES如果你真的想处理所有信息,你应该使用 MsgWaitForMultipleObjectsEx 的消息循环与的GetMessage 或的PeekMessage 与 PM_REMOVE 。这样做意味着一个潜在的可重入性狂潮。你还是别管以后调用到STA从下栈等组成。也就是说,一个模式对话框(如通用对话框开放)可能泵每封邮件的纯旧的窗口消息循环,但一些框架可能叫 CoWaitForMultipleHandles 。If you really want to process all messages, you should use MsgWaitForMultipleObjectsEx in a message loop with GetMessage or PeekMessage with PM_REMOVE. Doing so means a potential reentrancy frenzy. You still don't control further calls into the STA from other components down the stack. That is, a modal dialog (e.g. Common Dialog Box for Open) might pump every message in a plain-old window message loop, but some framework might call CoWaitForMultipleHandles.底线是,如果你正在做精深加工或阻塞操作,其委派到另一个线程(可能使用一个队列),如果需要告诉调用UI线程更新完成后运行。Bottom line is, if you're doing intensive processing or blocking operations, delegate it to another thread (possibly using a queue), and if needed tell the invoking UI thread to update after the operation is done.这是不是如不同冗长的用户界面调用,如OLE嵌入或模态对话框,那里通常是一个窗口消息循环沿着堆叠的地方。或者从漫长的,但chunkable /可恢复操作(比如一个状态机),在那里你可以在一段时间曾经处理邮件合作,或通过使用等待函数返回时,有消息这样你就可以再次等待之前处理它们。This is different than e.g. lengthy UI calls, such as OLE embedding or a modal dialog, where there's usually a window message loop somewhere along the stack. Or from lengthy but chunkable/resumable operations (e.g. a state machine), where you can cooperate by processing messages once in a while, or by using wait functions that return when there are messages so you can process them before waiting again.请注意,这只是非常适用于一个手柄;多个手柄例如互斥,你会想要么全或无,下一个最好的办法是有源环路,具有超时调用 WaitForMultipleObjects的后跟一个的PeekMessage 与 PM_REMOVE 窗口消息循环。这是一个边界线的情况下,可以接受的UI应用程序是人们关注的用户的中心(例如,这是他们的主要工作),但如果这样code可以自动运行,并根据需要不可接受的。除非你肯定知道,它需要发生在一个STA或UI线程,我的建议是不这样做。Be careful, this only works well for one handle; for multiple handles e.g. mutexes, you'd want either all or none, and the next best approach is an active loop, with a timed-out call to WaitForMultipleObjects followed by a PeekMessage with PM_REMOVE window message loop. This is a border-line case, acceptable for a UI application that is the user's center of attention (e.g. it's their main work), but unacceptable if such code may run unattended and on demand. Unless you know for sure that it needs to happen in an STA or UI thread, my advice is don't do this.最后,你应该开在微软连接错误,至少更新文档。或者,实际上使作为工作的预期。Finally, you should probably open a bug at Microsoft Connect, at least to update the documentation. Or actually make it work as "expected". 这篇关于CoWaitForMultipleHandles API不表现为记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 10-19 00:13