常駐型アプリケーション

 KAB−studioのほとんどのアプリケーションは常駐型と呼ばれるものです。普段はウィンドウが表示されず、どこかで出番が来るのをじっと待っています。そして、タスクトレイのアイコンがクリックされたりシステムフックからメッセージが送られてきたときに反応し、メニューやダイアログを表示したりするわけです。
 ここでは、この常駐型アプリケーションについて見ていきましょう。

「常駐型」の意味
 「常駐型アプリケーション」とは、どのようなアプリケーションなのでしょう。
 まず、多くの場合ウィンドウが表示されません。タスクバーにも表示されず、何かのイベントが起きるのをじっと待ちます。
 また、通常のMFCアプリケーションと違い、ドキュメント、つまり保存するファイルを持ちません。ドキュメント・ビュー・アーキテクチャーとはまったく無関係なアプリケーションということになります。
 とはいえ、アプリケーションは必ずひとつはウィンドウを持っていなければなりません。そうでないと、アプリケーションを終了させることができませんし、タスクトレイやシステムフックからのメッセージを受け取ることさえできません(なんで? と思われる方に説明すると、「ウィンドウ」は表示される四角形の枠という点ではそれほど重要でなはなく、むしろメッセージを処理するための単位として機能するのが本来の目的だからです)。
 つまり、ウィンドウをひとつだけ持ち、あとは余計なものを持たず、そして非表示になっているアプリケーションこそが常駐型と呼べるのではないでしょうか。
 ……ちなみに、「タスクトレイにアイコンがあれば常駐型」というのは間違いです。常駐型でなくてもタスクトレイにアイコンを置けますし、またその逆もOKです。この方法については、このMFCチップスのタスクトレイにアイコンを表示して、そのメッセージを受け取る方法を見てください。

ウィンドウを非表示にする方法
 ウィンドウを消すのはとっても簡単です。CWnd::ShowWindow()という関数は、ウィンドウの表示状態を変更することができます。この引数にSW_HIDEを入れて呼び出せば、そのウィンドウは見事に消え去り、タスクバーにも表示されなくなります。
 例えば、すべてのアプリケーションはCWinAppの派生クラスを持ちます。その中のInitInstance()関数の中で


	m_pMainWnd->ShowWindow( m_nCmdShow );
	

 と書かれた部分があるでしょう。このm_nCmdShowの部分をSW_HIDEに書き換えてしまえばあまりにもあっさりとウィンドウを非表示にすることができます。その他、この関数に様々な値を渡せば、アイコン化や最大化などもできます。
 ちなみにm_nCmdShowCWinAppのメンバで、アプリケーションのパラメータとして渡されたウィンドウの表示状態を決める値です。アプリケーションを実行するショートカットには「実行時の大きさ」という設定があります。この時の設定がそのまま渡されるというわけです。

「ドキュメント・ビュー・アーキテクチャー」からの解放
 では次に、余分なドキュメントやビューを取り払ってしまいましょう。
 アプウィザードを使って作成した場合、自動的にCView、CDocumentの各派生クラスが作製されます。常駐型アプリケーションには必要のないものなので、削除してしまいましょう。

 どのドキュメントにどのビューが割り当てられているのか等の関連づけは、先ほどと同じCWinAppの派生クラスのInitInstance()関数の中で次のように書かれています。


	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CTestDoc),
		RUNTIME_CLASS(CMainFrame),       // メイン SDI フレーム ウィンドウ
		RUNTIME_CLASS(CTestView));
	AddDocTemplate(pDocTemplate);
	

 この部分を削除するかコメントアウトしてください。そして、このファイルの一番上に書かれている、ビューとドキュメントのヘッダーファイルへのインクルード部分も削除してください。

 次に、各ファイルをドキュメントから切り離します。「ワークスペース」を開き、「ファイルビュー」の中から、CView、CDocument各派生クラスのソースファイルとインクルードファイルを選択してDELキーを押してください。
 これは、「プロジェクト」から、コンパイルしないように外させるだけで、元のファイルは削除されませんので安心してください。再び加えるときには「プロジェクト」−「プロジェクトへ追加」−「ファイル」を選んでください。

 さらに、この下に書かれた次の部分も、削除するかコメントアウトしてください。


	// DDE、file open など標準のシェル コマンドのコマンドラインを解析します。
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	// コマンドラインでディスパッチ コマンドを指定します。
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;
	

 この部分では、アプリケーションが実行されたときにパラメーターをチェックし、ファイル名が指定されていない場合には空のドキュメントとビューを、指定されている場合にはそのファイル形式にあったドキュメントとビューを構築します。つまり、この時点でウィンドウが作製されるということです。
 というわけで、この部分を消せば勝手にウィンドウを作製されることはなくなったのですが、反面、ウィンドウの作製を行うコードを自分で書かなければいけないということです。

ウィンドウクラスを独自に登録する
 では、実際にそのコーディング方法を見ていきましょう。
 まず、先ほどの削除した部分の下に、次の1行を加えてください。


	// 次の行を追加してください。
	m_pMainWnd = new CMainFrame;	// CMainFrameを直接構築します。

	// メイン ウィンドウが初期化されたので、表示と更新を行います。
	m_pMainWnd->ShowWindow( SW_HIDE );
	m_pMainWnd->UpdateWindow();
	

 m_pMainWndCWinAppの(正確にはCWinThreadの)メンバ変数です。このメンバ変数のポインタが指し示すCFrameWnd派生クラスが、アプリケーションのメインウィンドウということになります。
 最初に説明したとおり、アプリケーションは必ずひとつ、ウィンドウを持つ必要があります。そのためのウィンドウをCFrameWnd派生クラスを使って作製し、そのクラスへのポインタを入れる必要があるわけです。
 それと忘れてはいけないのが、CMainFrameのコンストラクタのアクセス指定をpublicにすることです。つまり、MainFrame.hの中のCMainFrame()関数を、publicの下に持ってきておいてください。これは、new演算子によるものみたいです(ちょっと不明)。

 さて次に、ウィンドウクラスを登録します。ウィンドウクラスとは、ウィンドウズに登録する「ウィンドウの型」のようなものです。ウィンドウを作製する前に、まずこのウィンドウクラスの登録をしなければなりません(ちなみに、このクラスは、C++のクラスとは全く関係がありません)。

 ウィンドウクラスの登録方法は3つあります。ひとつはCFrameWnd::LoadFrame()を使う方法です。ただCMainFrameのコンストラクタの中に、次のコードを入れるだけで済みます。


CMainFrame::CMainFrame()
{
	LoadFrame(IDR_MAINFRAME);	//ウィンドウを構築します。
}
	

 この関数は、ウィンドウクラスの登録だけでなく、ウィンドウそのものの構築もしてくれます。IDR_MAINFRAMEは、リソースの中での標準IDのようなもので、見てみれば分かるとおりほとんどのリソースの中に存在します。つまり、IDR_MAINFRAMEはこれらのアイコンやメニューを取り込んでウィンドウを作製するということです。
 ですが、ここでは「ウィンドウを表示させない」のが目的なので、アイコン等は意味がないことになるためお奨めしません。

 ふたつめの方法は、AfxRegisterWndClass()を使用することです。が、ここでは紹介しません。基本的には次のAfxRegisterClass()と同じことをしていますが、内容がいまいち中途半端なもので……。

 みっつめの方法はAfxRegisterClass()を使用することです。この関数は::RegisterClass()とほぼ同じ機能なので、かなり基本的な部分から構築することができます。実際には下のようにコーディングします。


CMainFrame::CMainFrame()
{
	//////////////////////////////////
	// ウィンドウクラスを登録します。
	WNDCLASS	stWndClass;
	char	chClassName[] = _T( "TestWClass" );

	stWndClass.style = CS_BYTEALIGNWINDOW;	//スタイルです。
	stWndClass.lpfnWndProc = &AfxWndProc;;	//ウィンドウプロシージャへのポインタです。
	stWndClass.cbClsExtra  = 0;	//0にします。
	stWndClass.cbWndExtra  = 0;	//0にします。
	stWndClass.hInstance  = AfxGetInstanceHandle();	//インスタンスハンドルです。
	stWndClass.hIcon  = NULL;	//アイコンです。
	stWndClass.hCursor = NULL;	//カーソルです。
	stWndClass.hbrBackground  = (HBRUSH)COLOR_BACKGROUND;	//背景色です。
	stWndClass.lpszMenuName  = NULL;	//メニューIDです。
	stWndClass.lpszClassName  = chClassName;	//ウィンドウクラスの名前です。

	AfxRegisterClass( &stWndClass );	//ウィンドウクラスを登録します。

	//////////////////////////////////
	// 実際にウィンドウを作製します。
	CRect	rect( 0, 0, 100, 100 );

	Create( chClassName, _T( "てすとですぅ" ), WS_OVERLAPPEDWINDOW | WS_MINIMIZE, rect );	//ウィンドウを構築します。
}
	

 ウィンドウクラスの登録にはWNDCLASS構造体が必要になります。基本的な説明は「テクニカル ノート 1: ウィンドウ クラスの登録」を参照してください。
 プロシージャへのアドレスはAfxWndProcへのポインタを入れます。アイコンやメニューは必要ないのでNULLを入れています。
 stWndClass.lpszClassNameには、ウィンドウクラス名が入ります。Spy++などでウィンドウを見てみると、ウィンドウ名の他にウィンドウクラス名があることが分かると思います。その名前をここで指定します。

 データを入れたら、AfxRegisterClass()を使用してウィンドウクラスを登録します。

ウィンドウの作製
 ウィンドウを登録したら、実際にウィンドウの構築を行います。
 ウィンドウの作製はCWnd::Create()でクラス名とウィンドウ名を指定してウィンドウを構築します。ここでは「表示されないウィンドウ」を作製しているので、別にウィンドウ名やウィンドウスタイルは必要ないでしょう。
 また、CWnd::CreateEx()を使用すると「拡張スタイル」を使用することができます。拡張スタイルを設定することで、さらに思い通りのウィンドウを作製することができると思います。と言っても、表示されないウィンドウを作製するわけですから、やはり必要がありませんね。

 と、ここまで来たらビルドしてもOKです。と、注意して欲しいのは、そのまま実行するとアプリケーションを終了することができません(笑)。タスクトレイにアイコンを表示させるなりタイマーを使うなりして、見えないウィンドウを閉じられるようにしましょう。

まとめ
 こういった作製方法は、Win32SDKベース(つまりMFCを使わないで)でアプリケーションを作る場合には初歩の初歩とでも言えるようなことのようですが、MFCで作る場合にはなかなか見えてこない部分ですし、また、実際にSDKだけで作る場合とは違う部分も多くあります。つまり、結局はMFCの中身について知っていないと難しいということになります。
 MFCの中身については、このCodianの「MFCユーザーのためのAPIプログラミング講座」を読んでもらえれば、それなりに分かってもらえるんじゃないかなぁと思います。

(C)KAB-studio 1997 ALL RIGHTS RESERVED.