C# 为什么可以';我是否检测到发送到我的CommonDialog的Windows消息?拦截它们的正确方法是什么?
我试图检测用户何时单击C# 为什么可以';我是否检测到发送到我的CommonDialog的Windows消息?拦截它们的正确方法是什么?,c#,winforms,windows-messages,common-dialog,C#,Winforms,Windows Messages,Common Dialog,我试图检测用户何时单击表单和公共对话框 形式相当简单。我创建了一个拦截消息的MessageFilter类: class MessageFilter : IMessageFilter { private const int WM_LBUTTONDOWN = 0x0201; public bool PreFilterMessage(ref Message message) { if (message.Msg == WM_LBUTTONDOWN)
表单
和公共对话框
形式相当简单。我创建了一个拦截消息的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)
当我运行控制台应用程序并单击表单
时,我会看到“活动”记录到控制台
当我将表单
替换为公共对话框
时:
SaveFileDialog dialog = new SaveFileDialog();
dialog.ShowDialog();
即使可以看到Windows消息被发送到CommonDialog(FWIW,我检测不到任何消息),我也无法再检测到鼠标单击:
那为什么我不能截获这些信息呢
我想到的是,由于
Application.AddMessageFilter
是特定于线程的,可能如果CommonDialog是在调用dialog.ShowDialog()
的线程之外的线程上创建的,我就不会收到这些消息
但是,我做了一个快速测试,尝试将WM_CLOSE
消息发送到调用dialog.ShowDialog()
的线程上的所有CommonDialogs,结果成功了:
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);
}
为什么我不能截获公共对话消息?我能做些什么?设置本地鼠标挂钩怎么样 对我的项目很有效
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();
. 和。@Jimi的备注部分我不明白为什么在我谈论CommonDialogs时,你会链接到一个关于消息框的部分。下面关于情态动词的部分并没有特别的帮助——或者我不知道你想让我从中得到什么。关于
HookProc
,我以前考虑过,但我希望它能应用于其他通用对话框,而不仅仅是FIleDialog。其次,看起来我必须在我的另一个扩展FileDialog的类中重写HookProc,如果可能的话,我不希望这样做。@Jimi此外,我应该从IMessageFilter的备注部分学到什么?它分派给控件和窗体,公共对话框是。。。也不我不确定我不能让它工作。当我将0
作为threadId参数传递给SetWindowsHookEx
时,它返回一个空句柄,我崩溃了。当我使用当前线程ID(这更符合我的要求)时,它会返回一个有效句柄,并且基本上每次勾选都会触发YOUR_EVENT_FUNC
,但我从不输入if
块。根据文档l
应该包含有关消息的信息,因此w==
应该是l==
吗?(即使我使用l
而不是w
,由于某种原因,if
块也不会被输入)(另外,您的一些外部方法类型是不正确的)ㄴ 您的_EVENT _FUNC应该在每次勾号时触发,因为它在您移动鼠标时被调用(如果使用鼠标,则会获取每个事件)。此外,w包含挂接消息的类型,l是指向的指针。所以,不要改变它。将WM_LBUTTONDOWN替换为WM_LBUTTONUP。而且,我们正在生成低级钩子,因此SetwindowsHookEx上的threadid应该为0,但您的程序出现错误,为0。。。如果这个片段不能解决你的问题,我将删除这篇文章。你的代码片段让我更接近了,但是我不得不使用当前的线程ID而不是0,并且我选择了WH_鼠标而不是LL版本。如果你不介意的话,我可以编辑你的代码片段来展示我做了什么。