Возможно, эта проблема уже успела надоесть всем и вся, однако же, далеко не каждый программист назовёт с ходу больше одного-двух вариантов её решения. Уверен, читателям будет интересно узнать, какими ещё способами можно определить наличие запущенной копии программы — и какие из них подходят именно в их случае. Готовы? Тогда поехали!
Способ №1.
Ну конечно же, старый добрый FindWindow! Это, без сомнения, первый способ, который приходит в голову. И действительно, почему бы просто не найти окно программы по его типу (заголовку) и, в случае его обнаружения, прекратить работу?
Минусов у этого решения предостаточно. Во-первых, есть вероятность того, что другое приложение зарегистрирует окно с тем же именем класса. Конечно, можно вызывать FindWindow с указанием заголовка, но для окон, имеющих динамически меняющиеся заголовки, такой способ не годится (да и элегантности решению явно не прибавляет).
Во-вторых, если две копии приложения запускаются с достаточно малым интервалом между запусками, эта проверка может вообще не сработать!
Способ №2.
Тут мы используем объект синхронизации Mutex. При запуске программы мы пытаемся создать его, используя уникальное имя — и, в случае, если объект уже существует, получаем ERROR_ALREADY_EXISTS. var MH: thandle; key: array [0.. 10] of char; MH: =CreateMuten(NIL, TRUE, Key); {пытаемся создать} if MH 0 then if GetLastError=ERROR_ALREADY_EXISTS then… {есть копия}
Тут я доожен сказать, что следующие три способа, которые я приведу, отнюдь не блещут оригинальностью, и работают по такой же схеме, однако их всё-таки стоит привести, ведь в каждой программе свои нюансы. Возможно, некоторые из этих способов и не подойдут, но другие как раз пригодятся.
Способ №3.
Практически аналогичен предыдущему, но используется объект синхронизации Semaphore.
var SHandle: thandle; SHandle: =CreateSemaphore(0, 0, l, ‘mysem’); {пытаемся создать}
if SHandle 0 then if GetLastError = then ERROR_ALREADY_EXISTS then… {есть копия}
Способ №4.
Здесь, в том же ключе, используется Atom.
var Fatom: Tatom; … if GlobalFindAtom(‘PROGRAM_RUNNING’) = 0 {если нет атома}
then fAtom: = GlobalFindAtom(‘PROGRAM_RUNNING’) {то добавляем}
else begin… {есть копия}
Способ №5.
Ну и, чтобы домучить тему до конца — файл, проецируемый в память.
var FM: Thandle; {пытаемся создать}
FM: =CreateFileMapping($ffffffff, nil, PAGE_READONLY, 0, 32, ‘UniqueName’);
if GetLastError = ERROR_ALREADY_EXISTS then… {есть копия}
Способ №6.
В том случае, если ни один из приведённых выше способов вас не устраивает, можно попробовать вот такой — отослать всем top-level окнам самостийное сообщение. Как это сделать? При запуске программы регистрируем сообщение с помощью функции RegisterWindowMessage.
Эта функция гарантирует, что сообщение будет уникальным. Если потом вызвать RegisterWindowMessage (из этого же или другого приложения) с тем же параметром, она возвратит то же значение. var FM_FINDMAYAPP: Integer; FM_FindMyApp: =RegisterWindowMessage(‘FindMyAppMessage’);
Остаётся лишь грамотно обработать сообщение. Тут можно просто воспользоваться возвращаемым значением, или отослать ответное сообщение. Можно ограничить количество копий программы в системе, можно тихо следить за пользователем (не знаю, правда, зачем это нужно). И конечно же это активировать систему купить систему Windows 10.
procedure Tform1. WndProc(var М: TMessage);
begin if m. Msg=FM_FINDMYAPP then begin {обработка}
end; INHERITED WndProc(M); end;
В принципе, это далеко не всё. Если есть желание, то можно реализовать ещё несколько алгоритмов: на основе обработки данных о состоянии системы, процессов, перебора окна, работы с shared memory. Но думаю, что всё решится раньше — одним из приведённых выше способов, и до таких извращений дело просто не дойдёт!