0

My application is a Winforms transparent overlay attached to another application.

Since there can be multiple overlays running at one time for different apps, I need my overlay to be always on top, but only over its target application.

What I tried was to bring the form to the foreground with

[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(int hWnd);   

When target app gains focus through my WindowHook eventType == NativeMethods.SWEH_Events.EVENT_OBJECT_FOCUS.

The problem is that the overlay gains focus after going on top, not allowing me to interact with the app underneath. Like if I try to drag the target app it will block me at first, and if I try to click anywhere nothing happens despite being clickthrough (works when I set Winforms TopMost = true;).

I would consider setting the form topmost when target app gains focus, but there is no event to check for loss of focus to undo the topmost, and even then it would be less than an elegant solution, to say the least.

There must be a simpler way to keep my overlay on top of its target app process. Or the very least, a way to bring the form on top without gaining focus / disrupting my mouse events.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 1
    Consider [SetWindowPos](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos). [GetWindow](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindow) may also prove useful. – NetMage Apr 27 '22 at 20:13
  • 1
    You could use the method shown here: [Move window when external application's window moves](https://stackoverflow.com/a/48812831/7444103) to *attach* your Window to another, also handle `EVENT_SYSTEM_FOREGROUND`, use `GetWindowPlacement()` to determine its state and `SetWindowPos()` to show your Window without activation (you can also override `ShowWithoutActivation` and add `WS_EX_NOACTIVATE` to the extended styles in `CreateParams`). – Jimi Apr 27 '22 at 20:27
  • Or set `MA_NOACTIVATE` when you get a [WM_MOUSEACTIVATE](https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-mouseactivate) message to prevent mouse activation (I don't remember which one works better in which System and Window Styles right now; to test) – Jimi Apr 27 '22 at 20:34
  • @Jimi Thanks I did actually use your post on how to attach a form to a window before I ran into this issue. SetWindowPos() doesn't bring it to foreground, is there some parameter I need to pass? WS_EX_NOACTIVATE doesn't work with SetForegroundWindow as mouse actions are still stopped. EVENT_SYSTEM_FOREGROUND is a better option that EVENT_OBJECT_FOCUS so I'll use that from now on. – Derek Bushiko Apr 28 '22 at 18:41
  • @Jimi `SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);` according to pinvoke uising 0 is meant to bring it to top. Could it be that WinEventCallback() method from your code catches events like EVENT_SYSTEM_FOREGROUND _before_ process resolves theirs? – Derek Bushiko Apr 28 '22 at 19:12
  • You need `SWP_ASYNCWINDOWPOS` and `SWP_NOACTIVATE`; anyway, I don't know how / when you're calling this function. -- `EVENT_SYSTEM_FOREGROUND` is better handled using a dedicated delegate. This event notifies when a Window becomes the foreground Window (right after). – Jimi Apr 28 '22 at 19:49
  • @Jimi I'm calling it the same way you are in your example. `WinEventDelegate = WinEventCallback;` – Derek Bushiko Apr 28 '22 at 19:59
  • I was referring to `SetWindowPos()`, not the `SetWinEvenHook()` delegate. – Jimi Apr 28 '22 at 20:13
  • @Jimi I'm calling SetWindowPos() from inside WinEventCallback() on EVENT_SYSTEM_FOREGROUND. But it's okay, I can make it work as is, thanks for taking time to look into this! – Derek Bushiko Apr 28 '22 at 20:24

1 Answers1

0

So EVENT_SYSTEM_CAPTUREEND = 0x0009 gets called after any interaction with the target process window. This means I can set my form topmost

SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); and undo it with SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); on EVENT_SYSTEM_CAPTUREEND. I'm not a fan of this solution but works and it's seamless.