プログラムの別のインスタンスが既に実行されているかどうかを確認するにはどうすればよいですか?
-
19-08-2019 - |
質問
プログラムの1つのインスタンスが実行されているかどうかを確認するにはどうすればよいですか? データファイルを使用してこれを行うことができると思ったが、それは単に面倒です:(
一度に1つのインスタンスのみを開くようにするため、これを実行します。
解決
セマフォを作成して実行を停止し(コードを* .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が最初に提案したように、ミューテックスを作成してみることができます。 CreateMutex
を呼び出します。 null以外のハンドルが返された場合は、 GetLastError
を呼び出します。 。あなたがミューテックスを作成したのか、それともミューテックスがすでに開いていたのか(Error_Already_Exists
)がわかります。ミューテックスの所有権を取得する必要はありません 。 mutexは相互排除に使用されていません。名前付きカーネルオブジェクトであるため、使用されています。イベントまたはセマフォも機能します。
ミューテックス技術により、ブール型の答えが得られます。はい、別のインスタンスが存在するか、または存在しません。
あなたは頻繁にそれ以上のことを知りたいと思うでしょう。たとえば、他のインスタンスのメインウィンドウのハンドルを知り、他のインスタンスの代わりにフォアグラウンドに来るように指示することができます。これは、メモリマップドファイルが役立つ場所です。最初のインスタンスに関する情報を保持できるため、後のインスタンスはそれを参照できます。
ミューテックスの名前を選択するときは注意してください。ドキュメントを注意深く読んで、一部の文字(バックスラッシュなど)は一部のOSバージョンでは許可されていませんが、他のOSバージョンの特定の機能には必要であることに注意してください。
他のユーザーの問題も覚えておいてください。プログラムをリモートデスクトップまたはユーザーの簡易切り替えで実行できる場合は、他のユーザーが既にプログラムを実行している可能性があり、現在のユーザーによるプログラムの実行を制限したくない場合があります。その場合、グローバル名を使用しないでください。すべてのユーザーのアクセスを制限する 場合は、ミューテックスオブジェクトのセキュリティ属性が、誰でもハンドルを開くことができるようなものであることを確認してください。 lpSecurityAttributes
パラメーターにNULLポインターを使用するだけでは不十分です。 <!> quot;デフォルトのセキュリティ記述子<!> quot; 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
関数を実装することを選択したとします。あなたが空想を得たいなら、あなたは前のインスタンスがすでにシャットダウンしていて、新しいデータを受け入れることができないというケースを説明できます。その場合、2番目のインスタンスを閉じたくありません。最初のインスタンスは、シャットダウンしていることがわかるとすぐにミューテックスハンドルを閉じ、実際には<!> quot; lame duck <!> quot;になります。インスタンス。 2番目のインスタンスは、mutexハンドルの作成を試み、成功し、それ自体を実際の最初のインスタンスと見なします。前のインスタンスは中断せずに閉じます。ミューテックスを閉じるには、CloseHandle
を使用します。メインフォームのOnClose
イベントハンドラーから呼び出すか、たとえばApplication.Terminate
を呼び出す他の場所で呼び出します。
強力なJVCL には、この目的のためのコンポーネントがあります。 <!> quot; TJvAppInstances <!> quot;を参照してください。
システム mutex を作成します。
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);
通常の解決策は、名前付きのシステム全体 mutex を作成することです。
- 作成することができれば、あなたが実行中のアプリケーションになります。
- そうしない場合、別のものがあることを知っています。
編集:
Delphiを知らないため、コードを提供していません。ただし、それが役立つ場合は、C#コードを提供できます。
Rob Kennedyによる優れた回答(DPRファイルにすべてをコピーするのではなく、彼のコードから関数を作成するのが最善であるという事実は別として、必要なパラメーターは2つだけです。 mutexの名前、およびmutextがユーザーごとかシステム全体かを示すブール値。)
この答えでは、ミューテックスの命名についてはあまり考慮されていません。 Inno Setup(およびおそらく他のセットアップツール)を使用してプログラムをインストールする場合、mutexを使用してセットアッププログラムにアプリケーションが現在実行されているかどうかを確認させ、アプリケーションのすべてのインスタンスを閉じる必要があります。ユーザーごとにプログラムの1つのインスタンスを許可することを選択した場合、セットアップはアプリケーションの実行インスタンスをまったく持つ必要がないため、2番目のシステム全体のミューテックスも作成する必要があります。ファイルを置き換えることができます。 InnoSetupインストーラーとの同期に使用される名前は、ハードコーディングする必要があります。
採用できる戦略はいくつかあると思います。しかし、最も簡単なのは(プラットフォーム固有ではない)あなた自身が提案したもの、つまり、プログラムの開始時に特定の場所にロックファイルが作成されているかどうかを確認することです。このロックファイルが存在する場合、別のインスタンスが既に実行されています。存在しない場合、実行中の別のインスタンスはありません。プログラムが終了したら、ロックファイルを削除します。
ただし、この戦略を採用すると、別の問題が発生します。プログラムがクラッシュするとどうなりますか?ロックファイルはまだ残っており、この特定のケースを処理する必要があります。
もう1つの戦略は、システム全体のミューテックスソリューションです。ここでは、オペレーティングシステム内に自分の存在を登録します(または、これが自動的に行われることもあります)。次に、2番目のインスタンスが起動を試みると、特定のIDを持つアクティブなプロセスが既に存在するかどうかを確認します。既に存在する場合、2番目のプロセスは開始しないことを選択し、オプションで最初のプロセスのウィンドウにフォーカスを移動します(問題のプロセスがウィンドウを所有している場合)。
ただし、この戦略はプラットフォーム固有であり、実装はプラットフォームごとに異なります。
FindWindow windows api関数を使用できます。デルファイでは、ウィンドウのクラス名はクラス名と同じですが、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;
アプリケーションインスタンスの数の制御:
このユニットを見る(CreateMutexを使用): UiApp
このページでは、さまざまな方法(mutex、FindWindowsなど)を使用して、この作業の利点と欠点を読むことができます。
このユニットには、これが検出されたときにアプリケーションのpreviosインスタンスをアクティブにするソリューションがあります。
よろしくお願いします。私の英語が下手です。
Neftal <!>#237; -Germ <!>#225; n Est <!>#233; vez-
実行を停止したい場合は、同じ時間にアプリを複数回(コードを *。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.
過去には、複数のインスタンスが同時に実行されるのを防ぐためにソケットを使用しました。ソケットが使用中の場合は、プログラムを続行しないでください。使用可能な場合は、すべてが正常に実行されるようにします。