ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

コントロールパネルにアイコンを追加する

1.はじめに

Windowsのコントロールパネルを開くと、複数のアイコンが並んでいる。

例えば、こんな感じだ。

Windows NT 4.0

Windows XP

バージョンや機種・インストールされているアプリケーションの種類などによって、その内容は様々に変わりうる。中には、明らかにWindowsの持ち物と思われるものもあれば、逆に、どう見てもサードパーティにより提供されていると思われるアイコンもある。

ということはつまり、このアイコンは、自分で作って組み込むことができるということを意味している。であれば、オリジナルのものを作って組み入れてみようと思うのは人間としての正常な欲求である。違うか?

ということでこのページでは、Windowsのコントロールパネルにオリジナルのアイコンを追加する方法について解説する。

2.するべきこと

簡潔に言うならコントロールパネルのアイコンは、誰だか知らないが恐らくはエクスプローラが、所定の場所に格納された「CPlAppletという関数をエクスポートするDLL」を読み込むことによって表示している。

つまり、「CPlAppletという関数をエクスポートするDLL」が、エクスプローラか何か知らない奴に対して、コントロールパネルに表示するアイコンの情報と、それがダブルクリックされたときに行うべき機能を提供していると言うことだ。

だから、そのCPlAppletという関数を自分で実装して、それをDLLにして、所定の方法でインストールしてやれば、コントロールパネルに好きな機能を持った好きなアイコンを追加することができるようになる。

3.CPlApplet

CPlAppletという関数は、下記のようなプロトタイプを持っている。

LONG CPlApplet(
    HWND hwndCPl,
    UINT uMsg,
    LONG lParam1,
    LONG lParam2
);

いつかどこかで見たことがあるような関数。

この関数に対して、どこかの誰かが下記のようなメッセージを送りつけてくるから、それぞれのメッセージを適当に捌いて処理するような機能を実装してあげればいい。

3.1 CPL_INIT

DLLがロードされた直後に、このメッセージが送られてくる。

何かすることがあるなら、ここで然るべき処理を行えばいい。何もないなら、何もする必要はない。

なお、このメッセージの処理が正常に終了した場合には、CPlApplet関数の戻り値はTRUEでなければならない。0を返すと、コントロールパネルにアイコンを表示してくれなくなる。

3.2 CPL_GETCOUNT

CPL_INITの後に送られてくる。

このDLLが、コントロールパネルにいくつのアイコンを表示したいのかを戻り値として返してやる。ごく普通に、一種類のダイアログボックスを表示したいだけであれば、1を返してやればいい。

3.3 CPL_INQUIRE

CPL_GETCOUNTの後に送られてくる。

コントロールパネルに表示するアイコンの情報を返してやる。このメッセージが送られてくる場合、CPlApplet関数の引数lParam2は、CPLINFOという構造体のアドレスになっているから、その構造体に然るべき値を設定してやる。

CPLINFOは下記のような構造体である。

typedef struct tagCPLINFO {
    int idIcon; 
    int idName; 
    int idInfo; 
    LONG lData; 
} CPLINFO;

3.3.1 idIcon

コントロールパネルに表示するアイコンの、リソースのIDを設定する。

3.3.2 idName

コントロールパネル中のアイコンの下に表示する名前を定義した、リソースのIDを設定する。

3.3.3 idInfo

コントロールパネルを詳細表示にした場合に表示される、「説明」のカラムに表示する文字列を定義した、リソースのIDを設定する。

3.3.4 lData

CPL_DBLCLKとCPL_STOPのメッセージが送られてくるとき、CPlApplet関数の引数lParam2に、このlDataに指定された値が設定される。

システム側ではこの値を直接使用することはなく、アプリケーションが勝手に使っていいらしい。

3.4 CPL_NEWINQUIRE

CPL_INQUIREと同じようなものだが、CPL_NEWINQUIREメッセージの応答として返した情報の方が優先的に使用される。

ただMSDNには、CPL_INQUIREの方が性能的に有利だから、CPL_INQUIREの処理だけを行って、CPL_NEWINQUIREは無視しろと書いてある。

一応説明しておくと、CPL_NEWINQUIREメッセージが送られてくるとき、lParam2にはNEWCPLINFO構造体のアドレス値が設定される。だから、そのNEWCPLINFO構想対の各メンバに適切な値を設定してやればいいことになる。

NEWCPLINFO構造体は下記のような構造となっている。

typedef struct tagNEWCPLINFO {
    DWORD dwSize; 
    DWORD dwFlags; 
    DWORD dwHelpContext; 
    LONG lData; 
    HICON hIcon; 
    TCHAR szName[32]; 
    TCHAR szInfo[64]; 
    TCHAR szHelpFile[128]; 
} NEWCPLINFO;

3.4.1 dwSize

NEWCPLINFO構造体のサイズ、すなわちsizeof( NEWCPLINFO )の値を設定してやる。

3.4.2 dwFlags

使ってないらしい。無視されると書いてある。

3.4.3 dwHelpContex

これも使っていないらしい。無視されると書いてある。

3.4.4 lData

CPL_DBLCLKとCPL_STOPのメッセージが送られてくるとき、CPlApplet関数の引数lParam2に、このlDataに指定された値が設定される。

システム側ではこの値を直接使用することはなく、アプリケーションが勝手に使っていいらしい。

3.4.5 hIcon

コントロールパネルに表示するアイコンのハンドルを指定する。

CPLINFO::idIconだとリソースのIDを指定すればよかったのだが、NEWCPLINFO::hIconの場合は自分でアイコンを読み込んで、ハンドルを取得してやらなければならない。

3.4.6 szName

コントロールパネル中のアイコンの下に表示する名前を設定する。

3.4.7 szInfo

コントロールパネルを詳細表示にしたときに表示される、「説明」のカラムに表示する説明文の文字列を設定する。

3.4.8 szHelpFile

これも使っていないらしい。無視されると書いてある。

3.5 CPL_SELECT

MSDNには書いてあるが、送られてくることはないらしい。

3.6 CPL_DBLCLK

このメッセージは、コントロールパネルに表示されたアイコンがダブルクリックされたときに送られてくる。

このメッセージを受け取ったときに、普通にダイアログボックスを表示してやればいい。また、関数の制御はダイアログボックスが閉じられたときに返せばいいので、MFCであればCDialog::DoModal()を使うと、簡単に実装できる。

処理が終わったら、0でも返しておけばよろしい。

3.7 CPL_STOP

ダイアログボックスが閉じられるときに送られてくる。何かすることがあればすればいいし、何もすることがないなら、何もする必要はないらしい。

3.8 CPL_EXIT

DLLが解放されるとき、つまりFreeLibrary関数によりDLLがアンロードされるときに、このメッセージが送られてくる。何かすることがあるなら、ここでしかるべき処理を行う。何もないなら、何もする必要はない。

3.9 CPL_STARTWPARAMS

Windows 2000以降から追加された。

CPL_DBLCLKと似たような奴で、ユーザがアイコンを選択したときに送られてくる。プログラムとしては、このタイミングでダイアログボックスを表示してやれば良いらしい。

なお、このメッセージが送られてくる場合は、lParam1にはサブプログラム番号が、lParam2には文字列の拡張情報が渡されるらしい。

4.実装

以上の説明を基に、コントロールパネルのアイコンを実装してみることにする。

とりあえずここでは、Win NT 4.0上のVisual Studio 6.0を使っている。ずいぶん、前時代的な気がしなくもないが、基本的なところは環境が変わっても同じはず。

4.1 DLLを作る

とりあえず横着をするために、MFCを利用するDLLを作るものとする。

4.2 CPlApplet関数の作成

cpltest.cppファイルに、下記の記述を追記する

・・・・・・
#include "stdafx.h"
#include "Cpl.h"     // 必要な定義が記述されている
#include "cpltest.h"
・・・・・・
CCpltestApp theApp;

LONG APIENTRY CPlApplet(
    HWND hwndCPl,
    UINT uMsg,
    LONG lParam1,
    LONG lParam2
)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState());

  switch ( uMsg ) {
  default:
    break;
  }
  return 0;
}

4.3 送られてくるメッセージを処理する

ここままではswitch文の中身が空で、何の役にも立たないため、各メッセージを処理するロジックを実装してやる必要がある。

4.3.1 CPL_INIT

ここでは特にすることはないので、とりあえず1を返しておく。

case CPL_INIT:
  return 1;

4.3.2 CPL_GETCOUNT

表示したいアイコンの個数を返す。とりあえずここでは1を返しておく。

case CPL_GETCOUNT:
  return 1;

4.3.3 CPL_INQUIRE

このメッセージは、CPL_GETCOUNTの時に返した値の数だけ送られてくるのだが、ここでは固定的に1を返しているため、細かいことは気にしないものとする。とりあえず、CPLINFO構造体に値を設定してやればいい。

だがその前に、構造体に設定するためのリソースの定義を行ってやる。

アイコン

ストリングテーブル

そして、定義したリソースを構造体に設定してやる。

case CPL_INQUIRE:
  ((CPLINFO*)lParam2)->idIcon = IDI_ICON1;
  ((CPLINFO*)lParam2)->idName = IDS_STRING1;
  ((CPLINFO*)lParam2)->idInfo = IDS_STRING2;
  ((CPLINFO*)lParam2)->lData = 0;

4.3.4 CPL_NEWINQUIRE

することはないから無視する。

case CPL_NEWINQUIRE:
  return 0;

4.3.5 CPL_SELECT

送られてくることもないはずだし、とりあえず無視する。

case CPL_SELECT:
  return 0;

4.3.5 CPL_DBLCLK

MFCで普通にダイアログボックスを定義して、DoModalを呼び出してやればいい。

ダイアログの追加

cpltest.cppにヘッダファイルをインクルード

#include "stdafx.h"
#include "Cpl.h"
#include "cpltest.h"
#include "CplDlg.h"

CPlApplet関数のswitch文に、CPL_DBLCLKメッセージを処理する記述を追記。

case CPL_DBLCLK:
  {
    CCplDlg dlg;
    dlg.DoModal();
  }
  return 0;

4.3.6 CPL_STOP

することはないから無視。

case CPL_STOP:
  return 0;

4.3.7 CPL_EXIT

することはないから無視。

case CPL_EXIT:
  return 0;

4.4 CPlApplet関数のエクスポート

作成したCPlApplet関数をエクスポートする必要があるため、defファイルに以下の記述を追記する。

・・・・・・
EXPORTS
  ; 明示的なエクスポートはここへ記述できます
  CPlApplet

4.5 コンパイル

うまくコンパイルが通ることを神に祈る。祈りが通じればcpltest.dllファイルが生成されるはずである。

5.インストール

作成したDLLをインストールして、コントロールパネルにアイコンとして表示させるには、以下の方法がある。

5.1 拡張子をcplにして、system32のフォルダに格納する

読んで字のごとく、system32のフォルダに格納してやる。

5.2 レジストリに設定する

HKEY_CURRENT_USER\Control Panel\MMCPLに値を追加する。

名前は何でもいいらしい。文字列の値として、DLLのファイル名を指定する。

うまくいけば、コントロールパネルを開いたときに、新しいアイコンが表示されるはず。

Windows NT 4.0

Windows XP

もっとも、ちゃんとアイコンを書いていないから、真っ白なままだが。

でもって、このアイコンをダブルクリックすれば、下記の様な虚しいダイアログボックスが表示される。

一応、上記の説明で作ったプログラムを公開しておく。

cpltest.zip 12KB

6.複数のアイコンを表示する

CPL_GETCOUNTメッセージの戻り値として、2以上の値を返せば、1つのDLLで複数個のアイコンを表示させることが可能となる。

またその場合、CPL_INQUIRE・CPL_NEWINQUIRE・CPL_DBLCLK・CPL_STOPの各メッセージは、CPL_GETCOUNTで返した値の数だけメッセージが送信されてくる。

さらに、そのときlParam1には、当該のメッセージがどのアイコンに対するものなのかを表すインデックス値が指定される。すなわち、CPL_GETCOUNTで2を返した場合、CPL_INQUIRE・CPL_NEWINQUIRE・CPL_DBLCLK・CPL_STOPの各メッセージが、lParam1が0の場合と1の場合の2回ずつ送られてくることになる。

だから、上記の各メッセージを処理する際に、lParam1の値を見て、それぞれのアイコンに対する処理を行うようにしてあげれば、1つのDLLでアイコンを2つ表示することが可能となる。

プログラムにすると下記のようになる。

switch ( uMsg ) {
・・・・・・
case CPL_GETCOUNT:
  return 2;  // アイコンを2つ表示する
case CPL_INQUIRE:
  switch ( lParam1 ) {
  case 0:
    // 一つ目のアイコンの情報を設定する
    break;
  case 1:
    // 二つ目のアイコンの情報を設定する
    break;
  }
  return 0;
case CPL_DBLCLK:
  switch ( lParam1 ) {
  case 0:
    // 1つ目のアイコンに対応する処理を行う
    break;
  case 1:
    // 2つ目のアイコンに対応する処理を行う
    break;
  }
  return 0:
・・・・・・
}

上記の要領で、アイコンを1つ表示させると、下記のようになる。

余り意味はないが、とりあえずアイコンを2つ表示させるプログラムも公開しておく。

cpltest2.zip 13KB

7.最後に

存外、普通のアプリケーションプログラムでは、コントロールパネルにアイコンを追加しなければならない要件というのは無いようだが、まぁ、好きならやってみるのも良いのではないだろうか。

もっとも、あんまり細かい芸を出し過ぎると、うざったがれることになりかねないがな。