HOWTO: Access the Application Desktop from a Service

ID: Q173687

The information in this article applies to:

SUMMARY

The configuration of a Windows NT service determines how it may access the application desktop. The application desktop is named "default" and belongs to the "WinSta0" Window station object. The application desktop is associated with the interactively logged-on user. A process that has access to the application desktop can display message boxes, windows, and dialog boxes that can be seen visually by the interactively logged-on user. In addition, a process with access to the application desktop can send messages to other processes running on the desktop. The application desktop is not destroyed when the interactively logged-on user logs off.

NOTE: Running interactive services under the LocalSystem account is a VERY dangerous practice. This is especially true of the command processor and batch files. A user who wants to control the system can just hit CTRL+C to get an interactive system command prompt.

MORE INFORMATION

A service that is configured in the LocalSystem account and is interacting with the desktop (the service type includes the SERVICE_INTERACTIVE_PROCESS flag) has access to the application desktop.

A service that is configured in the LocalSystem account and is not interacting with the desktop does not have access to the application desktop by default. If the service needs to display information through a message box, you can do this by specifying one of the following two flag types:

   MB_DEFAULT_DESKTOP_ONLY - The message box will appear on the application
                             desktop, for example, "winsta0\default".
   MB_SERVICE_NOTIFICATION - The message box will appear on the currently
                             active desktop.

In addition, a service configured in the LocalSystem account can reconfigure its thread to access the application desktop. This is demonstrated in the following sample code. The sample code obtains handles to the "WinSta0" window station and the "default" desktop. Then it re- associates the current thread to the application desktop. Then the sample will display a message box and, when access to the interactive desktop is no longer needed, it resets the thread to the original window station and desktop. This must be done for every thread that wants access to the application desktop.

Sample Code

   BOOL ThreadInteract(void)
   {
      HDESK   hdeskCurrent;
      HDESK   hdesk;
      HWINSTA hwinstaCurrent;
      HWINSTA hwinsta;

      // 
      // Save the current Window station
      // 
      hwinstaCurrent = GetProcessWindowStation();
      if (hwinstaCurrent == NULL)
         return FALSE;

      // 
      // Save the current desktop
      // 
      hdeskCurrent = GetThreadDesktop(GetCurrentThreadId());
      if (hdeskCurrent == NULL)
         return FALSE;

      // 
      // Obtain a handle to WinSta0 - service must be running
      // in the LocalSystem account
      // 
      hwinsta = OpenWindowStation("winsta0", FALSE,
                                  WINSTA_ACCESSCLIPBOARD   |
                                  WINSTA_ACCESSGLOBALATOMS |
                                  WINSTA_CREATEDESKTOP     |
                                  WINSTA_ENUMDESKTOPS      |
                                  WINSTA_ENUMERATE         |
                                  WINSTA_EXITWINDOWS       |
                                  WINSTA_READATTRIBUTES    |
                                  WINSTA_READSCREEN        |
                                  WINSTA_WRITEATTRIBUTES);
      if (hwinsta == NULL)
         return FALSE;

      // 
      // Set the windowstation to be winsta0
      // 
      if (!SetProcessWindowStation(hwinsta))
         return FALSE;

      // 
      // Get the default desktop on winsta0
      // 
      hdesk = OpenDesktop("default", 0, FALSE,
                            DESKTOP_CREATEMENU |
                  DESKTOP_CREATEWINDOW |
                            DESKTOP_ENUMERATE    |
                            DESKTOP_HOOKCONTROL  |
                            DESKTOP_JOURNALPLAYBACK |
                            DESKTOP_JOURNALRECORD |
                            DESKTOP_READOBJECTS |
                            DESKTOP_SWITCHDESKTOP |
                            DESKTOP_WRITEOBJECTS);
   if (hdesk == NULL)
           return FALSE;

   // 
   // Set the desktop to be "default"
   // 
   if (!SetThreadDesktop(hdesk))
           return FALSE;

   // 
   // Do a message box
   // 
   MessageBox(NULL, "MB_OK", "test_interact", MB_OK);

   // 
   // Reset the Window station and desktop
   // 
   if (!SetProcessWindowStation(hwinstaCurrent))
           return FALSE;

   if (!SetThreadDesktop(hdeskCurrent))
      return FALSE;

   // 
   // Close the windowstation and desktop handles
   // 
   if (!CloseWindowStation(hwinsta))
      return FALSE;

   if (!CloseDesktop(hdesk))
           return FALSE;

      return TRUE;
   }

A service configured for a user account can access the application desktop through the message box types mentioned above. The above code will fail for a service configured for a user account due to security reasons but it will work for a service that impersonates the interactively logged-on user. For example, impersonation can be done through a named pipe connection from a process running on the application desktop. The service would have to impersonate the named pipe client.

Additional query words:

Keywords          : kbKernBase kbService kbGrpKernBase 
Version           : WINDOWS NT:3.51,4.0
Platform          : NT WINDOWS
Issue type        : kbhowto

Last Reviewed: April 11, 1998