EternalWindows
コントロールパネル / cplファイルの作成

今回は、具体的なコードをCPlApplet関数に書いていきます。 早速ですが、プログラムを見てください。

#include <windows.h>
#include <cpl.h>
#include "resource.h"

INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

LONG APIENTRY CPlApplet(HWND hwndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
{
	switch (uMsg) {

	case CPL_INIT:
		return 1;

	case CPL_GETCOUNT:
		return 1;

	case CPL_INQUIRE: {
		LPCPLINFO lpcpl = (LPCPLINFO)lParam2;

		lpcpl->idIcon = IDI_ICON1;
		lpcpl->idName = IDS_STRING102;
		lpcpl->idInfo = IDS_STRING103;
		lpcpl->lData  = (LPARAM)GetModuleHandle(TEXT("mycpl.cpl"));

		break;
	}

	case CPL_DBLCLK: {
		HINSTANCE hinst = (HINSTANCE)lParam2;

		DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), hwndCPl, DialogProc);

		break;
	}
	
	case CPL_STOP:
		break;
	
	case CPL_EXIT:
		break;

	default:
		break;

	}

	return 0;
}

INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK) {
			TCHAR szModule[256];
			
			GetModuleFileName(NULL, szModule, sizeof(szModule));
			MessageBox(hwndDlg, szModule, TEXT("mycpl.cplをロードしているプロセス"), MB_OK);
		}
		else if (LOWORD(wParam) == IDCANCEL) {
			EndDialog(hwndDlg, 1);
			return 1;
		}
		else
			;
		break;
	
	case WM_CLOSE:
		EndDialog(hwndDlg, 1);
		return 1;

	default:
		break;

	}

	return 0;
}

まず、CPL_INITメッセージから見ていきます。

case CPL_INIT:
	return 1;

CPL_INITは、CPlAppletに最初に送られてくるメッセージです。 一般には、このメッセージでcplファイル内で扱うことになるであろうデータを 初期化したりするのですが、今回は大規模のプログラムを作るわけではないので、 特に何も処理をしていません。 しかし、このメッセージでは初期化に問題がなければ0以外の値を返すべきなので、 きちんとメッセージを捕らえ、0以外の値を返すようにします。

case CPL_GETCOUNT:
	return 1;

CPL_GETCOUNTは、サポートするダイアログの数を返します。 一般には、ダイアログを1つ表示するだけでしょうから1を返すことになるでしょう。 プロパティシートを表示するようなときは、ダイアログが複数になりますが、 そのような場合でも1を返してください。

case CPL_INQUIRE: {
	LPCPLINFO lpcpl = (LPCPLINFO)lParam2;

	lpcpl->idIcon = IDI_ICON1;
	lpcpl->idName = IDS_STRING102;
	lpcpl->idInfo = IDS_STRING103;
	lpcpl->lData  = (LPARAM)GetModuleHandle(TEXT("mycpl.cpl"));

	break;
}

CPL_INQUIREは、cplファイルをロードしたプロセスが、 そのcplファイルに含まれているアイコンや文字列を取得するために送られます。 これらの情報はCPLINFO構造体で表され、lParam2はそのアドレスを指しているので、 LPCPLINFO型として受け取り、メンバを初期化します。 idIconはアイコンの識別子、idNameはアイコンの名前を示す文字列の識別子、 idInfoにはアイコンの説明文を示す文字列の識別子を指定します。 lDataは初期化する必要のないメンバですが、 このメンバはCPL_DBLCLKのlParam2に相当することになるため、 CPL_DBLCLKはそれを利用しています。

case CPL_DBLCLK: {
	HINSTANCE hinst = (HINSTANCE)lParam2;

	DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), hwndCPl, DialogProc);

	break;
}

CPL_DBLCLKは、ユーザーがアイコンをクリックしたときに送られます。 このlParam2には、CPL_INQUIREで設定したlDataと同じ値が返ります。 つまり、GetModuleHandleの戻り値と同じです。 もちろん、CPL_DBLCLKでGetModuleHandleを呼び出しても問題はありません。 モジュールのハンドルはDialogBoxの第1引数が必要としますが、 ここで指定するハンドルは必ずmycpl.cplのハンドルでなければなりません。 GetModuleHandleの第1引数にNULLを指定すると、 cplファイルをロードしたプロセスのEXEモジュールのアドレスが返ることになるため、 この点は十分に注意しておくべきです。 DialogBoxは、モーダルダイアログボックスを作成するため、 ダイアログプロシージャがダイアログを破棄するまで制御を返しません。 第4引数に指定したDlgProc(ダイアログプロシージャ)は、以下のようになっています。

INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK) {
			TCHAR szModule[256];
			
			GetModuleFileName(NULL, szModule, sizeof(szModule));
			MessageBox(hwndDlg, szModule, TEXT("mycpl.cplをロードしているプロセス"), MB_OK);
		}
		else if (LOWORD(wParam) == IDCANCEL) {
			EndDialog(hwndDlg, 1);
			return 1;
		}
		else
			;
		break;
	
	case WM_CLOSE:
		EndDialog(hwndDlg, 1);
		return 1;

	default:
		break;

	}

	return 0;
}

このダイアログが閉じられるのは、キャンセルボタンと閉じるボタンを押したときであり、 それぞれはEndDialogを呼び出す設計になっています。 OKボタンが押されたときには、メッセージボックスを表示します。 何もしないダイアログでは寂しいので、このような処理を行いました。 実用的なダイアログの場合は、配置するコントロールも多いでしょうから、 DlgProcのコード量もそれに伴って増えることになります。

ビルドに成功するとcplファイルが作成されると思います。 後は、このファイルがコントロールパネルに反映されるよう、 c:\WINDOWS\system32に移動させることのみです。 なお、ダイアログのテストだけを行うつもりであれば、 cplファイルを右クリックメニューから開くだけで構いません。

これで、全ての処理が完了したことになります。 早速、コントロールパネルを開いてみましょう。

どういうわけか、自作したアイコンや文字列が表示されていません。 実はこれにはきちんとした理由があり、 ただ単純にc:\WINDOWS\system32にcplファイルを置くだけでは、 「コントロールパネルのその他のオプション」というカテゴリに 属することになっているのです。 したがって、「その他のオプション」を開けばアイコンを確認することができます。

アイコン、名前、説明文がいずれも正しく表示されていることが分かります。 クリックするとダイアログが表示されます。

芸のないダイアログではありますが、十分満足できる結果だと思います。 ダイアログを複数用意し、CPL_DBLCLKでDialogBoxではなく CreatePropertySheetPage及び、PropertySheetを呼び出すようにすれば、 プロパティシートを表示できるようになるため、一層見た目が強化されるはずです。

プログラムの内容は抜きにして、今回の結果に不満があるとすれば、 アイコンを表示させるカテゴリを選択できないことでしょうか。 たとえば、自作のアイコンを「その他のオプション」にではなく、 「ユーザー アカウント」のカテゴリに表示したいと場合もあるでしょう。 実は、これらはレジストリの設定を変更することにより、 任意のカテゴリにアイコンを表示させることが可能となっています。 次節では、その方法について説明します。


戻る