我试图检测用户何时点击 aForm
和 a CommonDialog
。
表格相当简单。我创建了一个MessageFilter
拦截消息的类:
class MessageFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
public bool PreFilterMessage(ref Message message)
{
if (message.Msg == WM_LBUTTONDOWN)
{
Console.WriteLine("activity");
}
return false;
}
}
我注册了消息过滤器:
MessageFilter mf = new MessageFilter();
Application.AddMessageFilter(mf);
Form form = new Form();
form.ShowDialog();
Application.RemoveMessageFilter(mf)
当我运行控制台应用程序并单击 时Form
,我看到控制台记录了“活动”。
当我替换Form
为CommonDialog
:
SaveFileDialog dialog = new SaveFileDialog();
dialog.ShowDialog();
即使我可以看到 Windows 消息被发送到 CommonDialog,我也无法检测到鼠标点击(FWIW,我无法检测到任何消息):
那我怎么不能截取那些消息呢?
我想到的是,由于Application.AddMessageFilter
是特定于线程的,也许如果 CommonDialog 是在与调用的线程不同的线程上创建的dialog.ShowDialog()
,我将不会收到任何这些消息。
但是,我做了一个快速测试,尝试向WM_CLOSE
调用 的线程上的所有 CommonDialogs 发送消息dialog.ShowDialog()
,并且它有效:
int threadId = 0;
Thread thread = new Thread(() =>
{
threadId = NativeMethods.GetCurrentThreadIdWrapper();
SaveFileDialog dialog = new SaveFileDialog();
dialog.ShowDialog();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Thread.Sleep(2000);
NativeMethods.CloseAllWindowsDialogs(threadId);
Thread.Sleep(2000);
NativeMethods 看起来像:
static class NativeMethods
{
public static int GetCurrentThreadIdWrapper()
{
return GetCurrentThreadId();
}
public static void CloseAllWindowsDialogs(int threadId)
{
EnumThreadWndProc callback = new EnumThreadWndProc(CloseWindowIfCommonDialog);
EnumThreadWindows(threadId, callback, IntPtr.Zero);
GC.KeepAlive(callback);
}
private static bool CloseWindowIfCommonDialog(IntPtr hWnd, IntPtr lp)
{
if (IsWindowsDialog(hWnd))
{
UIntPtr result;
const int WM_CLOSE = 0x0010;
const uint SMTO_ABORTIFHUNG = 0x0002;
SendMessageTimeout(hWnd, WM_CLOSE, UIntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 5000, out result);
}
return true;
}
private static bool IsWindowsDialog(IntPtr hWnd)
{
const int MAX_PATH_LENGTH = 260; // https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
StringBuilder sb = new StringBuilder(MAX_PATH_LENGTH);
GetClassName(hWnd, sb, sb.Capacity);
return sb.ToString() == "#32770";
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetCurrentThreadId();
private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, UIntPtr wp, IntPtr lp, uint fuFlags, uint timeout, out UIntPtr lpdwResult);
}
为什么我不能拦截 CommonDialog 消息?我能做些什么呢?