Одно из самых жутких, заброшенных и тоскливых мест Windows – это управление приложениями. Целиком мне сейчас о нём говорить не хочется, скажу только об одном аспекте: об анинсталле. Как он устроен – это дичайший бред. Попробуйте удалить несколько случайных продуктов на системе, которая проработала пару лет!
Не найден msi-файл установки, удаление невозможно.
Пропал компонент приложения, удаление невозможно.
Кто-то перезаписал мою регистрацию, удаление невозможно.
Кто-то вручную удалил с диска файлы, удаление невозможно.
Да нет конечно же! Это лентяи-программисты заставляют пользователей плясать под свою дудку. Вместо того, чтобы подумать головой и написать нормальный деинсталлятор, они предпочитают переваливать все проблемы на плечи пользователя. Нет уверенности, что мы удалим всё? Ну так не будем удалять ничего! Пусть пользователь сам разбирается.
Удаление, уничтожение, очистка – это операции, которые должны выполняться “наилучшим возможным образом”. Т.е. в каждой ситуации должен удаляться абсолютный максимум того, что можно удалить. Если каких-то файлов уже нет – удаляем те, что остались. Если установочного пакета нет – ладно, удаляем хотя бы то, о чём сохранились записи. Не сохранилось записей – удаляем хотя бы то, о чём можно догадаться без них.
Ошибок в этих операциях не должно существовать в принципе. Они должны выполняться всегда.
В качестве бонуса: этот же принцип относится и к деструкторам объектов в программировании. Деструктор должен работать всегда. В каком бы состоянии не находился объект, деструкция, деинициализация, и вообще всё, что угодно, с приставкой “де-“, должно игнорировать ошибки и удалять всё возможное.
Это не значит, что надо ловить в деструкторе все ошибки и затыкать им рты. Если ошибка уже случилась, её, как обычно, нужно вывести пользователю. Просто деструктор должен быть написан так, чтобы выполнять максимум без ошибок даже в неизвестных обстоятельствах. Например, даже если деструктор у вас вызывается единожды, и lpMyObject всегда существует, нужно писать:
if (lpMyObject != NULL) {
FreeMyObject(lpMyObject);
lpMyObject = NULL;
}
Хорошая практика – это чтобы ваш деструктор можно было вызвать на любом этапе жизни объекта, и он успешно выполнился бы. Даже в середине конструктора. Даже в процессе выполнения какой-то критической функции. Потому, что деструктор не может позволить себе вызывать исключения: его самого, вероятно, вам придётся вызывать, если вы надеетесь программно обработать какие-то исключения. Следовательно, деструктор уже должен быть готов обрабатывать исключительные ситуации.