【C++/Win32】Win32 を極めるための極意

C++
この記事は約9分で読めます。

WinUI3(Windows App SDK) に期待していたんですが、Windows 11 のメモ帳をみると、どうも僕が作りたいものと違ったので C++/Win32 でテキストエディタを作成を再開しました。

Win32 の UI はほんと古いですが、やっぱり、軽量で高速なのは何ものにも代え難いですね。無くならないことを祈るばかりですね。

ダイアログ系

DPI 変更時に自動的にサイズ変更してくれるのか?

対応しています。ただし、ダイアログが親ウィンドウになっているコントロールのみ DPI が変更されると、フォントサイズに応じたウィンドウサイズに変更されます。ダイアログではなく、何かのウィンドウの子ウィンドウになっていると、残念ながら手動でサイズ変更する必要があります。

ダイアログのサイズはダイアログ単位(DLU)です。ダイアログ単位とはフォントサイズをベースにウィンドウやコントロールのサイズが決まります。

ダイアログの標準フォントは何なのか?

Tahoma です。ダイアログの設定でシステムフォントを使用するを選択すると MS Shell Dlg が選択されますが内部では Tahoma が利用されます。

タブキーで子コントロールにフォーカス移動するには?

C# のパネルコントロールのような例えばスタティックコントロールを親にしてテキストボックスなどを配置すると、WS_TABSTOP が設定されていてもタブキーでテキストボックスにフォーカス移動しません。フォーカス移動させるには、CreateWindowEx の dwExStyle に WS_EX_CONTROLPARENT を設定するとフォーカス移動するようになります。

ダイアログに独自クラスを適用することはできるのか?

下記で可能です。これでダイアログでも通常ウィンドウで発生するメッセージ(例:WM_NCCALCSIZEなど)も処理できるようになります。

  • 独自クラス作成時の WNDCLASSEXW の cbWndExtra に DLGWINDOWEXTRA を設定する
  • リソースエディタでダイアログのクラス名に独自クラス名を設定する
  • ダイアログのデフォルトプロシージャは DefDlgProc に渡す

詳細は下記を参照してください。

コントロール系

コントロールの状態を取得するには?

コントロールの状態とはフォーカスインジケーターキーボードアクセラレータを表示する必要があるかどうかです。WM_QUERYUISTATE メッセージを送信すると取得できます。戻り値は下記になります。これずっと知りたくてどうやったらわかるのか謎でした。

コントロールを描画する時に WM_DRAWITEM のパラメータで状態はわかるのですが、 WM_PAINT で描画するときにどうやるのかがわからなくてフォーカス線は常に描画していました

UISF_ACTIVE(0x4):コントロールは、アクティブなコントロールに使用されるスタイルで描画する必要があります。
UISF_HIDEACCEL(0x2):キーボード アクセラレータは非表示になっています。
UISF_HIDEFOCUS(0x1):フォーカス インジケーターは非表示になります。

状態を取得する場合は WM_QUERYUISTATE メッセージを送信します。

LRESULT result = ::SendMessage(hWnd, WM_QUERYUISTATE, NULL, NULL);

状態を変更するには WM_UPDATEUISTATE メッセージを送信します。例えば、フォーカスを描画する状態にする場合は下記です。

::SendMessage(hWnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), NULL);

コントロールの ID を取得するには?

コントロールの ID を取得するには、GetWindowLong 関数にウィンドウハンドルを渡して取得します。IDC_STATIC だったらまとめて背景色を設定するなどの時に便利です。

LRESULT Sample::OnCtlColorStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    int controlId = ::GetWindowLong((HWND)lParam, GWL_ID);
    if(controlId == IDC_STATIC) {
        ::SetTextColor((HDC)wParam, RGB(0, 0, 0));
        ::SetBkColor((HDC)wParam, RGB(255, 255, 255));
        ::SetDCBrushColor((HDC)wParam, RGB(255, 255, 255));
        return (LRESULT)::GetStockObject(DC_BRUSH);
    }
    // IDC_STATIC 以外は対象のウィンドウに投げる
    return ::SendMessage((HWND)lParam, message, wParam, lParam);
}

オーナードローを子ウィンドウで処理するには?

通常オーナードローの WM_DRAWITEM メッセージは親ウィンドウに送信されます。これを子ウィンドウ側で処理するには親ウィンドウで受け取った WM_DRAWITEM メッセージを子ウィンドウに送信します。

switch(message) {
    case WM_DRAWITEM:
        {
            LPDRAWITEMSTRUCT lpDraw = (LPDRAWITEMSTRUCT)lParam;
            // 子ウィンドウにそのまま投げる
            SendMessage(lpDraw->hwndItem, WM_DRAWITEM, wParam, lParam);
            return TRUE;
        }
    default: return DefWindowProc(hWnd, message, wParam, lParam);
}

文字色や背景色の設定で親ウィンドウにメッセージが送られるコントロールは?

下記コントロールは文字色や背景色の設定時に親ウィンドウにメッセージが送信されます。

WM_CTLCOLORBTN:ボタン
WM_CTLCOLOREDIT:エディットコントロール
WM_CTLCOLORLISTBOX:リスト ボックス
WM_CTLCOLORSTATIC:静的コントロール

スタティック(静的)コントロールでキー入力を受け付けるコントロールは作成できるのか?

可能です。スタティックコントロールにキー入力処理を追加しようとしても、実際にキーを入力するするとキンキンとビープ音が鳴ります。スタティックコントロールはキー入力を受け付けていないからですね。下記のようにスタティックコントロールの WM_GETDLGCODE で例えば DLGC_WANTALLKEYS(すべてのキーボード入力) を返すとキー入力してもビープ音が鳴らなくなります。

LRESULT StaticClass::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {
        case WM_GETDLGCODE: return DLGC_WANTALLKEYS;
    }
    return ::DefSubclassProc(hWnd, message, wParam, lParam);
}

その他の戻り値は下記を参照してください。

スタティック(静的)コントロールのスクロールバーは利用できるのか?

利用できます。初期の状態ではスタティックコントロールにスクロールバーを表示しても、サム(つまみ)をドラッグすることができません。ドラッグできるようにするには、いくつかのウィンドウメッセージを DefSubclassProc ではなく、DefWindowProc に渡す必要があります。下記はその例です。これでスクロールバーが有効になります。

LRESULT StaticClass::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {
        case WM_NCHITTEST:              return ::DefWindowProc(hWnd, message, wParam, lParam);
        case WM_NCLBUTTONDOWN:          return ::DefWindowProc(hWnd, message, wParam, lParam);
        case WM_NCLBUTTONUP:            return ::DefWindowProc(hWnd, message, wParam, lParam);
    }
    return ::DefSubclassProc(hWnd, message, wParam, lParam);
}

詳細は下記を参照してください。

スクロールバーコントロールの設定値の制限は?

SetScrollInfo で SCROLLINFO 構造体に設定する際の制限です。スクロールバーを自作する時に役に立ちます。

ページサイズ(nPage)の制限

nPage に設定可能な値です。例えば、最小が 0 で最大が 100 なら、nPage に設定可能な最大値は 101 です。

0 から nMaxnMin + 1

スクロールボックスの位置(nPos)の制限

nPos に設定可能な値です。nMax が最大ではない点に注意です。例えば、最小が 0 で最大が 100 でページサイズが 10なら、nPos に設定可能な最大値は 91 です。

nMin から nMax – max(nPage – 1, 0)

詳細は下記を参照してください。

Win32を極めるためのサイト

インコのページ

C++/Win32 を始めたころにずっとサンプルプログラムを実行して動作確認していました。基本的な Win32 のコントロールの操作が学べるサイトです。

EternalWindows

新しいサブクラスのやり方を知ったのがこのサイトでした。

コメント

タイトルとURLをコピーしました