Итак, мы приближаемся к концу серии! Что же нас ждет в этой части?
- Part 1. Overview.
- Part 2. MAF addin UI - gadget. Object model.
- Part 3. WPF-interop and "airspace" notion - hosting gadgets in complex shape windows.
- Part 4. Publishing MAF addin settings.
- Part 5. Handling unhandled exceptions in isolated addin domains, gadget host.
- Part 6. Cross-AppDomain events propagation powered by Juval Lowy's WCF Pub-Sub framework.
Sabotage! Разрабатывая приложение, открытое для расширения при помощи плагинов, вы можете столкнуться с ситуацией, в которой плагины, разработанные сторонними и не очень добросовестными производителями, будут рушить ваше приложение. Вы спросите зачем нам что-то делать, если они и так уже изолированы в sandbox. Но сложность заключается в том, что если в одном из доменов плагинов произойдет необработанное исключение, то кверху лапками полетит все приложение. Решение мое, конечно же основано на рекоммендациях комманды MAF, о том что делать в таких случаях:
- Using AppDomain Isolation to Detect Add-In Failures [Jesse Kaplan]
- More on Logging UnhandledExeptions from Managed Add-Ins [Jesse Kaplan]
- UnhandledExceptionHelper - вспомогательный класс для обработки исключительных ситуаций, как следует из его названия;
- GadgetHost - хост-обертка для гаджетов, машина состояний, изменяющая своё представление в зависимости от состояния плагина/гаджета;
Интерфейс этого незаменимого помощника выглядит следующим образом:
Следуя названию, его призвание обрабатывать исключительные ситуации в плагинах и не давать при этом основному приложению умереть. Как он это делает? Он обманывает CLR, так что его скорее надо было бы назвать UnhandledExceptionLiar =). Обман заключается в перехвате события необработанного исключения из потока в другом домене и блокировка этого потока до завершения приложения. Это единственный способ, который я нашел для безопасного перехвата исключения в домене плагина без последующего закрытия всего приложения. Следует заметить, что если исключение произойдет в UI потоке, даже не смотря на то, что код выполняется в другом домене (ибо потоки умеют пересекать границы доменов), то приложение рухнет. Это может произойти, например, при настройке параметров плагина или в логике gadget'а. Блокировка умирающего потока происходит при помощи метода Lock(), в котором другой поток (как правило UI), захватывает эксклюзивную блокировку. Кто же вызывает этот метод? Неужели это должен делать разработчик? Нет. Об этом и о многом другом позаботится GadgetHost.
GadgetHost.
GadgetHost это контрол, с помощью которого можно управлять состоянием нашего Gadget'a. Он принимает на вход AddInToken, инициаллизирует Gadet в background потоке, а затем, по вызове функции ShowGadget, как следует из названия, создает гаджет и размещает его как свой контент в UI.
При получении AddInToken GadgetHost создает экземпляр UnhandledExceptionHelper'а, который оповещает последнего о поломке и не дает приложению упасть. При поломке надо срочно привлечь внимание пользователя! И с этой функцией справляется мой FaultedGadgetPlaceholder - просто UserControl с эффектной картинкой (полчаса в Paint.Net =) ).
Вкратце так. Не пишите падающие гаджеты! =)
Удачи!