Запрет запуска нескольких копий программы
Запрет запуска нескольких копий программы бывает полезен если может возникнуть конфликт из-за занятых системных ресурсов, монопольно открытых файлов или если задачи приложения подразумевают наличие только одного его экземпляра. Проверка наличия работающей копии программы реализуется несколькими способами в зависимости от поставленной задачи.
Первый способ основан на том, что в приложении можно определить расшаренную секцию, данные из которой будут доступны для всех его запущенных экземпляров. Достаточно прописать в ней некоторую переменную и присвоить ей уникальное значение. При старте выполняется проверка, и если значение переменной равно начальному, то считается что это старт первой копии, иначе приложение является второй копией и должно завершить свою работу. Поэтому первая копия приложения сразу после запуска и проверки должна заменить значение переменной на другое.
Code (Assembler) : Убрать нумерациюВыделить код
- ; Расшаренная секция, общая для всех копий данного приложения
- section ‘.shared’ datareadablewriteableshareable
- started dd 1 ; Флаг первого запуска
- ; Сегмент кода
- section ‘.code’ codereadableexecutable
- cmp [ started ] , 1 ; Уже запущен экземпляр программы?
- jne already_started ; Да, на выход
- xoreax , eax
- ; Префикс LOCK и команда XCHG используются для предотвращения
- ; возможных конфликтов на многопроцессорных машинах
- lockxchgeax , [ started ] ; Сбросить флаг
- ; Нормальный запуск программы
- . . .
- already_started :
- ; Выход из программы
- . . .
Этот способ будет работать только если приложение запускается из одной и той же папки. Если скопировать его в другую папку, то система будет считать что это совершенно другое приложение и разрешит запуск его второй копии. Впрочем, способ с расшаренной секцией вполне имеет место быть, если требуется запретить именно запуск второй копии приложения из одной и той же папки. Для глобального запрета этот способ не подходит.
Как запускать несколько копий приложения в OS X
Второй способ использует поиск окна по заголовку или имени класса окна через функцию FindWindow. Этот способ может применяться для глобального запрета запуска второй копии приложения.
Code (Assembler) : Убрать нумерациюВыделить код
Поиск по имени класса окна более надежный и выполняется аналогичным способом. Немного отойду от темы, но скажу что этим простым способом можно также проверять наличие других запущенных приложений, совместно с которыми не должно работать ваше. Также этот способ хорошо использовать для передачи пользовательских сообщений окну вашей программы, так как функция FindWindow возвратит хэндл главного окна работающего приложения. Например при попытке запуска второй копии будет разворачиваться или показываться окно первого уже работающего экземпляра вашей программы.
Третий, самый надежный и предпочтительный способ глобальной проверки и запрета основан на использовании мьютексов. Одновременно в системе не может быть двух мьютексов с одинаковыми именами, поэтому если один процесс создаст мьютекс с уникальным именем, то попытка создания одноименного мьютекса пока активен первый процесс, приведет к возникновению ошибки ERROR_ALREADY_EXISTS. Это и будет считаться признаком того, что одна копия вашей программы уже находится в памяти.
Схема запуска на оптимизации.
Code (Assembler) : Убрать нумерациюВыделить код
- ; Определить константу кода ошибки
- ERROR_ALREADY_EXISTS = 000000B7h
- ; Сегмент данных
- section ‘.data’ datareadablewriteable
- mutex db ‘Only One Demo #3’ , 0 ; Уникальное имя мьютекса
- ; Сегмент кода
- section ‘.code’ codereadableexecutable
- . . .
- invoke CreateMutex , NULL , 0 , mutex ; Создать мьютекс
- invoke GetLastError ; Проверить код ошибки
- cmpeax , ERROR_ALREADY_EXISTS ; Такой мьютекс уже есть?
- je already_started ; Да, на выход
- ; Нормальный запуск программы
- . . .
- already_started :
- ; Выход из программы
- . . .
В интернете упорно копируют одну и ту же статью, в которой написано что якобы на некоторых операционных системах повторный вызов 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.
Code (Assembler) : Убрать нумерациюВыделить код
- ; Определить константу кода ошибки
- ERROR_ALREADY_EXISTS = 000000B7h
- ; Сегмент данных
- section ‘.data’ datareadablewriteable
- fname db ‘Only One Demo 4’ , 0 ; Уникальное имя виртуального файла
- ; Сегмент кода
- section ‘.code’ codereadableexecutable
- . . .
- invoke CreateFileMapping , 0FFFFFFFFh , NULL ,
- PAGE_READWRITE , NULL , 1024 , fname ; Создать виртуальный файл
- invoke GetLastError ; Проверить код ошибки
- cmpeax , ERROR_ALREADY_EXISTS ; Такой виртуальный файл уже есть?
- je already_started ; Да, на выход
- ; Нормальный запуск программы
- . . .
- already_started :
- ; Выход из программы
- . . .
Это лишь несколько способов проверки, на самом деле их очень много и они ограничены лишь вашей фантазией.
Не забывайте в разных ваших программах использовать разные имена мьютексов, событий, виртуальных файлов, классов окон, заголовков окон и т.д. А то после неудачного копипаста куска кода придется немало поломать голову почему приложение работает не так как надо. Примеры программ с исходными текстами для всех описанных способов прилагаются.
Примеры программ с исходными текстами (FASM)
Источник: www.manhunter.ru
Как отключить несколько экземпляров приложения в Windows 10
Приложения могут ограничивать себя, так что вы можете запускать только один их экземпляр. Вообще говоря, приложения, которые запускают только один экземпляр, делают это в зависимости от их характера. Некоторым приложениям не нужно запускать несколько экземпляров.
Другие приложения, такие как браузеры, видеоплееры, текстовые процессоры и т. Д., Должны запускать несколько экземпляров, и они это делают. Тем не менее, если вы хотите ограничить запуск приложения несколькими экземплярами, вы можете это сделать. Вот как вы можете отключить несколько экземпляров приложения в Windows 10.
Отключить несколько экземпляров приложения
Чтобы отключить несколько экземпляров приложения в Windows 10, вам необходимо установить бесплатное приложение под названием SingleInstance. Вперед и скачатьи запустите приложение.
Как найти и удалить теневые мониторы в Windows 10
В приложении по умолчанию предварительно настроено одно приложение, а именно приложение «Калькулятор» в Windows 10. Вы можете удалить его, если хотите, но оно является инструктивным в том смысле, что показывает, как добавить приложение.
Введите имя приложения, несколько экземпляров которого вы хотите отключить. Для таких приложений, как Блокнот, вы можете просто ввести Notepad.exe, но для других нестандартных приложений, таких как Chrome, вам нужно будет ввести полный путь к EXE.
Что касается времени отклика, вы можете оставить его равным 1, но 0 может быть лучшим вариантом. Время отклика — это время, необходимое приложению, чтобы определить, запущен ли второй экземпляр приложения, а затем закрыть его.
Как удалить Ubuntu и восстановить Windows на ПК
SingleInstance не отключает возможность приложения запускать несколько экземпляров. Это невозможно без изменения самого приложения. Что он делает, так это то, что если он обнаруживает более одного запущенного экземпляра приложения, он закрывает второй экземпляр.
Вообще говоря, пользователи предпочитают, чтобы приложения могли запускать несколько экземпляров, но иногда эта функция срабатывает. Если вы случайно откроете файл дважды, скорее всего, внесенные в него изменения будут перезаписаны или потеряны. В этом случае, вероятно, будет хорошей идеей вообще отключить запуск второго экземпляра.
Следует отметить, что в некоторых приложениях есть встроенная возможность отключения нескольких экземпляров. Плеер VLC умеет, но это довольно необычная функция. Тем не менее, вы должны проверить, есть ли в приложении, для которого вы хотите отключить несколько экземпляров, встроенную опцию. Если это так, используйте его вместо стороннего приложения. Встроенный вариант всегда будет работать лучше, чем внешнее решение.
Как автоматически настроить громкость наушников в Windows 10
Запускается много копий программы
После долгих, многомесячных мучений смог-таки исхитриться состряпать кросс-платформенный модуль, работающий и под Вин и под Лин.
(проблема с любыми предложениями типа «создать файлик/семафор/ещё какую хрень, и проверять по его наличию запущена ли программа» упираются в тот факт, что программа может и умереть, не успев подчистить за собой. И что тогда — больше не запустится?)
Вот. В виндовсе эксплуатируется тот факт, что система сама метит мьютекс как «бесхозный» если скончалась породившая его программа (и не важно, успела она перед смертью пискнуть, или нет). В Линуксе эксплуатируется тот факт, что для всех запущенных процессов в файловой системе имеются виртуальные папки «/proc/».
Вопрос знатокам: моя процедура создаёт маленький файлик, куда пишет PID. Дело это муторное и не надёжное, из-за того, что не ясно в какой папке его создавать. Там же, где лежит сама программа? А если это /bin/ или /usr/bin/ ? Точно ведь по рукам дадут. Так вот, вопрос: как в Линуксе создавать именованые мьютексы, или сходжие объекты, способные содержать число?
{$ifdef fpc}
{$mode delphi}
{$endif}
uses
SysUtils {$ifdef win32}, Windows{$else}, baseunix {$endif};
function ThisIsAnOnlyInstance: boolean;
var
M: THandle;
Di: boolean = false;
function ThisIsAnOnlyInstance: boolean;
var N: string;
begin
Result:=True;
N:=ChangeFileExt(ExtractFileName(ParamStr(0)),») + ‘SingleInstanceMutex’;
M:=OpenMutex(MUTEX_MODIFY_STATE, False, PChar(N));
if M = 0 then M:=CreateMutex(nil, True, PChar(N))
else begin
if WaitForSingleObject(M, 0) <> WAIT_ABANDONED
then Result:=False;
end;
Di:=Result;
end;
var
Fn: string;
Di: boolean = false;
function ThisIsAnOnlyInstance: boolean;
var
i: integer;
t: Text;
s: stat;
begin
Result:=True;
Fn:= ‘/tmp/.’ + ChangeFileExt(ExtractFileName(ParamStr(0)),») + ‘SingleInstanceMutex’;
Try
if FileExists(Fn) then begin
i:=0;
AssignFile(t, Fn);
Reset(t);
Readln(t, i);
CloseFile(t);
if i = fpGetPid() then Exit
else
if DirectoryExists(‘/proc/’ + IntToStr(i)) then begin
if not FileExists(‘/proc/’ + IntToStr(i) + ‘/exe’) //I’m unsure if
// any *nix creates one or it’s just my distribution
or ((ExtractFileName(fpReadLink (‘/proc/’ + IntToStr(i) + ‘/exe’)) = ExtractFileName(ParamStr(0)))
and not ((Length(ParamStr(0)) >=8)and(copy(ParamStr(0),1, 8) <> ‘/tmp/upx’)))
then begin
Exit(False);
end;
end;
end;
AssignFile(t, Fn);
Rewrite(t);
Writeln(t, fpGetPid());
CloseFile(t);
Except
End;
Di:=Result;
end;
{$endif}
initialization
finalization
Try
{$ifdef win32}
if Di then ReleaseMutex(M);
{$else}
if Di and FileExists(Fn) then DeleteFile(Fn);
{$endif}
Except End;
end.
Последний раз редактировалось Cheb 17.04.2007 15:53:25, всего редактировалось 1 раз.
- Профиль
- Сайт
файлик вроде как надо создавать /var/run/
там даже апач этим занимается =)
но там тоже права нужны без них никуда
так что проще в /tmp or /var/tmp
тем более у них есть свойство после перезагрузке очищаться .
- Профиль
- Сайт
- ICQ
/var/tmp при перезагрузке не очищается, этим он и отличается от /tmp. А на моей системе и /tmp не очищается, и фиг поймешь чего подкрутить надо.
Можно, наверное, держать файл с pid все время открытым с запретом совместной записи. Тогда при смерти программы он должен закрыться, а запись — разрешиться.
Первый раз вижу, чтобы виндовый мьютекс проверяли на WAIT_ABANDONED. Даже в случае внезапной смерти программы все принадлежащие ей дескрипторы (handle) будут закрыты. Мьютекс (и подобные ему объекты) будет удален при закрытии последнего своего handle, поэтому риск умереть, не подчистив, отсутствует.
А вот гораздо более интересная задача — при обнаружении предыдущего экземпляра передать ему свою командную строку.
- Профиль
- ICQ
так что проще в /tmp or /var/tmp
А ведь и действительно! Кто угодно может туда писать. Спасибо
Тоды это будет
Код: Выделить всё Fn:= ‘/tmp/.’ + ChangeFileExt(ExtractFileName(ParamStr(0)),») + ‘SingleInstanceMutex’;
Первый раз вижу, чтобы виндовый мьютекс проверяли на WAIT_ABANDONED.
Уже не помню точно, но, ЕМНИП, я тестировал её разными способами, в том числе закомментировав вызов ReleaseMutex(M), и роняя программу разными способами — и мьютекс часто преспокойно переживал смерть программы. Почему, собственно, его и приходится проверять на бесхозность. Вроде бы, не помню точно, он остаётся неубранным если зависшую программу убить через Диспетчер задач.
- Профиль
- Сайт
По моему надо проверять не факт наличия файла-флага — а факт залоченности этого файла.
при создани файла — указываеш метод создания — монопольно, и поседующие экземпляры (пока работае экземпляр создавший этот файл) пересоздать файл не могут — следовательно программа запущена.
Если при запуске файл был обнаружен, но его получилось удалить — значит превыдущий экземпляр был завершён аварийно — и можно запускть текущий. Я по этой методике давно работаю — универсально кросплатформенное решение.
- Профиль
- Сайт
- ICQ
О. Уже обкатать успел, и пару страшных багов убить. Причём, пошёл совершенно другим путём.
Кстати, угадайте почему в предыдущей линукс-весиии каждый второй запуск следующего экземпляра увенчивался успехом?
Последний раз редактировалось Cheb 17.04.2007 15:54:27, всего редактировалось 1 раз.
- Профиль
- Сайт
finalization можно сократить до
Код: Выделить всё finalization
Try
{$ifdef win32}
if Di then ReleaseMutex(M);
{$else}
if Di and FileExists(Fn) then DeleteFile(Fn);
{$endif}
Except End;
end.
целых 3 слова =)
вот пытаюсь отгадать твою загадку
Кстати, угадайте почему в предыдущей линукс-весиии каждый второй запуск следующего экземпляра увенчивался успехом? Embarassed
только относительно другой задачи. хочу запустить процесс и следить чтоб не пропал =)
if DirectoryExists(‘/proc/’ + IntToStr(i)) then begin
ну этим находим что процесс такой вроде есть
if not FileExists(‘/proc/’ + IntToStr(i) + ‘/exe’) //I’m unsure if
// any *nix creates one or it’s just my distribution
or (ExtractFileName(fpReadLink (‘/proc/’ + IntToStr(i) + ‘/exe’)) = ExtractFileName(ParamStr(0)))
вот этого не пойму
что есть вероятность того что процесса уже нет а папка осталась ?
то что ты сравниваешь имя бинарника пида это понятно, вдруг этот пид занял другой процесс. а вот про папку раскажи плиз
- Профиль
- Сайт
- ICQ
ОБНАРУЖЕН СТРАШНЫЙ БАГ
Если программу пожать UPX’ом то ничерта не работает, поскольку имя бинарника генерится на лету, случайным образом и никогда не совпадает.
Обновил первый пост, добавив
and not ((Length(ParamStr(0)) >=8)and(copy(ParamStr(0),1, 8 ) <> ‘/tmp/upx’)))
вот пытаюсь отгадать твою загадку
Потому что было if FileExists(Fn) then DeleteFile(Fn);
что есть вероятность того что процесса уже нет а папка осталась ?
Какая папка? Pid? Тогда думаю, нулевая.
о что ты сравниваешь имя бинарника пида это понятно, вдруг этот пид занял другой процесс. а вот про папку раскажи плиз
.
Что про папку? Где про папку?
Просто у меня, в качестве временной меры, /usr/bin/ — это симлинк, указывающий на реальный файл, в папке проекта, куда всё компилится. Можно запустить и так, и так, имя бинарника будет тем же, а путь будет указан разный.
целых 3 слова =)
Спасибо, учёл
- Профиль
- Сайт
Проблему запуска нескольких экземпляров программы можно решить с помощью семафора, изменяя его значение с установкой флага SEM_UNDO. В этом случае после завершения процесса система откатит изменения.
Источник: www.freepascal.ru