Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Windows-service

Launching UI Application from Windows Service

4.83/5 (12 votes)
14 Jan 2016CPOL6 min read 73.2K   6K  
Windows Service that launches UI app
This tip provides a Windows Service that launches a UI application. The UI application runs under a specified user. The credentials of the user are saved encrypted in a configuration file.

Introduction

In Windows XP, Windows Server 2003 and earlier Windows versions, a Windows service could start an application with a user interface, and this application would be visible in the session of the currently logged-in user. In Windows Vista and later, Windows services run in Session 0 which does not allow any interaction with the user. A Windows Service may launch a UI application but this application will never be displayed on the currently logged-in user's desktop. This tip shows how to launch a UI application from a Windows Service by using the Windows API.

Background

This problem emerged when migrating a Cluster Resource from Windows Server 2003 to Windows Server 2012. In Windows Server 2003, a Cluster Resource could be a UI application. In Windows Server 2012, when bringing online the same Cluster Resource, the UI application does not become visible. This happens, because the Cluster Service is also a Windows Service, and the Cluster Resources launched by the Cluster Service are run in Session 0, which does not have user interaction. The workaround is to create a Windows Service that launches the UI application, and make this Windows Service a Cluster Resource. Only the Windows Service will be explained here.

Through the Windows API, it is possible to launch a new process in the currently interactive session. Only Windows Services that run under the local system account have the right to do that. However, the new process also runs under the local system account, whereas the requirement is for the new process to run under a specific user. The solution is for the new process to launch a second new process under a specific user. Once the second new process is started, the first new process terminates. At the end, the Windows Service and the second new process are running, the first in session 0 which is non interactive, and the latter in the currently interactive session. The Windows Service finds the process id of the second new process by the process name. When the service is stopped, it kills that process id. The Windows Service monitors if the process id exists, and stops if it no longer exists. The Windows Service can be made a Cluster Resource. When the second new process fails, the Windows Service will also stop and a Fail Over is initiated.

Using the Code

Two Visual Studio projects are provided:

  1. WindowsService1: Runs under the local system account, and starts WindowsApplication1 in the currently interactive session. Instructs WindowsApplication1 through the command line parameters to start Notepad.
  2. WindowsApplication1: Starts a new process given by the command line arguments. In this case, the new process is Notepad. The new process runs under a specific user, defined in the configuration file. When WindowsApplication1 is started without command line arguments, a form is shown to enter the user credentials and save them to the configuration file.

WindowsService1

Contains the following items:

  • WindowsApi: Contains the Windows API signatures of WTSGetActiveConsoleSessionId, WTSQueryUserToken, CreateProcessAsUser. You can get these at www.pinvoke.net.
  • Service1: Implements the Windows Service.
    • OnStart: Calls WTSGetActiveConsoleSessionId to get the current interactive session and puts it into the variable userTokenHandle with WTSQueryUserToken. Launches WindowsApplication1 with CreateProcessAsUserW. Passes as command line parameter "C:\Windows\System32\notepad.exe a b c". Calls GetProcId to get the process id that was created by WindowsApplication1. Starts the method MonitorProcess in a new thread.
    • GetProcId: Gets the process id with Process.GetProcessesByName, in this case it will be Notepad. If no such process is found, or more than one processes are found, an exception is thrown.
    • MonitorProcess: Checks with Process.GetProcessById if the process id still exists. If it does not exist, calls the Stop method of the service.
    • OnStop: Uses Process.Kill to kill the process id, in this case the Notepad process.
  • ProjectInstaller: This class is needed for installing the service.

WindowsApplication1

Contains the following items:

  • App.config: contains the credentials of the user under which the created process should run.
  • ModMain
    • Main: is the main entrypoint. If command line arguments exist, then ProcessStart is called, otherwise Form1 is shown.
    • ProcessStart: A new process is created. The user credentials are read from the configuration file with Configuration.ConfigurationManager.AppSettings. The password is set by calling SetPassword. The first of the command line arguments defines the executable to be started, and the rest of the command line arguments, define the command line arguments for the newly created process. The process is started with Process.Start.
    • SetPassword: Calls Decrypt to decrypt the password and fills a SecureString.
  • Form1: Provides textboxes to fill the domain, username and password. Upon pressing the button ok, the password is encrypted by calling Encrypt and the values are saved to the configuration file. The configuration file is opened with ConfigurationManager.OpenExeConfiguration and it is saved with Configuration.Save.
  • ModCrypto: provides methods for encryption and decryption.
    • Encrypt: Uses System.Security.Cryptography.ProtectedData.Protect to encrypt a string. The entropy makes guessing the password more difficult.
    • Decrypt: Uses System.Security.Cryptography.ProtectedData.UnProtect to decrypt a string.

Test

This program has been tested on Windows 7 and Windows Server 2012. It should run on Windows versions of Vista and later. To run the program, follow these steps:

  1. With Visual Studio 2010, open solution CreateAppFromService.sln.
  2. Build the solution.
  3. In Windows Explorer, double click on Source/bin/WindowsApplication1.exe. A form to enter the credentials is shown.

    User crdentials form.

  4. Enter the credentials under which the Notepad launched by WindowsApplication1 should run.
  5. Press Ok. The credentials are saved to bin/WindowsApplication1.exe.config. You may see that the Password is encrypted.
  6. In Windows Explorer, double click on Source/InstallService.bat. If the message "The transacted install has completed." is shown in the console, then Service1 has been installed successfully. Otherwise, try right-click and choose Run as administrator.
  7. Start/Run/services.msc. This starts the Services Management Console. Check that Service1 is installed.
  8. Right-click on Service1 and choose Start. The service's Status should change to Started.

    Services Management Console

  9. A Notepad should open asking: Cannot find the a b c.txt file. Do you want to create a new file?

    Notepad window

    You may click Yes or No. This is to demonstrate that parameters may be passed to the UI application.

  10. Start/Run/taskmgr to start the Windows Task Manager. Check that Notepad runs under the User Name entered in step 4. If this is the case, it seems that all worked well, that means, Windows Service "Service1" successfully started WindowsApplication1, which in turn started Notepad under the specified user.
  11. Right-click on Service1 and choose Stop. The Notepad should close and the service's Status should change to Stopped.
  12. Start again the Service1 to test the monitoring thread.
  13. Notepad should start again. Close the Notepad window.
  14. Right-click on Service1 and choose Refresh. The service's status should change to Stopped, because the monitoring thread found out that Notepad is no more running, and therefore the monitoring thread instructed the service to stop.
  15. If the service did not start, check the Application Log in the Event Viewer. You may start the event viewer with Start/Run/eventvwr.msc.
  16. You may find for example the following error:
    • Source: WindowsApplication1
    • Message: Main Logon failure: unknown user name or bad password.

      Event viewer

  17. In Windows Explorer, double click on Source/UnInstallService.bat. If the message "The uninstall has completed." is shown in the console, then Service1 has been uninstalled successfully. Otherwise, try right-click and choose Run as administrator. If this does not work either, in a command prompt type: sc delete Service1.

History

  1. WindowsService1 launching WindowsApplication1 launching Notepad with credentials in the configuration file encrypted by ProtectedData.Protect.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)