내 프로그램의 다른 인스턴스가 이미 실행 중인지 어떻게 알 수 있나요?
-
19-08-2019 - |
문제
내 프로그램의 한 인스턴스가 실행 중인지 어떻게 알 수 있나요?데이터 파일로 이 작업을 수행할 수 있다고 생각했지만 지저분할 뿐입니다.
한 지점에서 단 하나의 인스턴스만 열려 있기를 원하기 때문에 이 작업을 수행하고 싶습니다.
해결책
세마포어를 생성하고 실행을 중지하고 (코드를 *.dpr 파일에 넣음) 화면으로 실행할 수 있습니다.
var
Semafor: THandle;
begin
{ Don't start twice ... if already running bring this instance to front }
Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
if ((Semafor <> 0) and { application is already running }
(GetLastError = ERROR_ALREADY_EXISTS)) then
begin
RestoreWindow('TMyApplication');
CloseHandle(Semafor);
Halt;
end;
Application.CreateForm(....);
Application.Initialize;
Application.Run;
CloseHandle(Semafor);
end;
편집하다 (추가 RestoreWindow
방법):
그만큼 aFormName
응용 프로그램의 기본 양식 클래스의 이름입니다.
procedure RestoreWindow(aFormName: string);
var
Wnd,
App: HWND;
begin
Wnd := FindWindow(PChar(aFormName), nil);
if (Wnd <> 0) then
begin { Set Window to foreground }
App := GetWindowLong(Wnd, GWL_HWNDPARENT);
if IsIconic(App) then
ShowWindow(App, SW_RESTORE);
SetForegroundwindow(App);
end;
end;
다른 팁
Jon이 처음 제안한 것처럼 Mutex를 만들 수 있습니다. 부르다 CreateMutex
. 널 핸들이 아닌 핸들이되면 GetLastError
. 그것은 당신이 뮤텍스를 만든 사람인지 또는 뮤텍스가 이미 전에 열려 있는지 여부를 알려줄 것입니다.Error_Already_Exists
). 그것은 그렇습니다 ~ 아니다 뮤텍스의 소유권을 얻는 데 필요합니다. Mutex는 상호 배제에 사용되지 않습니다. 이름이 지정된 커널 객체이기 때문에 사용되고 있습니다. 이벤트 나 세마포도 효과가있을 수 있습니다.
Mutex 기술은 부울 대답을 제공합니다. 예, 다른 사례가 있거나 아니오, 그렇지 않습니다.
당신은 종종 그 이상을 알고 싶어합니다. 예를 들어, 다른 인스턴스의 기본 창의 핸들을 알고 싶어서 다른 인스턴스 대신 전경에 올 수 있다고 말할 수 있습니다. 그곳에서 메모리 매핑 된 파일이 유용 할 수있는 곳입니다. 첫 번째 인스턴스에 대한 정보를 보유 할 수 있으므로 나중에 인스턴스가 참조 할 수 있습니다.
뮤텍스의 이름을 선택할 때주의하십시오. 문서를주의 깊게 읽고 일부 문자 (예 : 백 슬래시)는 일부 OS 버전에서는 허용되지 않지만 다른 OS 버전의 특정 기능에는 필요합니다.
또한 다른 사용자의 문제를 기억하십시오. 원격 데스크탑 또는 빠른 사용자 전환을 통해 프로그램을 실행할 수 있다면 이미 프로그램을 실행하는 다른 사용자가있을 수 있으며 현재 사용자가 프로그램을 실행하는 것을 제한하지 않을 수 있습니다. 이 경우 글로벌 이름을 사용하지 마십시오. 만약 너라면 하다 모든 사용자의 액세스를 제한하고 MUTEX 객체의 보안 속성이 모든 사람이 핸들을 열 수 있도록하는지 확인하십시오. 에 널 포인터를 사용합니다 lpSecurityAttributes
파라미터는 충분하지 않습니다. MSDN이 언급 한 "기본 보안 디스크립터"는 현재 사용자에게 전체 액세스 권한을 부여하고 다른 사람에게 액세스 할 수 없습니다.
프로그램의 DPR 파일을 편집 할 수 있습니다. 그것은 일반적으로 이런 종류의 일을하기에 좋은 곳입니다. 당신이 기다릴 때까지 OnCreate
양식 중 하나 인 이벤트 인 경우 귀하의 프로그램은 이미 정상적으로 실행하는 데 약간의 추진력이 있으므로 해당 시점에서 프로그램을 종료하려고 시도하는 것은 서투른 것입니다. 너무 많은 UI 작업이 완료되기 전에 종료하는 것이 좋습니다. 예를 들어:
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.
뮤텍스 핸들을 언제 닫을 지에 대한 의문이 있습니다. 당신은 그것을 닫을 필요가 없습니다. 프로세스가 마지막으로 종료되면 (충돌이 발생하더라도) OS는 미결제 핸들을 자동으로 닫고 더 이상 핸들이 열리지 않으면 MUTEX 객체가 파괴됩니다 (따라서 프로그램의 다른 인스턴스가 시작하고 고려할 수 있습니다. 첫 번째 사례).
그러나 어쨌든 손잡이를 닫고 싶을 수도 있습니다. 당신이 그것을 구현하기로 선택했다고 가정 해 봅시다 SendDataToPreviousInstance
코드에서 언급 한 기능. 공상을 원한다면 이전 인스턴스가 이미 종료되고 새로운 데이터를 수락 할 수 없다는 경우를 설명 할 수 있습니다. 그런 다음 두 번째 인스턴스를 닫고 싶지 않을 것입니다. 첫 번째 인스턴스는 끊임없이 끊임없이 끊임없는 핸들을 닫을 수 있으며, 사실상 "절름발이 오리"인스턴스가됩니다. 두 번째 인스턴스는 뮤텍스 핸들을 만들고 성공하며 실제 첫 번째 인스턴스를 고려하려고합니다. 이전 인스턴스는 중단없이 닫습니다. 사용 CloseHandle
뮤텍스를 닫으려면; 주요 양식에서 호출하십시오 OnClose
이벤트 핸들러 또는 다른 곳마다 Application.Terminate
, 예를 들어.
모든 미성년자 JVCL 이 목적을위한 구성 요소가 있습니다. "tjvappinstances"를 참조하십시오.
당신은 a를 만듭니다 체계 뮤텍스.
델파이 코드는 없지만 여기에 C ++ 코드가 있습니다.
HANDLE Mutex;
const char MutexName[] = "MyUniqueProgramName";
Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);
if (Mutex)
throw Exception("Program is already running.");
else
Mutex = CreateMutex(NULL, true, MutexName);
정상적인 솔루션은 a를 만드는 것입니다 시스템 전체에 이름이 지정되었습니다 뮤텍스.
- 당신이 그것을 만들 수 있다면, 당신은 실행중인 응용 프로그램입니다.
- 그렇지 않으면 다른 것이 있다는 것을 알고 있습니다.
편집하다:
델파이를 모르기 때문에 코드를 제공하지 않았습니다. 도움이 될 경우 C# 코드를 제공 할 수 있습니다.
나는 하나의 점을 추가하고 싶다 Rob Kennedy의 훌륭한 답변 (모든 것을 DPR 파일에 복사하는 대신 코드에서 기능을 만드는 것이 가장 좋다는 사실 외에도 MUTEXT가 사용자 당 사용자 가어야하는지 여부에 관계없이 두 개의 매개 변수, MUTEX 이름 및 부울 만 있으면됩니다. 또는 시스템 전체).
그 대답은 뮤 테스의 이름 지정을 많이 고려하지 않습니다. 이노 설정을 통해 프로그램을 설치할 것으로 예상되는 경우 (및 기타 설정 도구도 마찬가지) MUTEX를 사용하여 애플리케이션이 현재 실행 중인지 확인하고 사용자에게 알려주는 데 신중하게 이름을 선택해야합니다. 응용 프로그램의 모든 인스턴스를 닫아야합니다. 사용자 당 프로그램의 한 인스턴스를 허용하기로 선택한 경우, 설정에 응용 프로그램의 실행중인 인스턴스가 없어야 할 수 있으므로 두 번째 시스템 전체의 MUTEX를 만들어야 할 수도 있습니다. 조금도 파일을 교체 할 수 있습니다. Innosetup 설치 프로그램과 동기화에 사용되는 이름은 하드 코딩해야합니다.
나는 당신이 사용할 수 있는 몇 가지 다른 전략이 있다고 말하고 싶습니다.그러나 가장 쉬운 방법(플랫폼에 국한되지 않음)은 여러분이 직접 제안한 것입니다. 즉, 프로그램 시작 시 특정 위치에 잠금 파일이 생성되었는지 확인하는 것입니다.이 잠금 파일이 존재하면 다른 인스턴스가 이미 실행 중인 것이고, 존재하지 않으면 다른 인스턴스가 실행되고 있지 않은 것입니다.프로그램이 종료되면 잠금 파일을 삭제합니다.
그러나 이 전략을 사용하면 또 다른 문제가 발생합니다. 프로그램이 충돌하면 어떻게 될까요?잠금 파일은 여전히 남아 있으므로 이 특정 사례를 처리해야 합니다.
또 다른 전략은 운영 체제 내에서 자신의 존재를 등록하는 시스템 차원의 뮤텍스 솔루션입니다(또는 이 작업이 자동으로 수행되는 것도 타당합니다).그런 다음 두 번째 인스턴스가 시작을 시도하면 특정 ID를 사용하여 이미 활성화된 프로세스가 있는지 확인합니다.이미 존재하는 경우 두 번째 프로세스는 시작하지 않도록 선택하고 선택적으로 첫 번째 프로세스의 창에 초점을 맞춥니다(해당 프로세스가 해당 창을 소유한 경우).
그러나 이 전략은 플랫폼마다 다르며 구현은 플랫폼마다 다릅니다.
FindWindow Windows API 기능을 사용하면 간단히 사용할 수 있습니다. 창의 델파이 클래스 이름은 클래스 이름과 동일하므로 CreateParams 함수를 재정의하여 클래스 이름을 재정의 할 수 있습니다. 창이 존재하는지 확인하려면 기본 창이 생성되기 전에 코드를 추가하십시오.
Program test
var
handle :HWND;
begin
handle := FindWindow('TMySuperApp', nil);
if IsWindow(handle) then
begin
//app is running
exit;
end.
Application.Initialize;
Application.CreateForm(TMySuperApp, SuperApp);
Application.Run;
end;
응용 프로그램 인스턴스 수 제어 :
이 장치를 참조하십시오 (Createmutex 사용) : UIAPP
또한이 페이지 에서이 작업에 대한 장단점을 읽을 수 있습니다 (Mutex, FindWindows, ...).
이 장치에는 응용 프로그램의 Previos 인스턴스를 감지 할 때 활성화하는 솔루션이 있습니다.
내 나쁜 영어에 대한 안부와 변명.
Neftalí -Germán Estévez-
당신이 원한다면 실행을 중지하십시오 당신의 앱 더 이상 한 번 안에 동시 (코드를 넣습니다 *.dpr 파일 프로젝트의). 두 번째 앱이 실행되면 메시지가 표시되고 즉시 중지됩니다.
Forms,
Unit1 in 'Unit1.pas' {Form1},
// add this units ....
TlHelp32,SysUtils,Windows,Dialogs;
{$R *.res}
function ProcessCount(const ExeName: String): Integer;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
Result:= 0;
while Integer(ContinueLoop) <> 0 do begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeName))) then Inc(Result);
ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
begin
if ProcessCount(ExtractFileName(Application.ExeName)) > 1 then begin
MessageDlg('Application is already running!', mtError, [mbOK], 0);
Application.Terminate;
end else begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
과거에는 소켓을 사용하여 여러 인스턴스가 동시에 실행되는 것을 방지했습니다. 소켓이 사용중인 경우 프로그램을 계속하지 마십시오. 사용 가능한 경우 모든 것이 정상으로 실행되도록하십시오.