пятница, 5 декабря 2008 г.

Much of muchness. Part 5. Handling unhandled exceptions in isolated addin domains, gadget host.


Итак, мы приближаемся к концу серии! Что же нас ждет в этой части?

Судя по списку, мне осталось рассказать о способах борьбы с коррупцией, или лучше сказать с саботажем, в рядах плагинов, о хосте для гаджетов и, оставим на сладкое, кросс-доменный EventAggregator.

Sabotage! Разрабатывая приложение, открытое для расширения при помощи плагинов, вы можете столкнуться с ситуацией, в которой плагины, разработанные сторонними и не очень добросовестными производителями, будут рушить ваше приложение. Вы спросите зачем нам что-то делать, если они и так уже изолированы в sandbox. Но сложность заключается в том, что если в одном из доменов плагинов произойдет необработанное исключение, то кверху лапками полетит все приложение. Решение мое, конечно же основано на рекоммендациях комманды MAF, о том что делать в таких случаях: И состоит это решение из 2ух частей:
  • UnhandledExceptionHelper - вспомогательный класс для обработки исключительных ситуаций, как следует из его названия;
  • GadgetHost - хост-обертка для гаджетов, машина состояний, изменяющая своё представление в зависимости от состояния плагина/гаджета;
UnhandledExceptionHelper
Интерфейс этого незаменимого помощника выглядит следующим образом:



Следуя названию, его призвание обрабатывать исключительные ситуации в плагинах и не давать при этом основному приложению умереть. Как он это делает? Он обманывает CLR, так что его скорее надо было бы назвать UnhandledExceptionLiar =). Обман заключается в перехвате события необработанного исключения из потока в другом домене и блокировка этого потока до завершения приложения. Это единственный способ, который я нашел для безопасного перехвата исключения в домене плагина без последующего закрытия всего приложения. Следует заметить, что если исключение произойдет в UI потоке, даже не смотря на то, что код выполняется в другом домене (ибо потоки умеют пересекать границы доменов), то приложение рухнет. Это может произойти, например, при настройке параметров плагина или в логике gadget'а. Блокировка умирающего потока происходит при помощи метода Lock(), в котором другой поток (как правило UI), захватывает эксклюзивную блокировку. Кто же вызывает этот метод? Неужели это должен делать разработчик? Нет. Об этом и о многом другом позаботится GadgetHost.

GadgetHost.

GadgetHost это контрол, с помощью которого можно управлять состоянием нашего Gadget'a. Он принимает на вход AddInToken, инициаллизирует Gadet в background потоке, а затем, по вызове функции ShowGadget, как следует из названия, создает гаджет и размещает его как свой контент в UI.



При получении AddInToken GadgetHost создает экземпляр UnhandledExceptionHelper'а, который оповещает последнего о поломке и не дает приложению упасть. При поломке надо срочно привлечь внимание пользователя! И с этой функцией справляется мой FaultedGadgetPlaceholder - просто UserControl с эффектной картинкой (полчаса в Paint.Net =) ).

Вкратце так. Не пишите падающие гаджеты! =)

Удачи!