nos*_*tio 10 .net c# windows com winapi
这是由我正在研究的另一个问题引发的.阅读可能太长了,所以请耐心等待.
显然,在MSDN CoWaitForMultipleHandles上没有记录的行为.
下面的代码(基于原始问题)是一个控制台应用程序,它启动一个带有测试Win32窗口的STA线程并尝试发布并抽取一些消息.它做了三个不同的测试CoWaitForMultipleHandles,都没有 COWAIT_WAITALL标志.
测试#1旨在验证这一点:
COWAIT_INPUTAVAILABLE如果设置,如果队列输入存在,则对CoWaitForMultipleHandles的调用将返回S_OK,即使已使用对另一个函数(如PeekMessage)的调用看到(但未删除)输入.
这不会发生,CoWaitForMultipleHandles阻塞并且在发出等待句柄之前不会返回.我不认为任何未决的消息应被视为输入(与同MWMO_INPUTAVAILABLE的MsgWaitForMultipleObjectsEx,它按预期工作).
测试#2旨在验证这一点:
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,则呼叫阻止但不抽取任何消息.
测试#3演示了我可以CoWaitForMultipleHandles使用调用线程的Windows消息队列的唯一方法.它是一个组合COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE.这确实是泵送和发送消息,尽管显然它是一种无证件的行为.
测试代码(可立即运行的控制台应用程序):
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;
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
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: True Test #2. CoWaitForMultipleHandles with COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS, press Enter to stop... Result: 0, pending messages in the queue: True Test #3. 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.
所有测试均在Windows 8.1 Pro 64bit + NET v4.5.1下完成.
我误读了文档还是遗漏了其他内容?
我应该将此报告为错误(至少是文档中的错误)吗?
应该CoWaitForMultipleHandles避免并替换为基于MsgWaitForMultipleObjectsEx(根据文档行为)的解决方案?
[更新]在Windows 7下,既不支持COWAIT_DISPATCH_WINDOW_MESSAGES也不COWAIT_DISPATCH_CALLS支持,CoWaitForMultipleHandles失败E_INVALIDARG (0x80070057).当以零作为标志调用时,它会在没有泵送的情况下阻塞
CoWaitForMultipleHandles旨在在 STA 中处理 COM 窗口消息(例如跨单元编组)和其他一些消息(不要问我是哪一个),或者只是在 MTA 中进行阻塞。在Chris Brumme 的这篇博客文章 \xc2\xabManagedblocking\xc2\xbb中,它表示CWFMH处理“适量”的窗口消息。但是,由于它将任何非 COM 发布的窗口消息留在队列中,因此队列可能仍会填满,只是没有 COM 窗口消息。
根据此文档 \xc2\xabMigration your Windows 8 Consumer Preview app to Windows 8 Release Preview\xc2\xbb,它说:
\n\n\n\n\nWindows 应用商店应用程序不再支持 CoWaitForMultipleHandles 函数。此外,以下 CoWait_Flags 已被删除:
\n\nCOWAIT_DISPATCH_CALLS
\n\nCOWAIT_DISPATCH_WINDOW_MESSAGES
\n
如果您确实想处理所有消息,则应该MsgWaitForMultipleObjectsEx在消息循环中使用 withGetMessage或PeekMessagewith PM_REMOVE。这样做意味着潜在的重入狂潮。您仍然无法控制从堆栈中的其他组件对 STA 的进一步调用。也就是说,模式对话框(例如用于打开的通用对话框)可能会在普通的旧窗口消息循环中泵送每条消息,但某些框架可能会调用CoWaitForMultipleHandles.
底线是,如果您正在执行密集处理或阻塞操作,请将其委托给另一个线程(可能使用队列),并在需要时告诉调用 UI 线程在操作完成后进行更新。
\n\n这与冗长的 UI 调用(例如 OLE 嵌入或模式对话框)不同,在这些调用中,堆栈中的某处通常有一个窗口消息循环。或者通过冗长但可分块/可恢复的操作(例如状态机),您可以通过偶尔处理消息来进行协作,或者使用在有消息时返回的等待函数,以便您可以在再次等待之前处理它们。
\n\n请注意,这仅适用于一个手柄;对于多个句柄(例如互斥锁),您需要全部或全部都不使用,下一个最佳方法是主动循环,其中超时调用后跟窗口WaitForMultipleObjects消息循环。这是一种边界情况,对于用户关注中心的 UI 应用程序来说是可以接受的(例如,这是他们的主要工作),但如果此类代码可以无人值守且按需运行,则这是不可接受的。除非您确定它需要在 STA 或 UI 线程中发生,否则我的建议是不要这样做。PeekMessagePM_REMOVE
最后,您可能应该在 Microsoft Connect 上打开一个错误,至少更新文档。或者实际上让它按“预期”工作。
\n