32

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

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

4

12 に答える 12

39

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

ミューテックス手法では、ブール値の答えが得られます。はい、別のインスタンスがあるか、いいえ、ありません。

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

ミューテックスの名前を選択するときは注意してください。ドキュメントを注意深く読み、一部の文字 (バックスラッシュなど) は一部の 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 は未処理のハンドルを自動的に閉じます。開いているハンドルがなくなると、ミューテックス オブジェクトが破棄されます (したがって、プログラムの別のインスタンスが開始され、それ自体を考慮できるようになります)。最初のインスタンスになります)。

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

于 2009-01-19T23:20:26.500 に答える
18

セマフォを作成して実行を停止し (コードを *.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;
于 2009-01-20T08:39:31.520 に答える
17

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

于 2009-01-19T23:18:01.763 に答える
5

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

  • なんとか作成できれば、実行中のアプリケーションはあなただけです。
  • そうでない場合は、別のものがあることがわかります。

編集:

私は Delphi を知らないので、コードを提供していません。それが役立つ場合は、C#コードを提供できます。

于 2009-01-19T23:11:19.497 に答える
5

システム ミューテックスを作成します。

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);
于 2009-01-19T23:15:04.093 に答える
2

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

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

于 2009-01-20T08:02:34.573 に答える
1

FindWindow Windows API 関数を使用するだけです。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;
于 2009-01-19T23:52:16.310 に答える
1

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

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

于 2012-02-23T12:11:11.983 に答える
1

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

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

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

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

于 2009-01-19T23:28:38.357 に答える
0

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

さらに、このページでは、さまざまな方法 (mutex、FindWindows など) を使用したこの作業の利点と欠点を読むことができます。

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

よろしくお願いします。私の英語が下手で申し訳ありません。


ネフタリ -ゲルマン・エステベス-

于 2009-01-20T15:13:02.723 に答える
-1

以前は、複数のインスタンスが同時に実行されるのを防ぐためにソケットを使用していました。ソケットが使用中の場合は、プログラムを続行しないでください。使用可能な場合は、すべてを通常どおり実行します。

于 2009-01-19T23:37:55.887 に答える