Programming Field

WM_NCACTIVATEメッセージのlParam引数

※ この記事は2006年ごろに記述してからほとんど変更しておらず、最新環境で十分な検証ができていないため、一部挙動が異なる可能性があります。

WM_NCACTIVATEは、非クライアント領域のアクティブ状態が変更されたときに呼び出されるメッセージで、wParamに「アクティブになる場合にTRUE、非アクティブになる場合にFALSE」が入ります。また、メッセージ処理結果となる戻り値をFALSEにすると既定の挙動を行わないようにすることが可能ですが、複雑な処理を要する場合もあるため、基本的にはDefWindowProcに値を渡して既定の処理(タイトルバーの再描画など)を行い、その戻り値を返すようにします。また、DefWindowProcを呼び出す場合でも意図的にwParamを書き換えて実際に描画させたいタイトルバーの状態をコントロールすることも可能です。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        ...
        case WM_NCACTIVATE:
            // アクティブ表示を維持したい場合に wParam を書き換え、表示状態を維持する
            if (!wParam && bKeepActivate)
                wParam = TRUE;
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

ところが、このメッセージのlParamにウィンドウのハンドル(らしき値)が入ってくることがあります。同じアプリケーション内でアクティブウィンドウを変えたとき、lParamが新しいアクティブウィンドウのハンドルになるようで、これを既定のウィンドウプロシージャ(DefWindowProc)に渡すと、たとえプロシージャに渡す、アクティブか非アクティブになるかの情報を持つwParamを(非アクティブになるということでFALSEだったのを)意図的にTRUEに替えても、呼び出し元のウィンドウは非アクティブにさせられるようです。

表にすると以下の通りになっています。

DefWindowProcに渡す値DefWindowProcの処理(結果)
wParamlParam
FALSE0タイトルバーを非アクティブにする
FALSEウィンドウのハンドル(らしき値)タイトルバーを非アクティブにする
TRUE0タイトルバーをアクティブにする
TRUEウィンドウのハンドル(らしき値)タイトルバーを非アクティブにする

このことから、WM_NCACTIVATEをハンドルして「非アクティブになりそうだけどアクティブ表示を維持したい」(この場合最初のwParamFALSEになる)をしたい場合は、wParamをTRUEにしつつ、lParamを0にしてDefWindowProcを呼び出すようにします。なお、この実装は(意図的かは不明ですが)MFCでも行われています。

Microsoft Docsによると「(lParam は)ビジュアルスタイルが適用されたウィンドウでは使用されない」とありますが、Windows XP~Windows 7までの挙動である可能性があります(Windows 10で lParam にウィンドウハンドルと思われる値を確認しています)。