Многие программисты программного обеспечения сталкивались с задачей запрета запуска копий приложения. Это делается с различными целями и зависит от ситуации. Существует даже отдельный термин для решения данной задачи — Mutex.
Также данная задача иногда возникает перед системными администраторами с тем отличием, что приложение стороннее (разработано другой организацией). Эта статья описывает относительно простой способ решения проблемы запрета запуска копий стороннего приложения.
Задача
Как то раз у меня на работе возникла проблема — некоторые сотрудники умудрялись запускать несколько копий приложений. Запуск нескольких копий, конечно, не приводил к катастрофическим результатам, но иногда порождал различные «глюки», нестабильную работу программы. Это, естественно, добавляло мне головной боли от многочисленных жалоб на эти «глюки».
Первым делом я провел разъяснительную беседу с сотрудниками о проблеме запуска копий приложения и о том, как этого избежать. Данная мера помогла на ближайшие пару дней, а далее всё вернулось на круги своя. Жаловаться начальству на такое попустительство некоторых работников было чревато моим увольнением, т.к. начальство безумно ценило этих работников, а ко мне отношение было кардинально противоположным.
Как говориться «Раз гора не идёт к Магомету, то Магомет идёт к горе». Я решил сам программно запретить запуск копий приложения. Задача оказалась не тривиальная.
Несложный анализ выявил 2 особенности моей ситуации:
Во-первых, у меня приложение стороннее, т.е. его разработчик не наша организация и доступа к исходному коду нет (поясню, приложение — один из продуктов Microsoft). Каких-либо регулирующих запуск настроек и параметров приложение не имеет.
Во-вторых, приложение запускается не напрямую, а как программа по умолчанию для открытия файлов определённого расширения. То есть приложение запускается с параметром (путь файла данных, который нужно открыть).
Решение — теория
Поиск в Интернете не дал мне явного решения. Запрета запуска второй копии приложения в виде системных настроек, как я понял не существует (хотя есть API). Также я нашел десятки примеров реализации mutex-ов на различных языках программирования, но все они, естественно, требовали внесения изменений в код и мне не подходили. Надо было искать своё оригинальное решение, и желательно попроще.
Единственное решение, которое я видел, было создание своеобразной «прослойки» между пользователем и приложением. Должно происходить следующие:
- Пользователь инициирует запуск приложения, открывая файл данных, ассоциируемый с приложением.
- По умолчанию вместо требуемого приложения запускается приложение-прослойка.
- Это приложение получает в виде параметра путь к файлу данных, создаёт mutex и запускает требуемое приложение с полученными параметрами. Приложение-прослойка, соответственно, должно быть скрыто.
- Далее запускается требуемое приложение и пользователь работает в нём. Приложение-прослойка всё это время продолжает работу. Его mutex блокирует попытки запуска копии.
- По окончанию работы пользователь закрывает основную программу. Программа-прослойка закрывается автоматически.
Как вы понимаете, такой подход достаточно универсален. Это приложение-прослойка может выполнять не только задачу запрета запуска копии программы, но и многие другие (логирование, запуск сопутствующих программ, оповещение и т.д.). Для пользователя работа приложения-прослойки будет незаметным.
Решение — практика
Практическое решение приведено для приложения «Блокнот» (понятное дело, что на его месте может быть любое приложение).
Выбор языка программирования для написания приложения-прослойки не вызвал у меня трудностей. Мне нужно было это сделать быстро и без заморочек, поэтому я выбрал язык автоматизации AutoIt. Легковесный AutoIt изначально «заточен» под работу со сторонними приложениями. Скачать его можно с официального сайта. В комплекте помимо самого пакета языка идёт достаточно удобный редактор кода SciTE Script Editor, в котором удобно писать скрипты для AutoIt.
Естественно, можно использовать любой другой язык с необходимыми возможностями.
Программный код скрипта (текстовый файл с расширением .au3) не представляет ничего сложного:
; ** Скрипт запрещения двойного запуска приложений**
; Функция Singleton создаёт семафор при помощи WinAPI. Если такой семафор уже создан, то заканчиваем работу
Func Singleton($semaphore)
Local $ERROR_ALREADY_EXISTS = 183
DllCall("kernel32.dll", "int", "CreateSemaphore", "int", 0, "long", 1, "long", 1, "str", $semaphore)
Local $lastError = DllCall("kernel32.dll", "int", "GetLastError")
If $lastError[0] = $ERROR_ALREADY_EXISTS Then Exit -1
EndFunc
Singleton("Mutex")
; Системная функция ShellExecuteWait запускает приложение и ожидает окончания работы с ним
; Дожидаться завершения программы нужно, чтобы корректно работал Mutex
; $CmdLineRaw - параметр запуска получаемый скриптом
ShellExecuteWait("C:WINDOWSnotepad.exe", $CmdLineRaw, @ScriptDir, "open", @SW_MAXIMIZE)
Как вы сами видите, весь код условно можно разделить на две части: mutex и запуск требуемого приложения. Более детально я объяснять не буду, т.к. статья посвящена не изучению возможностей AutoIt.
Написав скрипт его можно откомпилировать либо в редакторе SciTE Script Editor, либо через контекстное меню файла (правый клик по файлу скрипта). После чего его уже можно использовать. Можно также откомпилировать с дополнительными настройками (битность системы и иконка приложения) при помощи утилиты Aut2Exe, которая также идёт в комплекте с AutoIt.
Далее всё элементарно — помещаем получившееся приложение-прослойку в какую-нибудь директорию и настраиваем открытие по умолчанию для файлов данных (в примере для файлов с расширением .txt) при помощи этого приложения-прослойки.
На этом всё. Проверяем и радуемся решенной задаче — теперь никто не сможет запустить две копии «Блокнота».
Послесловие
Данный способ позволил мне решить проблему двойного запуска приложений. Правда, на следующий день после установки приложения-прослойки ко мне прибежали нерадивые сотрудники со словами «Оно не запускается». Пришлось провести разъяснительную беседу с сотрудниками на тему «Приложение не запускается, потому что уже запущено». Эта беседа оказалась куда эффективнее, т.к. особого выбора у сотрудников не было. Уже почти месяц данных проблем не возникало.
Если кто из читателей знает способ написать mutex в виде .bat файла для данной задачи, то поделитесь опытом. У меня есть подозрение, что такой способ есть.
Автор: VitaZheltyakov