プログラムの1つのインスタンスが実行されているかどうかを確認するにはどうすればよいですか?私はデータファイルでこれを行うことができると思いましたが、それは面倒です:(
一度に1つのインスタンスのみを開いてほしいので、これを実行したいと思います。
プログラムの1つのインスタンスが実行されているかどうかを確認するにはどうすればよいですか?私はデータファイルでこれを行うことができると思いましたが、それは面倒です:(
一度に1つのインスタンスのみを開いてほしいので、これを実行したいと思います。
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
。
セマフォを作成して実行を停止し (コードを *.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;
強力な JVCLには、この目的のためのコンポーネントがあります。「TJvAppInstances」を参照してください。
通常の解決策は、名前付きのシステム全体の ミューテックスを作成することです。
私は Delphi を知らないので、コードを提供していません。それが役立つ場合は、C#コードを提供できます。
システム ミューテックスを作成します。
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);
Rob Kennedy による優れた回答に 1 つのポイントを追加したいと思います (すべてを DPR ファイルにコピーするのではなく、彼のコードから関数を作成するのが最善であるという事実は別として。必要なパラメーターは 2 つだけです。ミューテックスの、およびミューテックスがユーザーごとかシステム全体かのブール値)。
答えは、ミューテックスの命名をあまり考慮していません。プログラムが Inno Setup (およびおそらく他のセットアップ ツールも) を介してインストールされることが予想される場合は、慎重に名前を選択する必要があります。ミューテックスを使用して、アプリケーションが現在実行されているかどうかをセットアップ プログラムに確認させ、ユーザーに警告することができるからです。アプリケーションのすべてのインスタンスを閉じる必要があります。ユーザーごとにプログラムの 1 つのインスタンスを許可することを選択した場合は、システム全体で 2 番目のミューテックスも作成する必要がある場合があります。これは、ファイルを置換できるようにするために、セットアップでアプリケーションの実行中のインスタンスがまったく必要ない場合があるためです。InnoSetup インストーラーとの同期に使用する名前は、ハードコーディングする必要があります。
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;
アプリケーション インスタンスの数の制御:
採用できる戦略はいくつかあると思います。しかし、最も簡単なもの (プラットフォーム固有ではない) は、あなた自身が提案したものです。つまり、プログラムの開始時に、特定の場所にロック ファイルが作成されているかどうかを確認します。このロック ファイルが存在する場合は、別のインスタンスが既に実行されています。存在しない場合は、別のインスタンスが実行されていません。プログラムが終了したら、ロック ファイルを削除します。
ただし、この戦略を採用すると、別の問題があります。プログラムがクラッシュした場合はどうなるでしょうか? ロック ファイルはまだ残っており、この特定のケースを処理する必要があります。
もう 1 つの戦略は、オペレーティング システム内に自分の存在を登録するシステム全体のミューテックス ソリューションです (または、これが自動的に行われる可能性もあります)。2 番目のインスタンスが起動しようとすると、特定の ID を持つアクティブなプロセスが既に存在するかどうかがチェックされます。すでに存在する場合、2 番目のプロセスは開始しないことを選択し、オプションで最初のプロセスのウィンドウをフォーカスします (問題のプロセスがウィンドウを所有している場合)。
ただし、この戦略はプラットフォーム固有であり、実装はプラットフォームごとに異なります。
このユニットを参照してください (CreateMutex を使用): UiApp
さらに、このページでは、さまざまな方法 (mutex、FindWindows など) を使用したこの作業の利点と欠点を読むことができます。
このユニットには、これが検出されたときにアプリケーションの以前のインスタンスをアクティブにするソリューションがあります。
よろしくお願いします。私の英語が下手で申し訳ありません。
ネフタリ -ゲルマン・エステベス-
以前は、複数のインスタンスが同時に実行されるのを防ぐためにソケットを使用していました。ソケットが使用中の場合は、プログラムを続行しないでください。使用可能な場合は、すべてを通常どおり実行します。