Application running itself on a separate desktop

2019-07-28 23:06发布

问题:

Trying to create a WPF caliburn micro application that creates a separate desktop and shows its main window there.

Problem: after the desktop is created and i switch there, no window is shown.

namespace WpfThreads
{
   class AppBootstrapper : Bootstrapper<WpfThreads.ViewModels.WpfThreadsViewModel>
   {
      protected override void OnStartup(object sender, StartupEventArgs e)
      {
          var desktop = Native.CreateDesktop("NewDesktop", 0, 0, 0, DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP, 0);
          Native.SetThreadDesktop(desktop);
          Native.SwitchDesktop(desktop);
          base.OnStartup(sender, e);
      }
   }
}

SetThreadDesktop() fails, other calls are successful. The OnStartup() method does run on main thread (which is also the UI thread).

回答1:

The SetThreadDesktop function will fail if the calling thread has any windows or hooks on the current desktop.

It's very unusual for a C# UI thread to not have any windows, because the project wizards mark Main() with [STAThreadAttribute], which causes a window to be created before any of your code runs. This window then gives your thread permanent affinity to the current desktop, preventing you from switching.

What you need to do to have both STA threading model and use a security desktop is to activate STA by hand.

First, create the desktop and activate it with SetThreadDesktop. This does not use COM or create any windows.

Then, set the COM threading model to STA:

Threading.Thread.CurrentThread.TrySetApartmentState(Threading.ApartmentState.STA);

This creates a window, permanently binding your UI thread to the desktop you just switched to.

After performing both these steps, you can safely start using other UI classes such as WinForms and WPF windows and widgets.

Unfortunately, this was only possible in .NET 1.x, which left COM uninitialized in a new thread until you set the threading model. Since 2.0, lack of STAThreadAttribute on Main() is interpreted as a request for MTA, and although that won't create a window that interferes with SetThreadDesktop, it does prevent changing to STA later. And new threads inherit their desktop from the process-creation options, not from the thread that spawns them, so you can't use an MTA thread to create and set the desktop and then spawn a STA thread to perform the UI work -- the STA thread won't end up in the new desktop.