О чём не пишут в книгах по Delphi
Шрифт:
Для обнаружения уже запущенного приложения многие авторы предлагают использовать именованные системные объекты (мьютексы, семафоры, атомы и т.п.). При запуске программа пытается создать такой объект с определенным именем. Если оказывается, что такой объект уже создан, программа "понимает", что она — вторая копия, и завершается. Недостаток такого подхода — с его помощью можно установить только сам факт наличия предыдущей копии, но не более того. В нашем случае задача шире: при запуске второго экземпляра приложения должен активизироваться первый, а если второму экземпляру была передана непустая командная строка, первый должен получить эту строку и выполнить соответствующее действие, поэтому описанный
Для решения задачи нам подойдут почтовые ящики (mailslots). Это специальные системные объекты для односторонней передачи сообщений между приложениями (ничего общего с электронной почтой эти почтовые ящики не имеют). Под сообщением здесь понимаются не сообщения Windows, а произвольный набор данных (здесь больше подходит скорее термин "дейтаграмма", а не "сообщение"). Каждый почтовый ящик имеет уникальное имя. Алгоритм отслеживания повторного запуска с помощью почтового ящика следующий. Сначала программа пытается создать почтовый ящик как сервер. Если оказывается, что такой ящик уже существует, то она подключается к нему как клиент и передает содержимое своей командной строки и завершает работу. Сервером в таком случае становится экземпляр приложения, запустившийся первым, — он-то и создаёт почтовый ящик. Остальным экземплярам останется только передать ему данные.
В случае аварийного завершения программы система сама закроет все открытые ею дескрипторы, поэтому даже если первая копия будет снята системой и не сможет корректно закрыть дескриптор почтового ящика, ящик будет уничтожен и не помешает пользователю запустить новую копию программы.
Почтовый ящик лучше создать как можно раньше, поэтому мы будем его создавать не в методе формы, а в основном коде проекта, который обычно программист не исправляет. В результате код в dpr-файле проекта будет выглядеть так, как показано в листинге 1.48.
Теперь осталось "научить" первую копию приложения обнаруживать момент, когда в почтовом ящике оказываются сообщения, и забирать их оттуда. Было бы идеально, если при поступлении данных главная форма получала бы какое-то сообщение, но готового такого механизма, к сожалению, не существует. Из положения можно выйти, задействовав события.
События — это объекты синхронизации, использующиеся в системе. Событие может быть взведено и сброшено. С помощью функции
В принципе, при использовании перекрытого ввода-вывода система может сама взводить указанное программой событие при получении данных почтовым ящиком, но перекрытый ввод-вывод имеет ограниченную поддержку в Windows 9х/МЕ и на почтовые ящики не распространяется. Чтобы приложение могло работать не только в Windows NT/2000/XP, мы не будем применять перекрытый ввод-вывод.