Click here to Skip to main content
Click here to Skip to main content
Go to top

Desktop Switching

, 7 Jul 2004
Rate this:
Please Sign up or sign in to vote.
An article showing how desktop switching can be achieved

Introduction

Having a spare computer available, I decided to try out Mandrake Linux, just to see what its like (having never used linux before). I thought its desktop switching ability was interesting, and began wondering if it was possible to do the same thing on my Windows box. A quick search on MSDN revealed the Desktop API functions, which allowed me to do just that. According to the docs, this functionality has been available since Windows 2000 Professional, and since I'm using XP, I thought I would give it a go.

When Windows loads, and you are logged in, a desktop is automatically created, called "default". Using the functions provided you can create, open, and switch desktops, unfortunately, deletion isn't provided (more on that later).

Using the Desktop class

Creating a Desktop

Creating a desktop is a very easy task, as the code example below shows. Desktops can be created using either the instance, or the static members provided.

// Instance method
Desktop desktop = new Desktop();
desktop.Create("myDesktop");
 
// Static method
Desktop desktop = Desktop.CreateDesktop("myDesktop");

Note: Desktop names cannot contain backslash characters.

Using either of the methods will result in a Desktop object, with an open handle to the desktop (provided the desktop was created successfully). New desktops have no processes running on them at all - and if you were to switch to it, all you would see is the default wallpaper. I have provided the Prepare method which loads explorer to the desktop, making it usable.

Switching Desktops

Switching desktops is just as easy as creating the desktop, all that is required is a call to either the static method stating the desktop name, or the instance method of an open Desktop object.

// instance method.
Desktop desktop = new Desktop();
desktop.Open("myDesktop");
desktop.Show();
 
// static method.
Desktop.Show("myDesktop");

Opening Processes in Desktops

The CreateProcess function in kernel32.dll takes a parameter of type STARTUPINFO, which has a parameter ("lpDesktop") which allows you to specify the desktop the process is to be created in. As a result, I chose to import this function, instead of using the .NET frameworks Process class, meaning reduced functionality when creating process, but I hope to resolve this in the next release. Now the code, this example shows the two ways of creating a process.

// instance method
Desktop desktop = Desktop.OpenDesktop("myDesktop");
Process p = desktop.CreateProcess("calc.exe");
 
// static method
Process p = Desktop.CreateProcess("calc.exe", "myDesktop");

A Process object is returned from both CreateProcess methods, which can be used for killing the process, or however you see fit.

Deleting Desktops

Deleting a desktop is a little trickier. The only way to delete a desktop is to kill all processes running on it, at which point, it is automatically deleted. So far, I have been unable to get a list of processes running on a desktop other than the input desktop (nor am I certain this is possible), but what I have done, is provided the GetInputProcesses method, which will return an array of all the processes running on the current input desktop (the desktop visible to the user).

With this in mind, the only way to delete a desktop which has been accessed by the user is to be on the desktop, enumerate the processes, and kill them one-by-one. Making sure your application isn't killed off before it has a chance to jump desktops (unless you want it that way).

Process[] processes = Desktop.GetInputProcesses();
Process thisProc = Process.GetCurrentProcess();
foreach(Process p in processes)
{
   if (p.ProcessName != thisProc.ProcessName)
   {
      p.Kill();
   }
}

Settings a Thread's Desktop

Threads of your process can be moved between desktops, provided they do not have any hooks or windows in the current desktop.

Desktop desktop = Desktop.OpenDesktop("myDesktop");
Desktop.SetCurrent(desktop);

The code example above would move the calling thread into "myDesktop", but would fail if the thread has a window or a hook.

Behind the Scenes

Desktop's desktop switching functionality is achieved using the following API functions (imported from user32.dll):

  • CreateDesktop
  • OpenDesktop
  • OpenInputDesktop
  • CloseDesktop
  • SwitchDesktop
  • SetThreadDesktop
  • GetThreadDesktop

Creating a Desktop

[DllImport("user32.dll")]
private static extern IntPtr CreateDesktop(string lpszDesktop,
                                           IntPtr lpszDevice,
                                           IntPtr pDevmode,
                                           int dwFlags,
                                           long dwDesiredAccess,
                                           IntPtr lpsa);

The CreateDesktop function is imported from user32.dll. It is called when you attempt to create a new desktop, with only the desktop name (lpszDesktop), flags (dwFlags) and desired access rights (dwDesiredAccess) being specified.

m_desktop = CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, 
    AccessRights, IntPtr.Zero);

The return value is a handle to the desktop, or IntPtr.Zero if an error occurred.

Switching Desktops

[DllImport("user32.dll")]
private static extern bool SwitchDesktop(IntPtr hDesktop);

The SwitchDesktop function is imported from user32.dll. When switching desktops, the handle to the desktop you want to become the input desktop is passed as the only parameter, the value returned indicates if the switch was successful.

bool result = SwitchDesktop(m_desktop);

Opening Processes in Desktops

[DllImport("kernel32.dll")]
private static extern bool CreateProcess(
   string lpApplicationName,
   string lpCommandLine,
   IntPtr lpProcessAttributes,
   IntPtr lpThreadAttributes,
   bool bInheritHandles,
   int dwCreationFlags,
   IntPtr lpEnvironment,
   string lpCurrentDirectory,
   ref STARTUPINFO lpStartupInfo,
   ref PROCESS_INFORMATION lpProcessInformation);

The CreateProcess function is imported from kernel32.dll. When creating a process in a desktop, this function is called with only the command line (lpCommandLine), inherit handles (bInheritHandles), creation flags (dwCreationFlags), startup into (lpStartupInfo) and process information (lpProcessInformation) specified, as shown in the code below.

// set startup parameters.
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = m_desktopName;
 
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
 
// start the process.
bool result = CreateProcess(null, path, IntPtr.Zero, IntPtr.Zero, true,
    NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref si, ref pi);

Specifying a PROCESS_INFORMATION structure allows my to retrieve the process ID of the process that was just created, allowing a managed Process object to be created for it.

return Process.GetProcessById(pi.dwProcessId);

Setting a Thread's Desktop

[DllImport("user32.dll")]
private static extern bool SetThreadDesktop(IntPtr hDesktop);

The SetThreadDesktop function is imported from user32.dll. A call to SetCurrent would result in a call to this function. The Desktop object passed to SetCurrent, if open, will contain a valid desktop handle, accessible via the DesktopHandle property, which is passed to SetThreadDesktop.

return SetThreadDesktop(desktop.DesktopHandle);

Example usage

I have not provided an example application with the source, as it is very straight forward, and all members (except private) are XML commented. However, if you would like to see how this could be used in an application, try my website, where I have used it to make a small desktop switching application.

History

Version

Comments

1.0
  • Initial release
1.1
06 Jun 2004
  • Added Window and WindowCollection classes
  • Added another GetWindows overload, that used WindowCollection
  • Added GetInputProcesses method to retrieve processes on Input desktop
  • Changed GetWindows and GetDesktops to return arrays, instead of them being passed by ref.
1.2
08 Jul 2004
  • Implemented IDisposable
  • Implemented ICloneable
  • Overrided ToString to return desktop name

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Nnamdi Onyeyiri

United Kingdom United Kingdom
Nnamdi was born in 1986, all his life he has been into anything electrical (starting with plug sockets as soon as he could crawl) from there it moved onto dismantelling things to see what they looked like on the inside. Eventually this interest moved on to computers.
 
Started simple, with HTML, and moved on to JS. Then moved onto C#, he's all over the shop, doing programming(ish) type things, web designing [My Website] and graphics, although he cant draw to save his life.
 
He likes to use his computer, socialise with his mates, and laugh at his mates as they all try to apply for holiday jobs, while he has an office job in London, as a network administrator, all because he wowed his boss when he went there on work experience.
 
He goes by the alias 'TheEclypse'

Comments and Discussions

 
QuestionFixes for Windows 7 [modified] Pinmembersamidi25-Aug-14 20:38 
GeneralMy vote of 1 PinmemberMember 104087879-May-14 5:25 
GeneralRe: My vote of 1 PinprofessionalEddy Vluggen6-Jun-14 0:31 
Questionhow about in windows 8 Pinmemberytfrdfiw3-Dec-13 18:00 
QuestionQuestion related to close desktop Pinmemberashug9215-Nov-13 4:31 
AnswerRe: Question related to close desktop Pinmemberashug9219-Nov-13 4:08 
GeneralMy vote of 5 Pinmemberma.vicente21-May-13 6:20 
Question[Windows 7 screensaver issue:- My custom desktop hides windows default screen saver in Windows7] [modified] Pinmembershijuck21-Jan-13 21:45 
I have a problem with windows screensaver.
 
My screensaver settings is On and timeout is 60 seconds.
 
I have created my own desktop using CreateDesktop() API of windows SDK. And switch the desktop to my own desktop.
 
here is the code.
 
DWORD dwThreadId = ::GetCurrentThreadId ( ) ;
HDESK m_hUserDesktop = 0;
HDESK m_hOrigDesktop = ::GetThreadDesktop ( dwThreadId ) ;
 
m_hUserDesktop = ::CreateDesktop ( _T("MyNewDesktop"),
0,
0,
0,
GENERIC_ALL,
0 ) ;
::SwitchDesktop ( m_hUserDesktop );
 
Sleep( 70000 );
 
::SwitchDesktop ( m_hOrigDesktop );
::CloseDesktop( m_hUserDesktop );
 
The problem is MyNewDesktop hides windows default screen saver in Windows7. It always displayed behind MyNewDesktop screen.
I can see the screensaver if I switch the desktop to the original one. That means screensaver appears after 60 seconds but not comes topmost.
 
Issue only in Windows7, It works fine in XP.
shijuck


modified 22-Jan-13 3:54am.

QuestionVB.NET version Pinmemberjmiguy16-Jul-12 11:10 
AnswerRe: VB.NET version Pinmembercellurl20-Nov-12 10:26 
QuestionI needed to change dwDesiredAccess type from long to int, and CreateDesktop()'s extern, to make this work [modified] PinmemberJ.Guyette21-Jul-11 10:33 
QuestionImproved Close() [modified] PinmemberJ.Guyette21-Jul-11 10:14 
QuestionWhere am I doing wrong? PinmemberChelifero7-Jun-11 22:52 
GeneralMy vote of 5 Pinmembershahshi2-Feb-11 23:21 
GeneralRe: My vote of 5 PinmemberJ.Guyette1-Aug-11 11:26 
Generalnice Pinmemberalejandro29A27-Jan-11 8:12 
Generalnicely written Pinmemberhsmcc5-Jan-11 3:10 
GeneralAcitve Directory Pinmembers.kammonah5-Oct-10 23:34 
Generalmy vote of 5 Pinmemberdl4gbe1-Aug-10 7:46 
GeneralMy vote of 5 PinmemberJaykul19-Jul-10 4:06 
GeneralLink to your website Pinmemberjase_0243-Apr-10 22:32 
General[My vote of 2] It doesn't work PinmemberMember 332889013-Jan-10 23:00 
GeneralRe: [My vote of 2] It doesn't work Pinmembersamidi25-Aug-14 20:48 
GeneralMy vote of 2 Pinmemberblak3r27-Aug-09 7:40 
GeneralVista Pinmemberpikipoki23-Jul-09 2:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 8 Jul 2004
Article Copyright 2004 by Nnamdi Onyeyiri
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid