문제

내 프로그램의 한 인스턴스가 실행 중인지 어떻게 알 수 있나요?데이터 파일로 이 작업을 수행할 수 있다고 생각했지만 지저분할 뿐입니다.

한 지점에서 단 하나의 인스턴스만 열려 있기를 원하기 때문에 이 작업을 수행하고 싶습니다.

도움이 되었습니까?

해결책

세마포어를 생성하고 실행을 중지하고 (코드를 *.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;

응용 프로그램 인스턴스 수 제어 :

http://delphi.about.com/od/windowsshellapi/l/aa100703a.htm

이 장치를 참조하십시오 (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.

과거에는 소켓을 사용하여 여러 인스턴스가 동시에 실행되는 것을 방지했습니다. 소켓이 사용중인 경우 프로그램을 계속하지 마십시오. 사용 가능한 경우 모든 것이 정상으로 실행되도록하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top