質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.01%

SetLayeredWindowAttributesの透過領域のメッセージについて

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 569

Weapon

score 79

前提・実現したいこと

子ウィンドウを"閉じるボタン"として親ウィンドウ子ウィンドウともにSetLayeredWindowAttributesの指定色で透過しました.
あとから全体に描画はする予定で一時的にGRAY_BRUSHで中央に色が塗ってありますが"閉じるボタン"としての子ウィンドウ部分は透過部分もボタンとして作用してほしいです.
SetLayeredWindowAttributesの透過部分のLBUTTONDOWN等のメッセージは受け取るようにはできませんか?

該当のソースコード

#define UNICODE

#include <Windows.h>

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WindowDestroyProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hGlobalInstance;

const wchar_t CLASS_NAME[] = L"CLASS NAME";
const wchar_t DESTROY_CLASS_NAME[] = L"DESTROY CONTROL CLASS NAME";

HWND mhwnd;

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR nCmdLine, int nCmdShow) {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(nCmdLine);
    hGlobalInstance = hInstance;

    WNDCLASSEX wc = {
        sizeof(WNDCLASSEX), CS_VREDRAW | CS_HREDRAW, WindowProc,
        0, 0, hInstance,
        NULL, (HCURSOR)LoadCursor(NULL,IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH),
        NULL, CLASS_NAME, NULL
    };

    RegisterClassEx(&wc);

    HWND hwnd = CreateWindowEx(
        WS_EX_LAYERED, CLASS_NAME, NULL, WS_POPUP | WS_BORDER,
        100, 100, 960, 540,
        NULL, NULL, hInstance, NULL
        );

    mhwnd = hwnd;

    SetLayeredWindowAttributes(hwnd, 0x0, 0, LWA_COLORKEY);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg = {};

    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) {
    switch (uMsg) {
    case WM_CREATE:
    {
        WNDCLASSEX wdc = {
            sizeof(WNDCLASSEX), NULL, WindowDestroyProc,
            0, 0, hGlobalInstance,
            NULL, (HCURSOR)LoadCursor(NULL,IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH),
            NULL, DESTROY_CLASS_NAME, NULL
        };
        RegisterClassEx(&wdc);
        HWND dwnd = CreateWindowEx(
            0, DESTROY_CLASS_NAME, NULL, WS_CHILD,
            815, 30, 105, 30,
            hwnd, NULL, hGlobalInstance, NULL
        );

        ShowWindow(dwnd, SW_SHOW);
        UpdateWindow(dwnd);
    }
        break;

    case WM_LBUTTONDOWN:
        PostMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
        break;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc = { 50, 50, 910, 530 };

        FillRect(hdc, &rc, (HBRUSH)GetStockObject(GRAY_BRUSH));

        EndPaint(hwnd, &ps);
    }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

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

LRESULT CALLBACK WindowDestroyProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_LBUTTONUP:
        SendMessage(mhwnd, WM_DESTROY, 0, 0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        RECT rc = { 5, 5, 100, 25 };

        FillRect(hdc, &rc, (HBRUSH)GetStockObject(GRAY_BRUSH));

        EndPaint(hwnd, &ps);
    }
    break;

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

補足情報

Windows10 Pro
VisualStudio2017 Community

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

SetLayeredWindowAttributesの透過部分のLBUTTONDOWN等のメッセージは受け取るようにはできませんか?

非常に残念ですが、WS_EX_LAYERED なウィンドウを LWA_COLORKEY で透過させた場合、そのウィンドウで直接マウスメッセージを受け取る手段はおそらくありません。とりあえず、回避策として私が提案できるのは以下のものになります。

1.別のトップレベルウィンドウを透明に近い半透明にして重ね合わせる手法

LWA_COLORKEY で透過させたウィンドウの同じ位置・同じサイズに LWA_ALPHA を指定した別ウィンドウを配置する手法です。そのウィンドウでマウスを処理させることにより透過されつつマウス動作を実現します。アルファ値が 0 だとマウスが透過してしまうので、必ず1以上を指定する必要があります。アルファブレンドされるため、完全に元のイメージのままではありませんが、下の案よりは比較的ましな動きをします。
実際には LWA_ALPHA を指定しなくても WS_EX_LAYERED スタイルを指定すれば、完全に透過してマウスメッセージを受け取れるウィンドウが作成可能でした。元のウィンドウの位置にピッタリ合わせて動かす部分も含めて検証したので、以下のコードを確認してみてください。
https://github.com/atata0319/teratail176225

2.SetCapture でマウスメッセージをキャプチャする手法

透明部分のマウスメッセージをキャプチャして、自ウィンドウで処理する手法です。該当のウィンドウの外をクリックされた際に下のウィンドウにマウスを PostMessage するなど、標準動作ではない動きが必要になるため作ろうとしているアプリケーション以外の操作性に難が出る可能性があります。

3.ウィンドウの下側にあるウィンドウをキャプチャして透明にしたい部分に描画する手法

直近に関係のある質問(ウィンドウの後ろ側をキャプチャする方法(win10))がありましたが、実装難度が高いのでお勧めしません。うまく動作させるまでが大変です。

4.フックでマウスメッセージを受け取る手法

2とほぼ同じですが問題も多いので、あまりお勧めしません。グローバルフック側でマウスメッセージを処理しなかった場合、怪しい動作が多いもので・・・。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.01%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る