9

There are several ways to do this using .NET (take this for instance), yet I wasn't able to reproduce the same thing using only C++ win32.

My approach was to use WS_EX_LAYERED and then SetLayeredWindowAttributes to have some control over the opacity, but I read more and I found out that WS_EX_TRANSPARENT is 'better'- it allows click-through.

However, using

hWnd = CreateWindowEx(WS_EX_TRANSPARENT, fooName, fooName, WS_OVERLAPPEDWINDOW | WS_POPUP | WS_CLIPSIBLINGS, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

doesn't seem to do the trick. There is also another thing: once I get the click-through window working, can I use

PostMessage(hWnd, WM_LBUTTONUP, 0, MAKELPARAM(GET_X_LPARAM(lParam) ,GET_Y_LPARAM(lParam)));

to block the dragging state from passing through?

Note: the dragging state is produced using a touchpad device.

Community
  • 1
  • 1
George Netu
  • 2,758
  • 4
  • 28
  • 49
  • 1
    [Like the cake, WS_EX_TRANSPARENT is a lie, or at least not the entire truth](http://blogs.msdn.com/b/oldnewthing/archive/2012/12/17/10378525.aspx). Faking a `WM_LBUTTONUP` message will fail to update internal state vital for input handling. Depending on whom you're asking whether the left mouse button is down, you'll get different answers. – IInspectable Jul 09 '15 at 09:44
  • Did you notice that a STATIC window does not receive click messages? WM_LBUTTONDOWN and / UP go to the parent window. This seems not to depend on the creation flags but on the Window class. – Elmue Apr 05 '18 at 07:03

2 Answers2

6

The click-through part:

Indeed, WS_EX_TRANSPARENT by itself is a big lie; so I used WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST instead.

I have control over the opacity using SetLayeredWindowAttributes(hWnd, 0, (255 * opacity) / 100, LWA_ALPHA); (quite unorthodox, but it works) and I also use

SetCapture(hWnd);
ShowCursor(false);

to grab the mouse focus as the top level window doesn't let go and hides the cursor.

I also tried to force the focus on the window adding WM_NCACTIVATE and WM_ACTIVEAPP:

case WM_MOUSEMOVE:
        fprintf(stdout, "Mouse move [%d][%d]\n", GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
        SetForegroundWindow(hWnd);
        break;

case WM_LBUTTONDOWN:
        printf("Mouse click\n");
        SetForegroundWindow(hWnd);
        break;

case WM_NCACTIVATE:
        return false;

case WM_ACTIVATEAPP:
        wActive = (bool)wParam;

        if(wActive == false)
            return 0;
        else
            return DefWindowProc(hWnd, message, wParam, lParam);

The dragging part:

In my particular case I wanted to 'poke' the window underneath (child window) without losing focus; unfortunately, any mouse click event will change the focus to that child window - a solution would be to:

  1. set a timer (SetTimer, WM_TIMER) and check whether your application lost focus or not
  2. set a hook to your window and reply to a WM_KILLFOCUS message with a WM_SETFOCUS message
George Netu
  • 2,758
  • 4
  • 28
  • 49
  • What create command did you use? I tried `m_overlay.CreateEx(WS_EX_COMPOSITED | WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST, _T("Static"), _T(""), 0, CRect(), this, -1)` without success, where `m_overlay` is a `CWnd` derived object (returned `FALSE` and no `HWND` was created). – Adrian Aug 29 '16 at 22:27
6

I have been lately working on creating windows with transparency and click-through properties and I just tried this:

HWND hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TRANSPARENT, cName, wTitle, NULL, 0, 0, 640, 480, NULL, 0, GetModuleHandle(NULL), 0);

You can't close it, minimize it, drag it, etc - every click you make goes straight through as if it didn't exist. Then just change transparency using:

SetLayeredWindowAttributes(hWnd, 0, 100, LWA_ALPHA);

It achieves everything that in your question, if I understood it correctly.

Your approach might have not worked because WS_EX_LAYERED must be defined if you use WS_EX_TRANSPARENT.

Snackoverflow
  • 5,332
  • 7
  • 39
  • 69