web-dev-qa-db-ja.com

プログラムの別のインスタンスがすでに実行されているかどうかを確認するにはどうすればよいですか?

プログラムの1つのインスタンスが実行されているかどうかを確認するにはどうすればよいですか?私はデータファイルでこれを行うことができると思いましたが、それは面倒です:(

一度に1つのインスタンスのみを開いてほしいので、これを実行したいと思います。

29
Arthur

セマフォを作成して実行を停止し(コードを* .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;

[〜#〜] edit [〜#〜]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;
18
Drejc

Jonが最初に提案したように、ミューテックスを作成してみることができます。 CreateMutex を呼び出します。 null以外のハンドルが返される場合は、 GetLastError を呼び出します。ミューテックスを作成したのがあなたであるかどうか、またはミューテックスが以前にすでに開いていたかどうかがわかります(Error_Already_Exists)。ミューテックスの所有権を取得する必要はないことに注意してください。ミューテックスは相互排除に使用されていません。名前付きカーネルオブジェクトであるため、使用されています。イベントやセマフォも機能する可能性があります。

ミューテックス手法はブール値の答えを提供します:はい、別のインスタンスがあります、またはいいえ、ありません。

あなたはしばしばそれ以上のものを知りたいと思うでしょう。たとえば、他のインスタンスのメインウィンドウのハンドルを知りたい場合は、他のインスタンスの代わりにフォアグラウンドに来るように指示できます。そこで、メモリマップトファイルが役に立ちます。最初のインスタンスに関する情報を保持できるため、後のインスタンスがそれを参照できます。

ミューテックスの名前を選択するときは注意してください。ドキュメントを注意深く読み、一部の文字(バックスラッシュなど)は一部のOSバージョンでは許可されていませんが、他のOSバージョンの特定の機能には必要であることに注意してください。

他のユーザーの問題も覚えておいてください。プログラムをリモートデスクトップまたは高速ユーザー切り替えで実行できる場合は、他のユーザーが既にプログラムを実行している可能性があり、現在のユーザーによるプログラムの実行を制限したくない場合があります。その場合、グローバル名は使用しないでください。 doすべてのユーザーのアクセスを制限したい場合は、ミューテックスオブジェクトのセキュリティ属性が、誰もがハンドルを開くことができるようになっていることを確認してください。 lpSecurityAttributesパラメーターにnullポインターを使用するだけでは不十分です。 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は未処理のハンドルを自動的に閉じ、開いているハンドルがなくなると、ミューテックスオブジェクトが破棄されます(したがって、プログラムの別のインスタンスを開始して、最初のインスタンスになります)。

しかし、とにかくハンドルを閉じたいと思うかもしれません。コードで説明したSendDataToPreviousInstance関数を実装することを選択したとします。気を付けたい場合は、前のインスタンスがすでにシャットダウンしていて、新しいデータを受け入れることができない場合を説明できます。次に、2番目のインスタンスを実際に閉じたくないでしょう。最初のインスタンスは、シャットダウンすることを認識するとすぐにミューテックスハンドルを閉じることができ、事実上「レームダック」インスタンスになります。 2番目のインスタンスは、ミューテックスハンドルを作成して成功し、それ自体を実際の最初のインスタンスと見なそうとします。前のインスタンスは中断されずに閉じます。 CloseHandleを使用してミューテックスを閉じます。メインフォームのOnCloseイベントハンドラーから呼び出すか、他の場所でApplication.Terminateを呼び出します。

39
Rob Kennedy

全能のJVCL には、この目的のためのコンポーネントがあります。 「TJvAppInstances」を参照してください。

17
utku_karatas

systemmutexを作成します。

私はDelphiコードを持っていませんが、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);
6
Kluge

通常の解決策は、という名前のシステム全体ミューテックスを作成することです。

  • あなたがそれを作成することに成功した場合、あなたはアプリケーションを実行している人です。
  • そうでない場合は、別のものがあることを知っています。

編集:

Delphiがわからないため、コードを提供していません。それでも役立つ場合は、C#コードを提供できます。

5
Jon Skeet

Rob Kennedyによる優れた回答 に1つのポイントを追加したいと思います(すべてをDPRファイルにコピーするのではなく、彼のコードから関数を作成するのが最善であるという事実は別として。あなたミューテックスの名前と、ミューテキストをユーザーごとにするかシステム全体にするかを決めるブール値の2つのパラメーターのみが必要です。

答えは、ミューテックスの命名をあまり考慮していません。プログラムがInnoSetup(およびおそらく他のセットアップツール)を介してインストールされることを期待する場合は、名前を慎重に選択する必要があります。ミューテックスを使用して、セットアッププログラムにアプリケーションが現在実行されているかどうかを確認させ、ユーザーに次のことを警告することができます。アプリケーションのすべてのインスタンスを閉じる必要があります。ユーザーごとにプログラムのインスタンスを1つ許可することを選択した場合、セットアップでアプリケーションのインスタンスを実行する必要がまったくないため、システム全体の2番目のミューテックスも作成する必要がありますファイルを置き換えることができるようにするため。 InnoSetupインストーラーとの同期に使用する名前はハードコーディングする必要があります。

2
mghie

私はあなたが採用できるいくつかの異なる戦略があると思います。しかし、最も簡単なもの(プラットフォーム固有ではない)は、あなた自身が提案したものです。つまり、プログラムの開始時に、特定の場所にロックファイルが作成されているかどうかを確認します。このロックファイルが存在する場合、別のインスタンスがすでに実行されています。存在しない場合、別のインスタンスは実行されていません。プログラムが終了したら、ロックファイルを削除します。

ただし、この戦略を採用すると、別の問題が発生します。プログラムがクラッシュした場合はどうなりますか?ロックファイルはまだ残っており、この特定のケースを処理する必要があります。

もう1つの戦略は、オペレーティングシステム内にプレゼンスを登録するシステム全体のミューテックスソリューションです(または、これが自動的に行われることも考えられます)。次に2番目のインスタンスが開始しようとすると、特定のIDでアクティブなプロセスがすでに存在するかどうかがチェックされます。すでに存在する場合、2番目のプロセスは開始しないことを選択し、オプションで最初のプロセスのウィンドウにフォーカスを移動します(問題のプロセスがそのウィンドウを所有している場合)。

ただし、この戦略はプラットフォーム固有であり、実装はプラットフォームごとに異なります。

1
user54808

FindWindow WindowsAPI関数を使用するだけです。ウィンドウのdelphiクラス名はクラス名と同じですが、CreateParams関数をオーバーライドすることでクラス名を再定義できます。ウィンドウが存在するかどうかを確認するには、メインウィンドウが作成される前、Application.Initializeの前にコードを追加します。

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;
1
Edin Omeragic

アプリケーションインスタンスの数の制御:

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

1
g2mk

実行を停止アプリ複数回 in 同時に(コードを*。dprファイル)に入れますプロジェクトの)。 2番目のアプリが実行された後にメッセージが表示され、すぐに停止します。

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.
0
Hamo Redone

このユニットを参照してください(CreateMutexを使用): iApp

さらに、このページでは、さまざまな方法(mutex、FindWindowsなど)を使用したこの作業の長所と短所を確認できます。

このユニットには、これが検出されたときにアプリケーションの以前のインスタンスをアクティブ化するソリューションがあります。

よろしくお願いします-私の悪い英語のために私。


Neftalí-GermánEstévez-