为什么我无法检测发送到CommonDialog的Windows消息?拦截它们的正确方法是什么?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么我无法检测发送到CommonDialog的Windows消息?拦截它们的正确方法是什么?相关的知识,希望对你有一定的参考价值。
我试图检测用户点击Form
和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消息?我能做些什么呢?
答案
如何设置本地鼠标挂钩?
适用于我的项目。
public const int WM_LBUTTONDOWN = 0x0201;
// add other button messages if necessary
public const int WH_MOUSE = 7;
private IntPtr _hookHandle;
private void HookStart() {
int threadId = GetCurrentThreadId();
HookProc mouseClickHandler = new HookProc(MouseClickHandler);
_hookHandle = SetWindowsHookEx(WH_MOUSE, mouseClickHandler, IntPtr.Zero, (uint) threadId);
if (_hookHandle == IntPtr.Zero) throw new Exception("Hooking failed!");
}
private void HookStop() {
if (UnhookWindowsHookEx(_hookHandle) == IntPtr.Zero) throw new Exception("Unhooking failed!");
}
private IntPtr MouseClickHandler(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode >= 0 && wParam == (IntPtr) WM_LBUTTONDOWN) {
// user clicked
}
return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, uint threadId);
[DllImport("User32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int UnhookWindowsEx(IntPtr idHook);
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
以上是关于为什么我无法检测发送到CommonDialog的Windows消息?拦截它们的正确方法是什么?的主要内容,如果未能解决你的问题,请参考以下文章