-2

So there are various questions on this topic already (from 4-5 years ago) and I have followed them to come up with the following solution to avoid my window reacting to Win+D (Show Desktop) Command:

public partial class MainWindow : Window
{

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindowEx(IntPtr hP, IntPtr hC, string sC, string sW);
    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    public MainWindow()
    {
        InitializeComponent();
        Loaded += OnWindowLoaded;
    }

    private void OnWindowLoaded(object sender, RoutedEventArgs e)
    {
        IntPtr nWinHandle = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Progman", null);
        nWinHandle = FindWindowEx(nWinHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
        SetParent(new WindowInteropHelper(this).Handle, nWinHandle);
    }
}

However this does not seem to work for me (the above code is in a brand new project).

Can anyone explain if there has been any change to the WinAPI, should this still work? This is the answer I come across on almost every question I find on this topic.

I am running:

  • Edition: Windows 10 Pro
  • Version: 21H2
  • Build: 19044.1645
  • Experience: Windows Feature Experience Pack 120.2212.4170.0,
Ross Patterson
  • 257
  • 1
  • 3
  • 10
  • 4
    When a user invokes the shortcut to show the desktop, chances are that they want to see their desktop. Doing the right thing doesn't require writing any code. Why do you believe that you need to write code to do the wrong thing? – IInspectable Apr 17 '22 at 16:01
  • The app I am creating is sort of a desktop widget (Desktop Organizer), therefore they want to see it on their desktop when they press Win+D. – Ross Patterson Apr 17 '22 at 16:04
  • Possible duplicate of https://stackoverflow.com/questions/65028303/pin-window-to-desktop-glue-window-to-desktop-always-on-bottom-window – Simon Mourier Apr 17 '22 at 19:08
  • @SimonMourier I tried the solution from that question (handling WM_SIZE & WM_SETFOCUS and adding the SWP_NOMOVE & SWP_NOSIZE flags) but to no avail. – Ross Patterson Apr 18 '22 at 07:44
  • You should add to your question everything you've tried – Simon Mourier Apr 18 '22 at 09:07
  • Sorry what I meant was I tried after you posted that link, but to no avail. – Ross Patterson Apr 18 '22 at 09:09

2 Answers2

0

Ok so I found the solution that works for me, I know setting the window as a child of the desktop has its problems/risks, but for people that still need to do it, here is what I found:

The "SHELLDLL_DefView" desktop window is not always a child of "Progman", sometimes it is a child of a "WorkerW" window instead. This is why the code in my original question didn't work for me.

So rather than finding the "Progman" window and finding the "SHELLDLL_DefView" child, you instead need to enumerate through all windows and find the window which has "SHELLDLL_DefView" as a child.

This is done with the EnumWindows function (https://www.pinvoke.net/default.aspx/user32.enumwindows)

The below code is a mashup of the one of the examples from the pinvoke.net link above and the following 2 answers:


    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr hP, IntPtr hC, string sC, 
    string sW);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList 
    lParam);

    public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);

    public static bool GetWindowHandle(IntPtr windowHandle, ArrayList 
    windowHandles)
    {
        windowHandles.Add(windowHandle);
        return true;
    }

    private void SetAsDesktopChild() 
    {
        ArrayList windowHandles = new ArrayList();
        EnumedWindow callBackPtr = GetWindowHandle;
        EnumWindows(callBackPtr, windowHandles);

        foreach (IntPtr windowHandle in windowHandles)
        {
            IntPtr hNextWin = FindWindowEx(windowHandle, IntPtr.Zero,     
            "SHELLDLL_DefView", null);
            if (hNextWin != IntPtr.Zero)
            {
                var interop = new WindowInteropHelper(_window);
                interop.EnsureHandle();
                interop.Owner = hNextWin;
            }
        }
    }

Now my WPF stays on the desktop after Show Desktop/Win+D as intended.

Ross Patterson
  • 257
  • 1
  • 3
  • 10
  • This boggles the mind. You've asked a question as a result of internals (presumably) having changed, only to provide a solution that's squarely focused on implementation details. There is absolutely *nothing* stopping any given module from registering a window class named `"SHELLDLL_DefView"`. And clearly, you are completely unaware of the consequences you [are now subject to](https://devblogs.microsoft.com/oldnewthing/20130412-00/?p=4683). Down-voted for *"works on my machine"* quality. – IInspectable Apr 19 '22 at 09:04
-1

There are some Windows operations that cannot be overridden by the developer, such as:

  • Always showing your tray icon on the main taskbar
  • Overriding the Win+D to always show your window on the desktop
  • Forcing notifications to always be shown

These are policies of the Windows operating system to ensure the user is always in control, and not the developer.

Having said that, you can try another approach to making your "widget" visible. Might I suggest the following:

A 1-second delay to show your widget is a small cost to your UX, in my opinion.

Tam Bui
  • 2,940
  • 2
  • 18
  • 27
  • Timers are bad mmkay, use a WinEvent hook – Anders Apr 17 '22 at 17:42
  • 2
    @Anders, please elaborate instead of being witty with Mr. Mackey mmkay comments. From my experience, WinEvent hooks in this case would be more costly as you would need to hook into every running process, and manage the hooks when someone opens any new apps. Please share a useful solution using WinEvent hooks. I'm sure many developers would be interested. – Tam Bui Apr 17 '22 at 17:55
  • Timers is a way of polling and polling is bad. `SetWinEventHook` does not hook into every process if you don't want it to. I don't know WPF so I can't really answer. A win32 app can probably detect being minimized without any hook or timer... – Anders Apr 17 '22 at 18:35
  • First of all, System.Threading.Timer is event-based, not polling based. Secondly, `SetWinEventHook` must hook into every process if you are not the owner of the other processes (i.e. every app window visible on the desktop), which is the case here. Third, we're talking about a desktop system here and not a smartphone (unless this dev is creating a defunct Win8 phone app?), so again 1-second timer is a small cost to pay for their desired UX. It shouldn't drain their battery at this low of an interval. – Tam Bui Apr 17 '22 at 18:50
  • 1
    Polling in a timer is still polling and 99.99% of the time, scheduling your thread for no reason. Timers and CPU sleep: https://devblogs.microsoft.com/oldnewthing/20220411-00/?p=106456 "And the recommendation is that inessential periodic timers have a minimum period of one minute" | WinEvent hooks have deep built-in support in the window manager, there is no extra code added anywhere. It has to deliver the event notification to your process, that is all. – Anders Apr 17 '22 at 18:59
  • You don't need WinEvent to catch your own window being minimized, `WM_SIZE` tells you (what the event in WPF is, I don't know). Since the OP is creating some form of desktop widget, other windows should not even matter. – Anders Apr 17 '22 at 19:41
  • Thanks for the clarification, Anders. Luckily, WPF can tap into the same event systems as the legacy Win32 APIs, so your solution would work best...assuming OP had no other way to minimize their window and assuming Win+D is the only way for them to do so. I'm sure this discussion can help others, so thanks for the solution. – Tam Bui Apr 17 '22 at 20:02