4

So, I have an application. I want it to always sit exactly one z-level above a target application. It is just going to display a status message in the title bar, it's ugly but it's the requirement I have to meet.

Like this:

Desired effect

I looked into WM_NCPAINT and it's not really a feasible solution at this stage of the project (prototyping/proposals).

I've discovered the SetWindowPos() function within user32.dll:

[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, Swp uFlags);

And hook all z-level related events (of all applications) with:

SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_FOCUS, IntPtr.Zero, ZOrderChanged, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS | WINEVENT_SKIPOWNTHREAD);

And this seems to work to some extent. However, as I can only set to be directly behind the target app, I have to call SetWindowPos() twice, to achieve this, once to aquire the z-level of the target and once to swap the two around:

    private void ZOrderChanged(IntPtr hWinEventHook, uint eventType, IntPtr lParam, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        SetWindowPos(Handle, _foxViewHWnd, 0, 0, 0, 0, Swp.Noactivate | Swp.Nomove | Swp.Nosize);
        SetWindowPos(_foxViewHWnd, Handle, 0, 0, 0, 0, Swp.Noactivate | Swp.Nomove | Swp.Nosize);
    }

This is inefficient, flickers and reeks of code-smell, would anyone know how to avoid this?

ScottishTapWater
  • 3,656
  • 4
  • 38
  • 81
  • Aren't you looking for window ownership? – David Heffernan Feb 21 '18 at 13:33
  • Does Window ownership apply when my target application is something completely external? (Take notepad.exe for instance) – ScottishTapWater Feb 21 '18 at 13:39
  • 1
    Maybe https://blogs.msdn.microsoft.com/oldnewthing/20110331-00/?p=11083 https://blogs.msdn.microsoft.com/oldnewthing/20130412-00/?p=4683 of course it's a shame you didn't explain why you want to do this. Asking for a solution to a problem you didn't specify is sub optimal – David Heffernan Feb 21 '18 at 13:52
  • @DavidHeffernan please see my update, I'll have a look at your links – ScottishTapWater Feb 21 '18 at 13:56
  • Okay, so I've tried: `SetWindowLongPtr(_foxViewHWnd, -8, Handle);` which doesn't seem to work – ScottishTapWater Feb 21 '18 at 14:17
  • *"exactly one z-level above a target application"* - OS Windows doesn't have support for this. There are few groups of windows, e.g. topmost, which have certain z-relations to other groups. But organizing that "one z-level above" is not possible. Polling and re-adjusting is the only way. I'd suggest you to rethink you UI. – Sinatr Feb 21 '18 at 14:18
  • Seems surprising that Raymond Chen says one thing and you say the opposite. – David Heffernan Feb 21 '18 at 14:22
  • I've got source code in front of me that shows it's not working... I'm not saying the article is wrong, I'm sureI'm making a mistake but what I have is not working – ScottishTapWater Feb 21 '18 at 14:44
  • Well, time to do some debugging. Trying to work on this using C# is making life hard for yourself. You need a deep understanding of Win32 I suspect, and you should be doing your trials using C++ programs using the official SDK. Then you could see whether the issue is that you are modifying your window owner after it is created and whether or not you need to create the window with the right owner. – David Heffernan Feb 21 '18 at 14:46
  • "*just going to display a status message in the title bar*", try expanding on this. Why not just change the title of the target window? – Tewr Feb 21 '18 at 14:59
  • @Tewr please see my update – ScottishTapWater Feb 21 '18 at 15:12
  • Create a [global hook](https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx) so you are loaded into the target process, create your window and set the target window as owner. The OS will care about the z-order then. – zett42 Feb 21 '18 at 17:27
  • I think you better stick with `SetWinEventHook()`. What Raymond Chen is saying is obviously correct, but applies to another "world". So (partially) does the `SetWindowsHookEx()` @zett42 is referring to. All of this is correct, but that's C++ realm. If you change the Owner with `SetParent()` or `SetWindowLong()`, your Form will probably just disappear. While this works with .Net Forms, just setting the Owner using the handle of a foreign process is not sufficient. If you instead hook your Form to that process, all is very much simpler. The "overlay" can be placed and stay anywhere you want. – Jimi Feb 21 '18 at 23:04

0 Answers0