It is impossible to use SetThreadDesktop and the VCL at the same time because a thread can only show windows on one desktop at a time. However VCL is not written for the use with multiple threads, so there is no way to show Delphi forms of same process on two different desktops. SetThreadDesktop describes this issue as followed.

The SetThreadDesktop function will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop).

Though there is a workaround to make VCL work with SetThreadDesktop and SwitchDesktop. The Application variable defined in unit Forms must be freed and then renewed. Here is the correct order :

  1. Close all your forms so the Application is going to quit.
  2. Free the Application instance in your DPR file.
  3. Then call SetThreadDesktop and SwitchDesktop
  4. Recreate the application object – now on the new desktop:
    Application := TApplication.Create(nil);
  5. Call all the usual stuff that has to be done to show your forms

…follow the same way as described to get back to the original desktop and forms (in this order!). However this has to be done in the main project file (DPR) (except you’re going to source it out) because you cannot just free Application in the event method of a button click. You have to shutdown your application (not your process) and continue right after :


In this way the process does not shut down and you can use VCL on one desktop at a time.

A second possibility is to use VCL on one desktop and NonVCL on the other. However in this way you have to create a new thread and use your own message loop (located in the new thread). Then you can call SetThreadDesktop in this thread and show the NonVCL windows.

A third possibility is to execute another or the same application with CreateProcess. This function allows you to specify the target desktop name for the new process in lpDesktop of the TProcessInformation structure.

uses JwaWindows;

  StartupInfo: TStartupInfo;
  ProcInfo : TProcessInformation;
  StartupInfo.cb          := SizeOf(StartupInfo);
  StartupInfo.lpDesktop   := ‘winsta0\default’;

    ‘appname.exe’,//__in_opt     LPCTSTR lpApplicationName,
    nil,//__inout_opt  LPTSTR lpCommandLine,
    nil,//__in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
    nil,//__in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
    true,//__in         BOOL bInheritHandles,
    CREATE_NEW_CONSOLE,//__in         DWORD dwCreationFlags,
    nil,//__in_opt     LPVOID lpEnvironment,
    nil,//__in_opt     LPCTSTR lpCurrentDirectory,
    StartInfo,//__in         LPSTARTUPINFO lpStartupInfo,
    ProcInfo,//__out        LPPROCESS_INFORMATION lpProcessInformation

Did you know that there is already a desktop class that provides all necessary function to administer desktops? Yes, there is! It is called TJwSecurityDesktop and resides in unit JwsclDesktops.

Tell me how you liked this blog entry by adding a comment.