プログラムの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;
[〜#〜] 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;
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
を呼び出します。
全能のJVCL には、この目的のためのコンポーネントがあります。 「TJvAppInstances」を参照してください。
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);
通常の解決策は、という名前のシステム全体ミューテックスを作成することです。
Delphiがわからないため、コードを提供していません。それでも役立つ場合は、C#コードを提供できます。
Rob Kennedyによる優れた回答 に1つのポイントを追加したいと思います(すべてをDPRファイルにコピーするのではなく、彼のコードから関数を作成するのが最善であるという事実は別として。あなたミューテックスの名前と、ミューテキストをユーザーごとにするかシステム全体にするかを決めるブール値の2つのパラメーターのみが必要です。
答えは、ミューテックスの命名をあまり考慮していません。プログラムがInnoSetup(およびおそらく他のセットアップツール)を介してインストールされることを期待する場合は、名前を慎重に選択する必要があります。ミューテックスを使用して、セットアッププログラムにアプリケーションが現在実行されているかどうかを確認させ、ユーザーに次のことを警告することができます。アプリケーションのすべてのインスタンスを閉じる必要があります。ユーザーごとにプログラムのインスタンスを1つ許可することを選択した場合、セットアップでアプリケーションのインスタンスを実行する必要がまったくないため、システム全体の2番目のミューテックスも作成する必要がありますファイルを置き換えることができるようにするため。 InnoSetupインストーラーとの同期に使用する名前はハードコーディングする必要があります。
私はあなたが採用できるいくつかの異なる戦略があると思います。しかし、最も簡単なもの(プラットフォーム固有ではない)は、あなた自身が提案したものです。つまり、プログラムの開始時に、特定の場所にロックファイルが作成されているかどうかを確認します。このロックファイルが存在する場合、別のインスタンスがすでに実行されています。存在しない場合、別のインスタンスは実行されていません。プログラムが終了したら、ロックファイルを削除します。
ただし、この戦略を採用すると、別の問題が発生します。プログラムがクラッシュした場合はどうなりますか?ロックファイルはまだ残っており、この特定のケースを処理する必要があります。
もう1つの戦略は、オペレーティングシステム内にプレゼンスを登録するシステム全体のミューテックスソリューションです(または、これが自動的に行われることも考えられます)。次に2番目のインスタンスが開始しようとすると、特定のIDでアクティブなプロセスがすでに存在するかどうかがチェックされます。すでに存在する場合、2番目のプロセスは開始しないことを選択し、オプションで最初のプロセスのウィンドウにフォーカスを移動します(問題のプロセスがそのウィンドウを所有している場合)。
ただし、この戦略はプラットフォーム固有であり、実装はプラットフォームごとに異なります。
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;
アプリケーションインスタンスの数の制御:
実行を停止アプリ複数回 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.
このユニットを参照してください(CreateMutexを使用): iApp
さらに、このページでは、さまざまな方法(mutex、FindWindowsなど)を使用したこの作業の長所と短所を確認できます。
このユニットには、これが検出されたときにアプリケーションの以前のインスタンスをアクティブ化するソリューションがあります。
よろしくお願いします-私の悪い英語のために私。
Neftalí-GermánEstévez-