Как впервые предположил Джон, вы можете попробовать создать мьютекс. Звоните CreateMutex
. Если вы получите ненулевой дескриптор, позвоните GetLastError
. Он сообщит вам, был ли вы тем, кто создал мьютекс, или мьютекс был открыт ранее (Error_Already_Exists
). Обратите внимание, что не необходимо для приобретения права собственности на мьютекс. Мьютекс не используется для взаимного исключения. Он используется, потому что это именованный объект ядра. Событие или семафор тоже могут сработать.
Техника мьютекса дает логический ответ: да, есть другой экземпляр или нет, его нет.
Вы часто хотите знать больше, чем просто это. Например, вы можете захотеть узнать дескриптор главного окна другого экземпляра, чтобы вы могли указать, чтобы он вышел на передний план вместо вашего другого экземпляра. Вот где файл с отображением в памяти может пригодиться; он может содержать информацию о первом экземпляре, чтобы более поздние экземпляры могли ссылаться на него.
Будьте внимательны при выборе названия мьютекса. Внимательно прочитайте документацию и помните, что некоторые символы (например, обратная косая черта) недопустимы в некоторых версиях ОС, но необходимы для определенных функций в других версиях ОС.
Также помните о проблеме других пользователей. Если ваша программа может быть запущена через удаленный рабочий стол или с помощью быстрого переключения пользователей, то могут быть другие пользователи, уже запускающие вашу программу, и вы, возможно, не захотите ограничивать текущий запуск программы вашим пользователем. В этом случае не используйте глобальное имя. Если вы do хотите ограничить доступ для всех пользователей, убедитесь, что атрибуты безопасности объекта мьютекса таковы, что каждый сможет открыть для него дескриптор. Использование нулевого указателя для параметра lpSecurityAttributes
недостаточно для этого; «дескриптор безопасности по умолчанию», который упоминается в MSDN, предоставляет полный доступ текущему пользователю и не дает доступа другим.
Вам разрешено редактировать файл DPR вашей программы. Обычно это хорошее место для подобных вещей. Если вы подождете до события OnCreate
одной из ваших форм, то ваша программа уже имеет некоторый импульс для нормального запуска, поэтому неуместно пытаться завершить программу в этот момент. Лучше прекратить работу, пока не было выполнено слишком много работы с пользовательским интерфейсом. Например:
var
mutex: THandle;
mutexName: string;
begin
mutexName := ConstructMutexName();
mutex := CreateMutex(nil, False, PChar(mutexName));
if mutex = 0 then
RaiseLastOSError; // Couldn't open handle at all.
if GetLastError = Error_Already_Exists then begin
// We are not the first instance.
SendDataToPreviousInstance(...);
exit;
end;
// We are the first instance.
// Do NOT close the mutex handle here. It must
// remain open for the duration of your program,
// or else later instances won't be able to
// detect this instance.
Application.Initialize;
Application.CreateForm(...);
Application.Run;
end.
Есть вопрос о том, когда закрывать дескриптор мьютекса. Вам не нужно закрывать это. Когда ваш процесс окончательно завершается (даже если происходит сбой), ОС автоматически закрывает все оставшиеся дескрипторы, а когда больше нет открытых дескрипторов, объект мьютекса будет уничтожен (что позволит запустить другой экземпляр вашей программы и считать себя быть первым экземпляром).
Но вы все равно можете закрыть ручку. Предположим, вы решили реализовать функцию SendDataToPreviousInstance
, о которой я упоминал в коде. Если вы хотите стать модным, то вы можете учесть, что предыдущий экземпляр уже закрывается и не может принять новые данные. Тогда вам не захочется закрывать второй экземпляр. Первый экземпляр может закрыть дескриптор мьютекса, как только узнает, что завершает работу, и фактически становится экземпляром «хромой утки». Второй экземпляр попытается создать дескриптор мьютекса, преуспеть и считать себя настоящим первым экземпляром. Предыдущий экземпляр будет закрыт непрерывно. Используйте CloseHandle
, чтобы закрыть мьютекс; вызывайте его из обработчика событий OnClose
главной формы или из любого другого места, например, Application.Terminate
.