Особенности Microsoft.Win32.SystemEvents в приложении с несколькими AppDomain
Жило-было консольное приложение, которое создавало appdomain и запускало в нём сервис на основе Topshelf. Но однажды в нём появился баг — после нажатия Ctrl-C оно не завершало свою работу, а сообщало о получении команды на выключение и зависало в таком состоянии. Краткий анализ показал, что зависало оно на выгрузке домена, но, что странно, не выкидывало исключение CannotUnloadAppDomainException.Далее было замечено, что приложение зависает при закрытии только после выполнении определенных задач, и при этом есть несколько «лишних» потоков перед выгрузкой домена, которых не наблюдается в случае нормального завершения приложения. Как можно догадаться из названия, дело оказалось в SystemEvents. При использовании System.Drawing.SolidBrush происходит добавление обработчика в SystemEvents.UserPreferenceChanging, но так как это отдельный appdomain, инициализация типа происходит еще раз, создается еще один поток с именем ».NET SystemEvents», который при старте добавляет в обработчики консоли свой.При закрытии приложения вызывается SystemEvents.Dispose (), и в нём при удалении обработчика консоли UnsafeNativeMethods.SetConsoleCtrlHandler (consoleHandler, 0) приложение зависает. Я нагуглил пару упоминаний о проблеме с удалением обработчиков консоли, но все решения сводились к вызову SystemEvents.Shutdown () через Reflection, но мне это не помогло, да и не должно вроде помогать, оно ж потом вызывает Dispose ().Тогда я решил, что нужно каким то образом избежать создания второго потока SystemEvents, и лазейка нашлась в методе SystemEvents.EnsureSystemEvents (bool requireHandle, bool throwOnRefusal): if (Thread.GetDomain ().GetData (».appDomain») != null) { if (throwOnRefusal) { throw new InvalidOperationException (SR.GetString (SR.ErrorSystemEventsNotSupported)); } return; } //тут идёт инициализация SystemEvents В моём случае этот метод вызывался с throwOnRefusal = false, поэтому добавление domain.SetData (».appDomain», new object ()) позволяет избежать создания еще одного потока SystemEvents и зависания при удалении его обработчика консоли, приложение корректно завершает свою работу.Возможен побочный эффект в случае кода, который опирается на проверку свойства ».appDomain», он может посчитать наше приложение за asp.net)) Если кто то знает, какие проблемы могут быть вызваны этой настройкой, или знает более безопасное и подходящее решение проблемы, прошу поделиться.