📜 ⬆️ ⬇️

Features of Microsoft.Win32.SystemEvents in a Multi AppDomain Application

Once upon a time there was a console application that created appdomain and started a service based on Topshelf in it. But once a bug appeared in it - after pressing Ctrl-C, it did not complete its work, but reported receiving a shutdown command and hung in that state. A brief analysis showed that it hung on unloading the domain, but, strangely, did not throw out the exception CannotUnloadAppDomainException .

Further, it was noted that the application freezes when closed only after performing certain tasks, and there are several “extra” threads before unloading the domain, which are not observed in the case of normal application termination. As you can guess from the name, the case turned out to be in SystemEvents . When using System.Drawing.SolidBrush , the handler is added to SystemEvents.UserPreferenceChanging , but since this is a separate appdomain, the type is initialized again, another stream is created with the name ".NET SystemEvents", which, when started, adds its own to the console handlers.
When the application is SystemEvents.Dispose() is called, and there when the UnsafeNativeMethods.SetConsoleCtrlHandler(consoleHandler, 0) console handler is UnsafeNativeMethods.SetConsoleCtrlHandler(consoleHandler, 0) application hangs. I naguglil couple of mentions of a problem with removal of console handlers, but all solutions were reduced to a call of SystemEvents.Shutdown() through Reflection, but it did not help me, and should not seem to help, it then causes Dispose ().
Then I decided that I need to somehow avoid creating a second SystemEvents thread, and a loophole was found in the SystemEvents.EnsureSystemEvents(bool requireHandle, bool throwOnRefusal) method SystemEvents.EnsureSystemEvents(bool requireHandle, bool throwOnRefusal) :

 if (Thread.GetDomain().GetData(".appDomain") != null) { if (throwOnRefusal) { throw new InvalidOperationException(SR.GetString(SR.ErrorSystemEventsNotSupported)); } return; } //   SystemEvents 

In my case, this method was called with throwOnRefusal = false, so adding domain.SetData(".appDomain", new object()) avoids creating another SystemEvents stream and freezing when you remove its console handler, the application shuts down correctly.
A side effect is possible in the case of code that relies on checking the ".appDomain" property, it can count our application for asp.net)) If someone knows what problems may be caused by this setting, or knows a safer and more appropriate solution to the problem, please share.

')

Source: https://habr.com/ru/post/238333/


All Articles