el-f
На этот вопрос пришло просто огромное количество ответов, и разных. Отдельное спасибо хочу сказать Андрею Твердохлебову, который прислал ссылку на действительно замечательную статьюна эту тему, которая позволила многое прояснить. Именно в ней приводится по-настоящему правильный и надежный способ.
К сожалению, действительно хороших ответов, несмотря на большое общее количество, получилось довольно мало. Многие удовольствовались в ответе решением задачи ограничения числа запущенных экземпляров до одного, совершенно забывая о том, что задача еще и активизировать уже запущенный экземпляр.
A 1 Относительно вопроса, заданного в №25 данной рассылки хотелось бы сразу высказать сильное сомнение по поводу возможности его решения при помощи использования имен оконных классов. Мне кажется, что каждый экземпляр приложения в операционной системе Windows имеет, как это не пародоксально звучит, свой набор зарегистрированных оконных классов. Общность стандартных (системных) оконных классов поддерживается автоматической загрузкой в адресное пространство системных библиотек, которые, в момент своей инициализации, регистрируют свои оконные классы. Список DLL (только они позволяют делать общедоступными определенные виды окон), подгружаемых автоматом каждому приложению, хранится где-то в реестре (не помню точно где). Еще одним доводом в пользу предположения об уникальности списка зарегистрированных оконных классов каждого приложения (экземпляра приложения) служит сама процедура RegisterClass(Ex). В качестве аргумента данной процедуры выступает указатель на структуру, одним из элементов которой является указатель (адрес) на оконную процедуру. Нет 100% гарантии того, что разные DLL проекцируются в одно и то же адресное пространство всех приложений без исключений. Следовательно, адрес оконной процедуры перестает нести смысловую нагрузку. Из изложенного выше, позвольте сделать вывод: если в результате регистрации приложением оконного класса произошла ошибка типа "Оконный класс с указанным именем уже существует" это означает лишь то, что именно ЭТО приложение уже зарегистрировало подобный класс. И наоборот, если регистрация прошла успешно, то это не значит, что нет такого приложения (экземпляра приложения) которое не зарегистрировало бы оконный класс с подобным именем. Следовательно подобный подход при решении подобной проблемы невозможен.
Решить указанную проблему можно лишь при помощи объектов ядра операционной системы, объектов файловой системы (так, по моему, решаются подобные задачи в OS типа Linux/Unix) и некоторых других объектов (типа mailslot, TCP ports и наверное можно придумать что либо еще). Важно выполнение следующих условий:
1. Объекты должны быть доступны из различных приложений
2. Объекты должны быть одинаковым образом идентефицированы для всех приложений (идет отказ от дескрипторов объектов, которые актуальны только в рамках одного процесса).
3. Желательно (а может обязательно?), что бы OS поддерживала синхронизацию доступа к данным объектам.
Самый простой способ идентефикации объектов заключается в присвоении им строковых имен. Такой способ применяестся к объектам ядра, файловой системы, mailslot. TCP способ использует разименовку по номеру порта. И имя и номер имеют одно и то же значение для всех приложений. Для функции RegisterClass(Ex), похоже, не выполняется первое условие. Способ определения повторного запуска экземпляра приложения давно известен и использует объект ядра системы типа "mutex". В задаче, кроме того, требуется подать сигнал активизации первому экземпляру приложения. Пришлось модифицировать известный способ, попутно решив эту проблему для себя, и использовать объект ядра типа "event". В общем, принцип работы схемы выглядит так:
1. Попытка получить доступ к объекту ядра по имени
1.1. Доступ получить не удалось из-за отстутствия объекта с указанным именем – данный экземпляр приложения первый. Переходим к п. 2
1.2. Доступ получен – большая вероятность того что данное приложение запущено во второй раз. Почему не 100% уверенность? Делаем скидку на то, что кто то другой мог выбрать для своих нужд именно этот тип объектов и именно с этим именем :). Переходим на п. 3
2. Инициализация и активация главного окна
2.1. Создаем объект ядра с именем, использованным в п. 1
2.2. Инициализируем и запускаем приложение
2.3. Время от времени проверяем поступление сигнала о запуске второй копии приложения
Читать дальше