Вы можете увидеть, что происходит, если вы замените вызов MessageBox.Show на Debugger.Break и подключите отладчик с включенной встроенной отладкой, когда перерыв сработает.Стек вызовов выглядит следующим образом:
WindowsFormsApplication3.exe!WindowsFormsApplication3.Form1.notifyIcon1_MouseClick(object sender = {System.Windows.Forms.NotifyIcon}, System.Windows.Forms.MouseEventArgs e = {X = 0x00000000 Y = 0x00000000 Button = Left}) Line 30 + 0x1e bytes C#
System.Windows.Forms.dll!System.Windows.Forms.NotifyIcon.OnMouseClick(System.Windows.Forms.MouseEventArgs mea) + 0x6d bytes
System.Windows.Forms.dll!System.Windows.Forms.NotifyIcon.WmMouseUp(ref System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons button) + 0x7e bytes
System.Windows.Forms.dll!System.Windows.Forms.NotifyIcon.WndProc(ref System.Windows.Forms.Message msg) + 0xb3 bytes
System.Windows.Forms.dll!System.Windows.Forms.NotifyIcon.NotifyIconNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0xc bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg = 0x00000800, System.IntPtr wparam, System.IntPtr lparam) + 0x5a bytes
user32.dll!_InternalCallWinProc@20() + 0x23 bytes
user32.dll!_UserCallWinProcCheckWow@32() + 0xb3 bytes
user32.dll!_DispatchClientMessage@20() + 0x4b bytes
user32.dll!___fnDWORD@4() + 0x24 bytes
ntdll.dll!_KiUserCallbackDispatcher@12() + 0x2e bytes
user32.dll!_NtUserPeekMessage@20() + 0xc bytes
user32.dll!__PeekMessage@24() + 0x2d bytes
user32.dll!_PeekMessageW@20() + 0xf4 bytes
ole32.dll!CCliModalLoop::MyPeekMessage() + 0x30 bytes
ole32.dll!CCliModalLoop::PeekRPCAndDDEMessage() + 0x30 bytes
ole32.dll!CCliModalLoop::FindMessage() + 0x30 bytes
ole32.dll!CCliModalLoop::HandleWakeForMsg() + 0x41 bytes
ole32.dll!CCliModalLoop::BlockFn() - 0x5df7 bytes
ole32.dll!_CoWaitForMultipleHandles@20() - 0x51b9 bytes
WindowsFormsApplication3.exe!WindowsFormsApplication3.Form1.notifyIcon1_MouseClick(object sender = {System.Windows.Forms.NotifyIcon}, System.Windows.Forms.MouseEventArgs e = {X = 0x00000000 Y = 0x00000000 Button = Left}) Line 32 + 0x14 bytes C#
Соответствующей функцией является CoWaitForMultipleHandles.Это гарантирует, что поток STA не может блокировать синхронизируемый объект, не пересылая сообщения.Что очень вредно для здоровья, так как это может вызвать тупик.Особенно в случае NotifyIcon, поскольку при блокировке уведомляющего сообщения окно лотка зависает, и все значки перестают работать.
Далее вы видите модальный цикл COM, печально известный тем, что вызывает проблемы с повторным входом.Обратите внимание, как он вызывает PeekMessage (), вот как снова активируется обработчик событий MouseClick.
Что удивительно в этом стеке вызовов, так это отсутствие свидетельств перехода оператора lock в код, которыйвызывает CoWaitForMultipleHandles.Это как-то сделано самой Windows, я уверен, что в CLR для этого нет никаких условий.По крайней мере, не в версии SSCLI20.Это говорит о том, что Windows действительно обладает некоторыми встроенными знаниями о том, как CLR реализует класс Monitor.Довольно классная штука, без понятия, как они могли заставить это работать.Я подозреваю, что это исправляет адреса точек входа DLL для обнаружения кода.
Anyhoo, эти специальные контрмеры действуют только во время выполнения уведомления NotifyIcon.Обходной путь - отложить действие обработчика события до завершения обратного вызова.Вот так:
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) {
this.BeginInvoke(new MethodInvoker(delayedClick));
}
private void delayedClick() {
if (reentrancyDetected) System.Diagnostics.Debugger.Break();
reentrancyDetected = true;
lock (thisLock) {
//do nothing
}
reentrancyDetected = false;
}
Проблема решена.