Click here to Skip to main content
Click here to Skip to main content

Desktop Switching

By , 7 Jul 2004
 

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

About the Author

Nnamdi Onyeyiri
United Kingdom United Kingdom
Member
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'

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberma.vicente21 May '13 - 6:20 
Thank you very much !
Question[Windows 7 screensaver issue:- My custom desktop hides windows default screen saver in Windows7] [modified]membershijuck21 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:54.

QuestionVB.NET versionmemberjmiguy16 Jul '12 - 11:10 
Is there a VB.NET version available?
AnswerRe: VB.NET versionmembercellurl20 Nov '12 - 10:26 
I have some code but it doesn't all work. Here you go....
 
Option Strict Off
 
Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.Text
 
Module Module1
 
    Private Declare Function CreateProcessA Lib "kernel32.dll" (ByVal lpApplicationName As String, _
                                                                                  ByVal lpCommandLine As String, _
                                                                                  ByRef lpProcessAttributes As Int32, _
                                                                                  ByRef lpThreadAttributes As Int32, _
                                                                                  ByVal bInheritHandles As Int32, _
                                                                                  ByVal dwCreationFlags As Int32, _
                                                                                  ByVal lpEnvironment As IntPtr, _
                                                                                  ByVal lpCurrentDriectory As String, _
                                                                                  ByRef lpStartupInfo As STARTUPINFO, _
                                                                                  ByRef lpProcessInformation As PROCESS_INFORMATION) As Integer
 
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
    Structure STARTUPINFO
        Public cb As Int32
        Public lpReserved As String
        Public lpDesktop As String
        'Public lpDesktop As Byte()
        Public lpTitle As String
        Public dwX As Int32
        Public dwY As Int32
        Public dwXSize As Int32
        Public dwYSize As Int32
        Public dwXCountChars As Int32
        Public dwYCountChars As Int32
        Public dwFillAttribute As Int32
        Public dwFlags As Int32
        Public wShowWindow As Int16
        Public cbReserved2 As Int16
        Public lpReserved2 As Int32
        Public hStdInput As Int32
        Public hStdOutput As Int32
        Public hStdError As Int32
    End Structure
 

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure PROCESS_INFORMATION
        Public hProcess As IntPtr
        Public hThread As IntPtr
        Public dwProcessId As Int32
        Public dwThreadId As Int32
    End Structure
 
    Private Structure STARTUPINFOW
        Dim cbSize As Long
        Dim lpReserved As Long
        Dim lpDesktop As Long
        Dim lpTitle As Long
        Dim dwX As Long
        Dim dwY As Long
        Dim dwXSize As Long
        Dim dwYSize As Long
        Dim dwXCountChars As Long
        Dim dwYCountChars As Long
        Dim dwFillAttribute As Long
        Dim dwFlags As Long
        Dim wShowWindow As Integer
        Dim cbReserved2 As Integer
        Dim lpReserved2 As Long
        Dim hStdInput As Long
        Dim hStdOutput As Long
        Dim hStdError As Long
    End Structure
 
    Private Property lastError As Integer
 

 

    Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Int32) As Int32
    Private Declare Function GetThreadDesktop Lib "user32" (ByVal dwThread As Int32) As Int32
    Private Declare Function GetCurrentThreadId Lib "kernel32" () As Int32
 
    Private Declare Function OpenInputDesktop Lib "user32" ( _
          ByVal dwFlags As Long, _
          ByVal fInherit As Boolean, _
          ByVal dwDesiredAccess As Long _
       ) As Long
 
    Private Declare Function SetThreadDesktop Lib "user32" (ByVal hDesktop As Int32) As Int32
    Private Declare Function CloseDesktop Lib "user32" (ByVal hDesktop As Long) As Long
    Dim GENERIC_ALL As IntPtr = &H10000000
    Private Const DESKTOP_SWITCHDESKTOP = &H100&
    Private Const STILL_ACTIVE = &H103
    Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
    Private Const INFINITE As Long = &HFFFFFFFF       '  Infinite timeout

    ' To Report API errors:
    Private Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100&
    Private Const FORMAT_MESSAGE_ARGUMENT_ARRAY = &H2000&
    Private Const FORMAT_MESSAGE_FROM_HMODULE = &H800&
    Private Const FORMAT_MESSAGE_FROM_STRING = &H400&
    Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000&
    Private Const FORMAT_MESSAGE_IGNORE_INSERTS = &H200&
    Private Const FORMAT_MESSAGE_MAX_WIDTH_MASK = &HFF&
    Private Declare Function FormatMessageW Lib "kernel32" ( _
        ByVal dwFlags As Long, lpSource As Object, _
        ByVal dwMessageId As Long, ByVal dwLanguageId As Long, _
        ByVal lpBuffer As Long, ByVal nSize As Long, _
        Arguments As Long) As Long
 
    Private Const ERR_BASE As Long = 40670
 
    Enum PROCESS_CREATION_FLAG As Integer
        CREATE_BREAKAWAY_FROM_JOB = &H1000000
        CREATE_DEFAULT_ERROR_MODE = &H4000000
        CREATE_NEW_CONSOLE = &H10
        CREATE_NEW_PROCESS_GROUP = &H200
        CREATE_NO_WINDOW = &H8000000
        CREATE_PROTECTED_PROCESS = &H40000
        CREATE_PRESERVE_CODE_AUTHZ_LEVEL = &H2000000
        CREATE_SEPARATE_WOW_VDM = &H800
        CREATE_SHARED_WOW_VDM = &H1000
        CREATE_SUSPENDED = &H4
        CREATE_UNICODE_ENVIRONMENT = &H400
        DEBUG_ONLY_THIS_PROCESS = &H2
        DEBUG_PROCESS = &H1
        DETACHED_PROCESS = &H8
        EXTENDED_STARTUPINFO_PRESENT = &H80000
        INHERIT_PARENT_AFFINITY = &H10000
    End Enum
 

    Private Declare Function OpenDesktop Lib "user32" Alias "OpenDesktopA" _
          (ByVal lpszDesktop As String, _
           ByVal dwFlags As IntPtr, _
           ByVal fInherit As Boolean, _
           ByVal dwDesiredAccess As IntPtr) As IntPtr
 
    Private Declare Function CreateDesktop Lib "user32" Alias "CreateDesktopA" _
        (ByVal lpszDesktop As String, _
        ByVal lpszDevice As String, _
        ByVal lpDevMode As Int32, _
        ByVal dwFlags As Int32, _
        ByVal dwDesiredAccess As Int32,
        ByVal lpSecAttrib As Int32) As Int32
 
    Private Declare Function GetLastError Lib "coredll.dll" () As Int32
    Private Declare Function SwitchDesktop Lib "user32" (ByVal hDesktop As Int32) As Int32
 
    Private m_sDesktop As String
    Private m_hDesktopThreadOld As Int32
    Private m_hDesktopInputOld As Int32
    Private m_hDesktop As Int32
    Private m_hDesktop2 As Int32
    Private lR As Int32
 
    Public Declare Unicode Function CreateProcessWithLogonW Lib "Advapi32" (ByVal lpUsername As String, ByVal lpDomain As String, ByVal lpPassword As String, ByVal dwLogonFlags As Int32, ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal dwCreationFlags As Int32, ByVal lpEnvironment As IntPtr, ByVal lpCurrentDirectory As String, ByRef si As STARTUPINFO, ByRef pi As PROCESS_INFORMATION) As Integer
    Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntPtr) As Integer
 
    Public Const LOGON_WITH_PROFILE As Int32 = &H1
 
    Public Const NORMAL_PRIORITY_CLASS As Int32 = &H20&
 
    Public Const STARTF_USESHOWWINDOW As Int32 = &H1
    Public Const SW_HIDE As Int16 = 0
    Public Const SW_SHOW As Int16 = 5
    Dim strCurrentDirectory As String = "."
    Const desktop_name As String = "myDesktop"
 

    Const SW_NORMAL As Int16 = 1
    Const STARTF_USESTDHANDLES As Int32 = &H100
    Const UOI_NAME As Int32 = 2
    Const STARTF_USEPOSITION As Int32 = &H4
    Const DESKTOP_CREATEWINDOW As Int32 = &H2
    Const DESKTOP_ENUMERATE As Int32 = &H40
    Const DESKTOP_WRITEOBJECTS As Int32 = &H80
    Const DESKTOP_CREATEMENU As Int32 = &H4
    Const DESKTOP_HOOKCONTROL As Int32 = &H8
    Const DESKTOP_READOBJECTS As Int32 = &H1
    Const DESKTOP_JOURNALRECORD As Int32 = &H10
    Const DESKTOP_JOURNALPLAYBACK As Int32 = &H20
    Const AccessRights As Int32 = DESKTOP_JOURNALRECORD Or DESKTOP_JOURNALPLAYBACK Or DESKTOP_CREATEWINDOW Or DESKTOP_ENUMERATE Or DESKTOP_WRITEOBJECTS Or DESKTOP_SWITCHDESKTOP Or DESKTOP_CREATEMENU Or DESKTOP_HOOKCONTROL Or DESKTOP_READOBJECTS
 
    Public Sub startcalc()
 
        Dim pi As New PROCESS_INFORMATION
        Dim si As New STARTUPINFO
 
        si.cb = Marshal.SizeOf(si)
        si.dwFlags = STARTF_USESHOWWINDOW 'Or STARTF_USESTDHANDLES
        si.wShowWindow = SW_SHOW                           'SW_HIDE  SW_SHOW            like 'Normal Hidden Minimized Maximized 
        'si.lpDesktop = ""
        si.lpDesktop = desktop_name
 
        System.Windows.Forms.MessageBox.Show("Press Return")
 
        Dim cmd As String
        'cmd = "Calc.exe"
        cmd = "C:\WINDOWS\SYSTEM32\Calc.exe"
 

        Dim result As Integer
        'result = CreateProcess(Nothing, cmd, IntPtr.Zero, IntPtr.Zero, True, NORMAL_PRIORITY_CLASS, IntPtr.Zero, Nothing, si, pi)
        'result = CreateProcess(Nothing, cmd, IntPtr.Zero, IntPtr.Zero, True, NORMAL_PRIORITY_CLASS, IntPtr.Zero, Nothing, si, pi)
        result = CreateProcessA(cmd, Nothing, IntPtr.Zero, IntPtr.Zero, True, NORMAL_PRIORITY_CLASS, IntPtr.Zero, "c:\windows\system32\", si, pi)
 
        'Works with default desktop
        'result  = CreateProcess(cmd, Nothing, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, si, pi)

        'result  = CreateProcess("C:\WINDOWS\SYSTEM32\Calc.exe", Nothing, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, si, pi)
        'result  = CreateProcess("Calc.exe", Nothing, IntPtr.Zero, IntPtr.Zero, False, 0, IntPtr.Zero, Nothing, si, pi)
   
        'result  = CreateProcess("calc.exe", "calc.exe", IntPtr.Zero, IntPtr.Zero, True, NORMAL_PRIORITY_CLASS, IntPtr.Zero, Nothing, si, pi)
    End Sub
 
    Public Sub run()
 
        m_hDesktopThreadOld = GetThreadDesktop(GetCurrentThreadId())
 
        'm_hDesktop = CreateDesktop(desktop_name, "", IntPtr.Zero, IntPtr.Zero, GENERIC_ALL, IntPtr.Zero)

        m_hDesktop = CreateDesktop(desktop_name, IntPtr.Zero, IntPtr.Zero, 0, AccessRights, IntPtr.Zero)
 
        If Not (m_hDesktop = 0) Then
            lR = SwitchDesktop(m_hDesktop)
            Thread.Sleep(1000)
            lR = SetThreadDesktop(m_hDesktop)
            If (lR = 0) Then
                lastError = Marshal.GetLastWin32Error()
            End If
            m_sDesktop = desktop_name
 
            Thread.Sleep(1000)
 
        End If
 
        startcalc()
 
        System.Windows.Forms.MessageBox.Show("wheedoggy")
 
        'Dim proc As New System.Diagnostics.Process()

        'With proc.StartInfo
        '    '.FileName = "c:\windows\system32\cmd.exe"
        '    '.Arguments = "/C pause"
        '    .FileName = "C:\Users\mylittle.exe"
        'End With
        'proc.Start()

        Thread.Sleep(5000)
 

        SwitchDesktop(m_hDesktopThreadOld)
        SetThreadDesktop(m_hDesktopThreadOld)
 
    End Sub
 
    Sub Main()
 
        'startcalc()

        Dim th As System.Threading.Thread
        th = New Thread(AddressOf run)
        th.Start()
 
        Thread.Sleep(5000)
    End Sub
 

End Module

QuestionI needed to change dwDesiredAccess type from long to int, and CreateDesktop()'s extern, to make this work [modified]memberJ.Guyette21 Jul '11 - 10:33 
I had to change dwDesiredAccess's type from a long to an int, and change the CreateDesktop() extern to...
 

[DllImport("user32", EntryPoint = "CreateDesktopW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags,
int dwDesiredAccess, IntPtr lpsa);

 
BTW, I'm using Vista 32

modified on Saturday, July 23, 2011 4:10 PM

QuestionImproved Close() [modified]memberJ.Guyette21 Jul '11 - 10:14 
Closing can be a problem depending on the design of the program. I think an improved approach to Close() is to add a SwitchDesktop() that switches back to the the original desktop. This way, even if the desktop doesn't close, the original desktop is still restored.
 
I added an "IntPtr m_origDesktop" instance variable and set it in the Create() method using "m_origDesktop = OpenInputDesktop(0, false, DESKTOP_SWITCHDESKTOP);" (after "if (m_desktop != IntPtr.Zero)"). Then in the Close method (after CheckDisposed()) I added "if (m_origDesktop != IntPtr.Zero) SwitchDesktop(m_origDesktop);".

modified on Thursday, July 21, 2011 4:25 PM

QuestionWhere am I doing wrong?memberChelifero7 Jun '11 - 22:52 
First of all thanks for this article. Big Grin | :-D
I've read messages posted about this article and I've noticed that all people can use class Desktop without problems except me!!! Can someone tell me why? Frown | :(
1. I'm using IDE Visual Studio express 2008 C#
2. I've created a new solution with a new project (console application)
3. In the main method of the class Program I've used the Desktop class
Desktop desktop = new Desktop();            
desktop.Open("Billy");            
desktop.Show();
4. I don't have any errors and build succeeded.
5. When I try it with F5 or ctrl+F5 I see cmd shell but nothing else.
6. I've also try to run a process but... nothing
 
Ideas? thanks Nnamdi
GeneralMy vote of 5membershahshi2 Feb '11 - 23:21 
Very nice..But i am not getting how to show the desktopmnager window after switching another desktop.
GeneralRe: My vote of 5memberJ.Guyette1 Aug '11 - 11:26 
Call Desktop instance method CreateProcess("explorer.exe");
Generalnicememberalejandro29A27 Jan '11 - 8:12 
but please, don't use #regions like that...
you are a programmer, so, you want to show your code, not hiding it behind little boxes.
Dios existe pero duerme...
Sus pesadillas son nuestra existencia.
(Ernesto Sabato)

Generalnicely writtenmemberhsmcc5 Jan '11 - 3:10 
It hardly makes something that is still useful in coding world after a few years. This is one.
GeneralAcitve Directorymembers.kammonah5 Oct '10 - 23:34 
The program is not working on active directory any ideas please?
Generalmy vote of 5memberdl4gbe1 Aug '10 - 7:46 
Exatly what I need. A class for a second desktop. Smile | :) Big Grin | :-D
GeneralMy vote of 5memberJaykul19 Jul '10 - 4:06 
Awesome.
Fired it up via Add-Type in PowerShell and instantly had a PowerShell-based multiple-desktop switcher. Very cool Smile | :)
GeneralLink to your websitememberjase_0243 Apr '10 - 22:32 
Thank you very much for this great article! However, I'm unable to download the sample application from your website as the link seems to be broken, and when i go to your homepage and then try to navigate to that page containing the demo app, the "code" menu item on your site doesn't work. Is there another way for me to obtain your demo application? Thank you once again for your article Smile | :)
General[My vote of 2] It doesn't workmemberMember 332889013 Jan '10 - 23:00 
On Win XP SP2 it fails.
 
Calling to
private static extern IntPtr OpenDesktop(string lpszDesktop, int dwFlags, bool fInherit, long dwDesiredAccess);
returns 0000 -> error
 
Missing GetLastError in this class, I added
[DllImport("Kernel32")]
internal static extern long GetLastError();
to get an error code
 
Error number was 2 -> "File not found"
 
I think this code was never tested...
GeneralMy vote of 2memberblak3r27 Aug '09 - 7:40 
Well written article, but doesn't work.
GeneralVistamemberpikipoki23 Jul '09 - 2:25 
Has anybody managed to get this working under Vista?
GeneralRe: Vistamemberblak3r27 Aug '09 - 7:39 
I couldn't get it working on any OS about a month ago. I also tried downloading the application from the author's site and that too gave me the same errors. This one works but it's C++: Virtual Desktop: A Simple Desktop Management Tool[^]
 
http://www.blakerobertson.com

GeneralRe: VistamemberJaykul19 Jul '10 - 4:07 
I haven't tried it on Vista, but it works great on Window 7 Big Grin | :-D
::Jaykul <><
Lynch's Law: When the going gets tough, everyone leaves.

QuestionHow to cme back to previous default desktopmemberrasika1110 Apr '09 - 5:33 
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...
AnswerRe: How to cme back to previous default desktopmemberjase_0242 Apr '10 - 21:20 
lol........ you can check out the code on the author's website where he demonstrates how to do this quite easily, infact. Hope you get your desktop back! Smile | :)
Generalway cool -- thanks for the API wrappermemberradink29 Jan '08 - 10:08 
Just wanted to say thanks for the API wrapper. it's just what I was looking for.
Great work.
QuestionWhy didn't display the application windows form in new desktop?memberdeeloo16 Dec '07 - 20:44 
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)
{
// Save original ...
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);

//LockDesktopAPI.SwitchDesktop(hNewDesktop);
StartOfficeViewer(desktopName, applicationName);
 
LockDesktopAPI.SwitchDesktop(hOriginalInput);
LockDesktopAPI.SetThreadDesktop(hOriginalThread);
LockDesktopAPI.CloseDesktop(hNewDesktop);
}

GeneralClearing Up the CodememberIan MacLean4 Oct '07 - 16:29 
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.

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 8 Jul 2004
Article Copyright 2004 by Nnamdi Onyeyiri
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid