Win32API — back to Windows Desktop

Time:2021-10-16

This article shares the implementation method of returning to the desktop function, and the effect is the same as the shortcut key (Win + D).

Implementation method

There are many ways to realize the function of returning to the desktop in windows. You can simulate shortcut keys or perform the following methods. The first method needs to reference shell32.dll. The method is to add a reference. Select com, find “Microsoft shell controls and automation”, select and confirm, and set its embedded interoperability type to false.

//Method 1, [reference link]( https://stackoverflow.com/questions/41598951/programmatically-show-the-desktop )
Shell32.ShellClass objShel = new Shell32.ShellClass();
objShel.ToggleDesktop();

//Method 2, [reference link]( https://social.msdn.microsoft.com/Forums/vstudio/en-US/a27ca1e4-bd02-434b-8d02-06553c35f3d5/show-desktop-program-no-working )
Type shellType = Type.GetTypeFromProgID("shell.application");
object shell = Activator.CreateInstance(shellType);
shellType.InvokeMember("ToggleDesktop", BindingFlags.InvokeMethod, null, shell, new object[] { });

problem

Under normal circumstances, both methods can be executed successfully.

However, today we encountered a device that failed to operate successfully. The scenario is that the WPF application fails to return to the desktop when it receives a UDP message.

See some netizens say that when executing the above code, it needs to be executed in sta thread, otherwise an error will be reported. The first method needs to be executed in sta thread, but it can not solve this problem.

When analyzing the problem again, it is found that when the WPF application is the current active window, the operation is executed successfully, otherwise the execution fails. Therefore, activating the window first and then executing the above code can successfully solve the problem.

On the faulty device, it is not possible to use the simple show() and active() methods to activate the window. It only flashes the icon in the taskbar and uses theThe method of activating the window provided by the bossCan be activated successfully.

The difficulty of this problem is that not all devices have this problem. I have two devices with the same operating system, but one is good and the other is not. The faulty device code is executed. I don’t know why it doesn’t work. You must set the application as an active window. If you have any partners who understand the problem, welcome to discuss it.

Part of the code of this test demo is as follows, which can be seen in detailGithub

//WPF main window
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        InitLogger();
        InitUdpThread();
        showDesktop = Method1;
        Logger.LogMessage(Severity.Info, $"start process, Main Thread id: {Thread.CurrentThread.ManagedThreadId}");
    }

    private void InitLogger()
    {
        var file = new FileLogger("log.txt");
        Logger.LogMessage(Severity.Info, "Init logger success");
    }

    private void InitUdpThread()
    {
        Thread udpThread = new Thread(new ThreadStart(GetUdpMessage));
        udpThread.IsBackground = true;
        udpThread.Start();
    }

    private void GetUdpMessage()
    {
        UdpClient udpClient = null;
        try
        {
            udpClient = new UdpClient(10001);
        }
        catch (Exception)
        {
            Logger.LogMessage(Severity.Error, "create udp client failed");
            return;
        }
        Logger.LogMessage(Severity.Info, "create udp client success");

        IPEndPoint remotePoint = null;
        while (true)
        {
            try
            {
                byte[] receiveData = udpClient.Receive(ref remotePoint);
                string receiveString = Encoding.Default.GetString(receiveData);
                Logger.LogMessage(Severity.Info, $"receive udp message: {receiveString}");

                if (receiveString.ToLower().Contains("showdesktop"))
                    showDesktop?.Invoke();
            }
            catch (Exception e)
            {
                Logger.LogMessage(Severity.Error, e.Message);
            }
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn)
        {
            switch (btn.Name)
            {
                case "method1":
                    showDesktop = Method1;
                    Logger.LogMessage(Severity.Info, "turn to method1");
                    break;
                case "method2":
                    showDesktop = Method2;
                    Logger.LogMessage(Severity.Info, "turn to method2");
                    break;
                case "activeFirst":
                    showDesktop = ActiveFirst;
                    Logger.LogMessage(Severity.Info, "turn to activeFirst method");
                    break;
                default:
                    break;
            }
        }
    }

    private void Method1()
    {
        Thread newSta = new Thread(()=>
        {
            Shell32.ShellClass objShel = new Shell32.ShellClass();
            objShel.ToggleDesktop();
            Logger.LogMessage(Severity.Info, $"Current Thread id: {Thread.CurrentThread.ManagedThreadId}");
        });
        newSta.TrySetApartmentState(ApartmentState.STA);
        newSta.Start();
    }

    private void Method2()
    {
        Type shellType = Type.GetTypeFromProgID("Shell.Application");
        object shellObject = System.Activator.CreateInstance(shellType);
        shellType.InvokeMember("ToggleDesktop", System.Reflection.BindingFlags.InvokeMethod, null, shellObject, null);
        Logger.LogMessage(Severity.Info, $"Current Thread id: {Thread.CurrentThread.ManagedThreadId}");
    }

    private void ActiveFirst()
    {
        App.Current.Dispatcher.Invoke(new Action(() =>
        {
            Win32Api.SetWindowToForegroundWithAttachThreadInput(this);
            Method2();
        }));
    }

    private Action showDesktop;
}