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.
Desktop desktop = new Desktop();
desktop.Create("myDesktop");
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.
Desktop desktop = new Desktop();
desktop.Open("myDesktop");
desktop.Show();
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.
Desktop desktop = Desktop.OpenDesktop("myDesktop");
Process p = desktop.CreateProcess("calc.exe");
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.
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = m_desktopName;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
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 |
|
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
|
| You must Sign In to use this message board. |
|
|
 |
|
|
 |
 | Vista  pikipoki | 3:25 23 Jul '09 |
|
|
 |
|
|
 |
|
 |
Hi...I successfully switched to another desktop..But i'm not getting how to come back to previous default desktop...process is running but i can't see icon in the system tray when i switch to another desktop...how can i see icon there? Plz help...
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
|
 |
|
 |
hi!~ the application which is new created has run,but it don't display in any desktop.why? the code as follow:
public static void ProcessDesktop(string desktopName, string applicationName) { IntPtr hOriginalThread = LockDesktopAPI.GetThreadDesktop(AppDomain.GetCurrentThreadId());
IntPtr hOriginalInput = LockDesktopAPI.OpenInputDesktop(0, false, (uint)DESKTOP_ACCESS_RIGHT.AccessRights);
IntPtr hNewDesktop = LockDesktopAPI.CreateDesktop(desktopName.ToString(), IntPtr.Zero, IntPtr.Zero, 0, DESKTOP_ACCESS_MASK.GENERIC_ALL, IntPtr.Zero); LockDesktopAPI.SetThreadDesktop(hNewDesktop); StartOfficeViewer(desktopName, applicationName);
LockDesktopAPI.SwitchDesktop(hOriginalInput); LockDesktopAPI.SetThreadDesktop(hOriginalThread); LockDesktopAPI.CloseDesktop(hNewDesktop); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
When I downloaded the code initially, it was very neat and cool! but, geez, was it confusing. i didn't know why there was a public blank constructor with both public and then other static methods for creating / opening / showing / blah.
so, basically, i'm just trying to say that i've rearranged the original class to be a whole lot less confusing. .... umm, email me if you want it.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
hi all,
I want to create one windows service which will create newdesktop using Desktop class and then open browser in newdesktop using same service.
I build one windows service which opens newdesktop but not opening\creating process in that desktop.
Desktop desktop = new Desktop();
desktop.Create("myDesktop");
desktop = Desktop.OpenDesktop("myDesktop");
Process p = desktop.CreateProcess(@"C:\Program Files\Internet Explorer\iexplore.exe");
desktop.Show();
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Thank you. Without your sample, I never would have gotten it working.
This article has great utility, but the lack of sample code is seriously annoying.
Narf.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Instead of running a process in the new desktop, how about executing a worker thread from the same app and display a windows form?
Thanks.
"We don't see things how they are. We see things as we are." -Talmud
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Hello, I am an engg student,and designing the same application.I want am apllication which displays all the desktops with there windows opened.I want to retrieve the processes from each desktop.Needs help.
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
The API throws exceptions on the following client-side sequence:
Desktop d = new Desktop(); d.Open("abc"); d.Switch();
Without (supposedly tested) source code for the client-side, API wrappers like this one provide too little or no value in cases (as this one) where the implementation generates P/Invoke imbalances and associated execution errors.
Granted, the author's web-site posts an executable image with no test script to validate anything after installation, but again with no source code, so good luck to anyone trying it.
I believe postings like these should at least show client source code and/or some kind of validation script (text or code) before deserving publication in forums like these.
Erik Kreps
|
| Sign In·View Thread·PermaLink | 1.83/5 |
|
|
|
 |
|
 |
He GAVE you a set of wrappers and constructs for the native windows API saving loads of time doing all the research and writing. If you need to be spoon fed more than that, you shouldn't be mucking around with something like desktop switching.
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
|
 |
|
 |
I am trying to write a screen capture for an ASP.NET service.
It creates a browser instance, navigates to a site then captures that browser. The first problem I ran into was the absense of a desktop, which I can now create with this code. Great. I also create an STA thread and launch a modal dialog from it. Once the dialog appears I snap the desktop.
It's working on the current destktop, but doesn't work on a desktop that I create unless I switch to it - I get a black window - I presume it hasn't been painting. Any idea of how to capture an invisible desktop (force it to repaint?)
Thx dB.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
As far as im aware, paint signals arent sent to invisible desktops (which makes sense really). My attempts at getting it to paint windows on other desktops failed, if you are successful however, please let me know, I would be interested to see how you did it.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
|
 |
|
 |
Hi,it is possible logon different user on each desktop? If it can , I think it is possible to open same app on different desktop.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I dont think that would be possible, you would have to invoke the user switching mechanism in windows which I do not know how to do.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
First, I would like to thank you for this great article. I have a question: When switching desktops, the tray icons (the icons on the right of the taskbar) disappear. Is there any options to give to the SwitchDesktop or to the CreateDesktop function, to keep them for all different desktops ? Thank you.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Unfortunatly, no, the reason they disappear is because the new desktop is running a new instance of explorer.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Nnamdi,
Firstly great stuff buddy. I'm facing a problem and need your expertise. Here's my scenario:
I want to print a document say a pdf file to a file using a virtual printer driver say Microsoft Office Document Imaging. When I fire the process for printing, it displays the Save As file dialog asking for the output mdi filename. I can use SendKeys.SendWait(outputFilename + "~") to get rid of this dialog and the printing works just fine. My problem is, I want all this nasty stuff to happen in the background without the user even knowing about it.
That's where your cool DesktopAPI comes into play. I created a new desktop called printingDesktop and created a process in that desktop to do the printing stuff mentioned above. But for some reason, the SendKeys command doesn't seem to work until I switch the desktop to printingDesktop using printingDesktop.Show(). I show the desktop for a fraction of second and the print command works. But showing the desktop defeats the purpose for me as the whole idea is to do the printing in the background without any UI flickers.
Can I request you to put some light on what could be happening behind the scenes and why is the SendKeys command waiting for the desktop to be activated before it could process the keys? Any help would be immensely useful!!!
I'm using C# and .NET 2.0.
Regards, Viv
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
My guess would be that only the input desktop (which is the desktop the user is looking at) can recieve input (keyboard, mouse, etc.). It makes it harder to do cool things like you are planning, but from a security point of view, means a malicous coder cant make an app run in another desktop, recieving the users key input, or something.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks for your prompt reply Nnamdi; appreciate that!
I get your point. Do you think FindWindow and SendMessage API calls could be a workaround? What's say???
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Possibly, I do not know, I am inclined to say it wouldnt work seeing as you cant make a GUI'd application switch between desktops, I would be supprised if you cant find windows in a desktop other than the current one.
Sorry I couldnt be of more help.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
|