EternalWindows
コントロールパネル / コントロールパネルのカテゴリ

cplファイルが属するカテゴリは、次のレジストリキーに列挙されています。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\ Extended Properties\{305CA226-D286-468e-B848-2B2E8E697B74} 2

そのキーの一部を次の表に示します。

名前 データ
%SystemRoot%\system32\desk.cpl 1
%SystemRoot%\system32\inetcpl.cpl 3, 10
%SystemRoot%\system32\main.cpl 2
%SystemRoot%\system32\nusrmgr.cpl 9

名前は、cplファイルが格納されているパスを表しており、 データに設定されている値はカテゴリを表しています。 値とカテゴリとの対応は次のようになっています。

カテゴリ
0 コントロールパネルのその他のオプション
1 デスクトップの表示とテーマ
2 プリンタとその他のハードウェア
3 ネットワークとインターネット接続
4 サウンド、音声、およびオーディオデバイス
5 パフォーマンスとメンテナンス
6 日付、時刻、地域と言語のオプション
7 ユーザー補助オプション
8 プログラムの追加と削除
9 ユーザー アカウント
10 セキュリティ センター
0xFFFFFFFF カテゴリに追加しない

この表を参考に最初に示した表を見てみると、 たとえば、main.cplはデータが2になっていることから、 「プリンタとその他のハードウェア」のカテゴリに属することが分かります。 また、inetcplはデータが2つ設定されており、それらが3と10であるため、 「ネットワークとインターネット接続」と「セキュリティ センター」の カテゴリに属することが分かります。 なお、上記したレジストリキーに書き込まれていないcplファイルは、 「コントロールパネルのその他のオプション」に属することになります。

さて、上記したレジストリキーにcplファイルのパスと値を設定することで、 cplファイル任意のカテゴリに属させることができるのは分かりましたが、 どうやってこれらをレジストリに書き込めばよいでしょうか。 これは、単純にレジストリ系の関数を使用するのが一番だと思われます。 関数の詳細はここでは説明しませんが、次のようなコードになると思われます。

lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Extended Properties\\{305CA226-D286-468e-B848-2B2E8E697B74} 2"), 0, KEY_SET_VALUE, &hkey);
if (lResult == ERROR_SUCCESS) {
	DWORD dwData = 5;
	RegSetValueEx(hkey, TEXT("%SystemRoot%\\system32\\mycpl.cpl"), 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD));
	RegCloseKey(hkey);
}

この場合、データが5であることから、mycpl.cplは 「パフォーマンスとメンテナンス」に属することになります。

今回のプログラムは、上記したレジストリキーのエントリを列挙します。 つまり、カテゴリ登録しているcplファイルを列挙します。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBoxLeft = NULL;
	static HWND hwndListBoxRight = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		int   i = 0;
		HKEY  hKey;
		LONG  lResult;
		TCHAR szData[256];
		TCHAR szValue[256];
		DWORD dwData;
		DWORD dwType;
		DWORD dwValue;

		hwndListBoxLeft = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndListBoxRight = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Extended Properties\\{305CA226-D286-468e-B848-2B2E8E697B74} 2"), 0, KEY_QUERY_VALUE, &hKey);
		if (lResult != ERROR_SUCCESS) {
			MessageBox(NULL, TEXT("レジストリキーのオープンに失敗しました。"), NULL, MB_ICONWARNING);
			return -1;
		}

		for (;;) {
			dwData = sizeof(szData);
			dwValue = sizeof(szValue);
			lResult = RegEnumValue(hKey, i, szValue, &dwValue, 0, &dwType, (LPBYTE)szData, &dwData);
			if (lResult == ERROR_NO_MORE_ITEMS)
				break;

			if (dwType == REG_DWORD) {
				DWORD dw = *((LPDWORD)szData);
				wsprintf(szData, TEXT("%x (%u)"), dw, dw);
			}

			SendMessage(hwndListBoxLeft, LB_ADDSTRING, 0, (LPARAM)szValue);
			SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szData);

			i++;
		}
		
		RegCloseKey(hKey);

		return 0;
	}

	case WM_SIZE: {
		int cxRight = 250;

		MoveWindow(hwndListBoxLeft, 0, 0, LOWORD(lParam) - cxRight, HIWORD(lParam), TRUE);
		MoveWindow(hwndListBoxRight, LOWORD(lParam) - cxRight, 0, cxRight, HIWORD(lParam), TRUE);

		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

レジストリ関係のプログラムであるため特に説明はしませんが、 取得したデータの型を調べているのは重要なところです。

lResult = RegEnumValue(hKey, i, szValue, &dwValue, 0, &dwType, (LPBYTE)szData, &dwData);
if (lResult == ERROR_NO_MORE_ITEMS)
	break;

if (dwType == REG_DWORD) {
	DWORD dw = *((LPDWORD)szData);
	wsprintf(szData, TEXT("%x (%u)"), dw, dw);
}

RegEnumValueではデータをszDataで受け取っていますが、 データは常に文字列で格納されているとは限りませんし、 むしろ数値で格納されている方が多いのです。 そのため、データの型がREG_DWORDである場合には、 一度DWORD型の変数に移し、szDataを再初期化しています。

実をいうと、レジストリにはcplファイルのカテゴリ以外に、 cplファイル自体のパスを書き込むことのできるキーが存在します。 次のレジストリキーのエントリのデータには、cplファイルのパスが指定されています。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Cpls

このデータが読み込まれることにより、cplファイルをc:\WINDOWS\system32に置くことなく、 コントロールパネルに反映させることができます。 このおかげでメーカーは、自社のアプリケーションがインストールされたフォルダに cplファイルを在中させることができるため、管理しやすくなります。 実際にエントリを書き込むときには、次のような感じになるでしょうか。

lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls"), 0, KEY_SET_VALUE, &hKey);
if (lResult == ERROR_SUCCESS) {
	TCHAR szData[] = TEXT("C:\\Program Files\\mysoft\\mycpl.cpl")
	RegSetValueEx(hKey, TEXT("mycpl"), 0, REG_DWORD, (LPBYTE)szData, sizeof(szData));
	RegCloseKey(hKey);
}

RegSetValueExで、データの部分にcplファイルのパスを指定するのは当然ですが、 名前(第2引数)は自由に決めてよいと思われます。


戻る