Почему я не могу обнаружить сообщения Windows, отправляемые на мой CommonDialog? Как правильно их перехватить?
Я пытаюсь определить, когда пользователь нажимает на 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? Что я могу с этим поделать?
1 ответ
Как насчет настройки локальной мыши?
Хорошо работает на моем проекте.
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();