アークピットのホームページに戻る

WinAPIトピックのトップページに戻る

APIトピックの各章に移動する

ダウンロードのページに移動する
ダウンロードができ
ない場合の対処法
 

ページ移動

2-1-8. サブクラス化で広がる世界

 サブクラスは、コントロールなどの機能拡張に良く使用されます。ウィンドウの本来のプロシージャにフックを掛け、別のユーザが作成した関数に飛ばします。そしてその関数では、メッセージを追加や横取りして、機能を拡張または、統合して目的に添った改変を行ないます。

 サブクラスは、ウィンドウごとに管理しているプロシージャアドレスを変更します。よって対象のウィンドウしかその機能は搭載されません。以下の方法でサブクラス化を行ないます。

  1. GetWindowLong でオリジナルのプロシージャを得る。
  2. SetWindowLong で新しいプロシージャを設定する。
  3. 新しいプロシージャでは、新機能のメッセージをフックして処理を行なう。
  4. さらに新しいプロシージャは、オリジナルのプロシージャにジャンプする。
 つまりサブクラスとは、フック技術そのものです。フック(横取り)して利用者に都合のよい改変を行ないます。コントロールのキーボードインターフェースを付加する時などに、良くサブクラスを使用します。

サブクラス化の例

// コンボボックスのオリジナルのプロシージャを保存
static WNDPROC fnCbProc = NULL;

// 指定のコンボボックスにサブクラスを設定する。

VOID SetSubClass(HWND hCombo)
{
    if (fnCbProc == NULL)
        fnCbProc = (WNDPROC)GetWindowLong(hCombo,GWL_WNDPROC);
    SetWindowLong(hCombo,GWL_WNDPROC,(LONG)CbProc);
}

// サブクラス化のためのウィンドウプロシージャ

LRESULT CALLBACK CbProc(HWND hWnd,UINT uMsg
                                 ,WPARAM wParam,LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_LBUTTONDOWN:
        {
            if (ある条件)
            {
                SendMessage(hMainWnd,WM_COMMAND,
                            MAKEWPARAM(IDM_NEWCMD,0),0);
                return 0;
            }
            break;
        }
        case WM_MOUSEMOVE:
        {
            if (ある条件)
            {
                SetCursor(LoadCursor(hInstance,IDC_FINGER));
                return 0;
            }
            break;
        }
        case WM_CHAR:
        {
            if ((TCHAR)wParam == VK_RETURN)
            {
                SendMessage(hMainWnd,WM_COMMAND
                           ,MAKEWPARAM(IDM_NEWCMD,0),0);
                return 0;
            }
            break;
        }
    }
    return CallWindowProc(fnCbProc,hWnd,uMsg,wParam,lParam);
}
 この例では、コンボボックスを左クリックした時と、[Enter]キーを押した時に、WM_COMMAND メッセージを発行して、コマンドの実行を行ないます。またこのコントロールの上をマウスカーソルが移動する時に、ある条件では指カーソルに変更します。

 されプログラミングですが、まずコンボボックスを作成した後にそのハンドルをパラメータにして、SetSubClass 関数をコールします。ここでは、GetWindowLong 関数で、オリジナルのプロシージャを得て、SetWindowLong で新しいプロシージャを設定します。なお複数のコンボボックスにサブクラスを設定する場合は、2つ目以降は、GetWindowLong の必要はありません。もし重複してサブクラス化されている可能性がある時は、コンボボックスごとに、オリジナルのプロシージャを管理する必要があります。
 フックするメッセージは、WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_CHAR です。コードは見て頂けるとご理解できると思います。ただ、条件に合わず処理を行なわないときは、break してオリジナルのプロシージャに処理をまかせます。
 CallWindowProc 関数は、第一パラメータのプロシージャをコールします。(*fnCbProc)(hWnd,uMsg,wParam,lParam) と同じと考えてください。

 これらの例のフックの場合は、オリジナルのプロシージャの前に行なっています。そして、フック内容によっては、オリジナルのプロシージャの処理を行なったり、行なわなかったりします。しましまれに、オリジナルのプロシージャの処理を行なった後に、フックをしたいことがあります。

ページ移動