antenn-a
company profile

MFC航海日記 五十七日目(プログラムの2重起動禁止)


さて、良く見かけると思うけども1個起動していると、2回目起動しようとすると怒られたり、1回目に起動した奴がアクティブになる奴を実装しようかなと。
今回作っている物も多重起動されると厄介なのだ。ファイルに同時アクセスに行ったり、フォルダ名が変わってたりするし。
ので、やってみた。

prev


これもさして難しい実装では無いっす。
まずはApp Classを開きます。今回の場合は PictureAlbum.cpp 普通はアプリ名.h, cpp ってなってると思います。
で、CPictureAlbumApp::InitInstance()内に書きます。
BOOL CPictureAlbumApp::InitInstance()
{
	// アプリケーション マニフェストが visual スタイルを有効にするために、
	// ComCtl32.dll バージョン 6 以降の使用を指定する場合は、
	// Windows XP に InitCommonControls() が必要です。さもなければ、ウィンドウ作成はすべて失敗します。
	InitCommonControls();

	CWinApp::InitInstance();

	/* 2重起動防止 */
	HANDLE hMutex;
	hMutex = CreateMutex( NULL, TRUE, "PictureAlbum by Antenn-a" );
	
	if ( !hMutex )
		return FALSE;
	
	if ( GetLastError() == ERROR_ALREADY_EXISTS ){
		HWND hPrevWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
		while ( hPrevWnd ){
			if( ::GetProp( hPrevWnd, "PictureAlbum by Antenn-a" ) ){
				// 同じウィンドウが見つかった場合はそちらにフォーカスを移す
				// 小さくなってたらデカくする
				if( IsIconic( hPrevWnd ) )
					ShowWindow( hPrevWnd, SW_RESTORE );
				// ここでフォアグラウンドにする
				SetForegroundWindow( ::GetLastActivePopup( hPrevWnd ) );
				return FALSE;
			}
			hPrevWnd = GetWindow( hPrevWnd, GW_HWNDNEXT );
		}	
		return FALSE;
	}

	// 標準初期化
	// これらの機能を使わずに、最終的な実行可能ファイルのサイズを縮小したい場合は、
	// 以下から、不要な初期化ルーチンを
	// 削除してください。
	// 設定が格納されているレジストリ キーを変更します。
	// TODO: この文字列を、会社名または組織名などの、
	// 適切な文字列に変更してください。
	SetRegistryKey(_T("アプリケーション ウィザードで生成されたローカル アプリケーション"));
	LoadStdProfileSettings(4);  // 標準の INI ファイルのオプションをロードします (MRU を含む)
	// アプリケーション用のドキュメント テンプレートを登録します。ドキュメント テンプレート
	//  はドキュメント、フレーム ウィンドウとビューを結合するために機能します。
	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CPictureAlbumDoc),
		RUNTIME_CLASS(CMainFrame),       // メイン SDI フレーム ウィンドウ
		RUNTIME_CLASS(CPictureAlbumView));
	AddDocTemplate(pDocTemplate);
	// DDE、file open など標準のシェル コマンドのコマンドラインを解析します。
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);
	// コマンド ラインで指定されたディスパッチ コマンドです。アプリケーションが
	// /RegServer、/Register、/Unregserver または /Unregister で起動された場合、 False を返します。
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;
	// メイン ウィンドウが初期化されたので、表示と更新を行います。
	m_pMainWnd->ShowWindow(SW_SHOW);
	m_pMainWnd->UpdateWindow();
	// 接尾辞が存在する場合にのみ DragAcceptFiles を呼び出してください。
	//  SDI アプリケーションでは、ProcessShellCommand の直後にこの呼び出しが発生しなければなりません。

	// プロパティにセット(消去はMainFrm.cpp内のデストラクタに記述)
	::SetProp(m_pMainWnd->GetSafeHwnd(), "PictureAlbum by Antenn-a", (HANDLE)1);

	return TRUE;
}
						

着目点は /* 2重起動防止 */ のコメントアウトの当たりと、returnの直前にある ::SetProp()って奴です。
これらを追記してから、MainFrm.cpp を開きデストラクタで
CMainFrame::~CMainFrame()
{
	// プロパティ消去(作成は PictureAlbum.cpp内に記述)
	::RemoveProp(GetSafeHwnd(), "PictureAlbum by Antenn-a");
}
						

こんな感じです。
これで2重起動が防止できます。

prev


何をしているかというと、Mutex(相互排他オブジェクト)を作ってるんですね。
で、最初の着目点では CreateMutex しようとしてます。この時ユニークなIDを文字列で定義出来、今回は PictureAlbum by Antenn-aという文字列でした。
その後、1回目の起動の場合は、スルスルと流れて ::SetProp でそのウィンドウのプロパティに PictureAlbum by Antenn-a を書き込みます。
次回起動時は、同じIDでMutexを作ると失敗し、GetLastError に引っかかります。
ここで、return すればアプリケーションが作成されません。
また、一度セットしたプロパティは消さねばならないので、MainFrame のデストラクタでプロパティを消去してます。
これで、アプリケーションは2重起動する事は無く、最小化されていても元に戻ってアクティブウィンドウになります。
出来た出来た、わーい


antenn-a

prev next


antenn-a