Blog. Just Blog

Запрет запуска нескольких копий программы

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Запрет запуска нескольких копий программы бывает полезен если может возникнуть конфликт из-за занятых системных ресурсов, монопольно открытых файлов или если задачи приложения подразумевают наличие только одного его экземпляра. Проверка наличия работающей копии программы реализуется несколькими способами в зависимости от поставленной задачи.

Первый способ основан на том, что в приложении можно определить расшаренную секцию, данные из которой будут доступны для всех его запущенных экземпляров. Достаточно прописать в ней некоторую переменную и присвоить ей уникальное значение. При старте выполняется проверка, и если значение переменной равно начальному, то считается что это старт первой копии, иначе приложение является второй копией и должно завершить свою работу. Поэтому первая копия приложения сразу после запуска и проверки должна заменить значение переменной на другое.
  1. ; Расшаренная секция, общая для всех копий данного приложения
  2. section '.shared' data readable writeable shareable
  3. started  dd  1       ; Флаг первого запуска
  4.  
  5. ; Сегмент кода
  6. section '.code' code readable executable
  7.         cmp     [started],1       ; Уже запущен экземпляр программы?
  8.         jne     already_started   ; Да, на выход
  9.         xor     eax,eax
  10.         ; Префикс LOCK и команда XCHG используются для предотвращения
  11.         ; возможных конфликтов на многопроцессорных машинах
  12.         lock xchg eax,[started]   ; Сбросить флаг
  13.         ; Нормальный запуск программы 
  14.         ...
  15. already_started:
  16.         ; Выход из программы
  17.         ...
Этот способ будет работать только если приложение запускается из одной и той же папки. Если скопировать его в другую папку, то система будет считать что это совершенно другое приложение и разрешит запуск его второй копии. Впрочем, способ с расшаренной секцией вполне имеет место быть, если требуется запретить именно запуск второй копии приложения из одной и той же папки. Для глобального запрета этот способ не подходит.

Второй способ использует поиск окна по заголовку или имени класса окна через функцию FindWindow. Этот способ может применяться для глобального запрета запуска второй копии приложения.
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. title   db 'Only One Demo #2',0       ; Заголовок окна
  5.  
  6. ; Сегмент кода
  7. section '.code' code readable executable
  8.         ...
  9.          invoke  FindWindow,NULL,title ; Попробовать найти окно по заголовку
  10.          or      eax,eax               ; Уже запущен экземпляр программы?
  11.          jnz     already_started       ; Да, на выход
  12.         ; Нормальный запуск программы 
  13.         ...
  14. already_started:
  15.         ; Выход из программы
  16.         ...
Поиск по имени класса окна более надежный и выполняется аналогичным способом. Немного отойду от темы, но скажу что этим простым способом можно также проверять наличие других запущенных приложений, совместно с которыми не должно работать ваше. Также этот способ хорошо использовать для передачи пользовательских сообщений окну вашей программы, так как функция FindWindow возвратит хэндл главного окна работающего приложения. Например при попытке запуска второй копии будет разворачиваться или показываться окно первого уже работающего экземпляра вашей программы.

Третий, самый надежный и предпочтительный способ глобальной проверки и запрета основан на использовании мьютексов. Одновременно в системе не может быть двух мьютексов с одинаковыми именами, поэтому если один процесс создаст мьютекс с уникальным именем, то попытка создания одноименного мьютекса пока активен первый процесс, приведет к возникновению ошибки ERROR_ALREADY_EXISTS. Это и будет считаться признаком того, что одна копия вашей программы уже находится в памяти.
  1. ; Определить константу кода ошибки
  2. ERROR_ALREADY_EXISTS = 000000B7h
  3.  
  4. ; Сегмент данных
  5. section '.data' data readable writeable
  6.  
  7. mutex   db 'Only One Demo #3',0          ; Уникальное имя мьютекса
  8.  
  9. ; Сегмент кода
  10. section '.code' code readable executable
  11.         ...
  12.         invoke  CreateMutex,NULL,0,mutex ; Создать мьютекс
  13.         invoke  GetLastError             ; Проверить код ошибки
  14.         cmp     eax,ERROR_ALREADY_EXISTS ; Такой мьютекс уже есть?
  15.         je      already_started          ; Да, на выход
  16.         ; Нормальный запуск программы 
  17.         ...
  18. already_started:
  19.         ; Выход из программы
  20.         ...
В интернете упорно копируют одну и ту же статью, в которой написано что якобы на некоторых операционных системах повторный вызов CreateMutex не возвращает ошибку и проверять наличие ранее созданного мьютекса надо через функцию OpenMutex. Однако на моей Windows XP Pro SP3 все работает как задумано, да и в официальной технической документации четко написано:


If you need to detect whether another instance already exists, create a named mutex using the CreateMutex function. If the GetLastError function returns ERROR_ALREADY_EXISTS, another instance of your application exists (it created the mutex).


Просто имейте этот факт в виду. Вместо мьютексов точно по такой же схеме можно использовать события (events) с уникальными именами. Работа с ними осуществляется с помощью функций OpenEvent и CreateEvent.

Четвертый способ основан на создании в памяти виртуального файла при помощи функции CreateFileMapping. Аналогично мьютексам и событиям в системе одновременно может быть создан только один виртуальный файл с определенным именем. Попытки создания второго такого же файла приведут к ошибке ERROR_ALREADY_EXISTS.
  1. ; Определить константу кода ошибки
  2. ERROR_ALREADY_EXISTS = 000000B7h
  3.  
  4. ; Сегмент данных
  5. section '.data' data readable writeable
  6.  
  7. fname   db 'Only One Demo 4',0           ; Уникальное имя виртуального файла
  8.  
  9. ; Сегмент кода
  10. section '.code' code readable executable
  11.         ...
  12.         invoke  CreateFileMapping,0FFFFFFFFh,NULL,\
  13.                 PAGE_READWRITE,NULL,1024,fname ; Создать виртуальный файл
  14.         invoke  GetLastError             ; Проверить код ошибки
  15.         cmp     eax,ERROR_ALREADY_EXISTS ; Такой виртуальный файл уже есть?
  16.         je      already_started          ; Да, на выход
  17.         ; Нормальный запуск программы 
  18.         ...
  19. already_started:
  20.         ; Выход из программы
  21.         ...
Это лишь несколько способов проверки, на самом деле их очень много и они ограничены лишь вашей фантазией.

Не забывайте в разных ваших программах использовать разные имена мьютексов, событий, виртуальных файлов, классов окон, заголовков окон и т.д. А то после неудачного копипаста куска кода придется немало поломать голову почему приложение работает не так как надо. Примеры программ с исходными текстами для всех описанных способов прилагаются.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

There.Can.Be.Only.One.Demo.zip (8,034 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 16134 | Комментариев: 7

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (29.05.2020 в 20:11):
Нарисовал полноценную статью про запуск ограниченного количества копий:
http://www.manhunter.ru/assemb...ogrammi.html
ManHunter (23.09.2018 в 23:04):
Элементарно. Начинаешь создавать мьютексы с числовым счетчиком, типа "Mutex_01", "Mutex_02" и т.д., увеличивая счетчик до нужного значения, пока не встретится свободный. Если все мьютексы окажутся заняты, значит предельное количество копий приложения уже запущено. Аналогично с CreateFileMapping.
Aleksandr (23.09.2018 в 02:32):
А как реализовать запуск ограниченного числа копий программы больше одного ?
Adam.Martin (07.09.2010 в 14:34):
Автору спасибо. Для меня лично - очень полезный ресурс
ManHunter (06.09.2010 в 16:13):
Чо-то долго шел :)
Adam.Martin (06.09.2010 в 16:11):
Вот для этого и нужен интернет.
Нужно было на FASMe проверить копию программы в памяти. Чтото слышал про мьютекс, нужен был пример, вышел на эту страницу в 2 минуты.
BarMentaLisk (11.02.2009 в 19:58):
Весьма подробно все описано, спасибо!
Лично я пока довольствовался проверкой существования одноименного окна, но может когда-нибудь для безоконного приложения заюзаю и мутексы =).

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0035 сек.) / Память: 4.5 Mb
Наверх