Я пытаюсь определить, когда пользователь нажимает на 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);
}
Почему я не могу перехватить Comсообщения monDialog?Что я могу с этим поделать?