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.
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:
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.
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.
Contains the following items:
WindowsApi: Contains the Windows API signatures of
CreateProcessAsUser. You can get these at www.pinvoke.net.
Service1: Implements the Windows Service.
WTSGetActiveConsoleSessionId to get the current interactive session and puts it into the variable
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.
Process.Kill to kill the process id, in this case the Notepad process.
ProjectInstaller: This class is needed for installing the service.
Contains the following items:
- App.config: contains the credentials of the user under which the created process should run.
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
Decrypt to decrypt the password and fills a
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
ModCrypto: provides methods for encryption and decryption.
System.Security.Cryptography.ProtectedData.Protect to encrypt a
string. The entropy makes guessing the password more difficult.
System.Security.Cryptography.ProtectedData.UnProtect to decrypt a
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:
- With Visual Studio 2010, open solution CreateAppFromService.sln.
- Build the solution.
- In Windows Explorer, double click on Source/bin/WindowsApplication1.exe. A form to enter the credentials is shown.
- Enter the credentials under which the Notepad launched by
WindowsApplication1 should run.
- Press Ok. The credentials are saved to bin/WindowsApplication1.exe.config. You may see that the Password is encrypted.
- 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.
- Start/Run/services.msc. This starts the Services Management Console. Check that
Service1 is installed.
- Right-click on
Service1 and choose Start. The service's
Status should change to
- A Notepad should open asking: Cannot find the a b c.txt file. Do you want to create a new file?
You may click Yes or No. This is to demonstrate that parameters may be passed to the UI application.
- 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.
- Right-click on
Service1 and choose Stop. The Notepad should close and the service's Status should change to Stopped.
- Start again the
Service1 to test the monitoring thread.
- Notepad should start again. Close the Notepad window.
- 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.
- 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.
- You may find for example the following error:
- Message: Main Logon failure: unknown user name or bad password.
- 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.
WindowsApplication1 launching Notepad with credentials in the configuration file encrypted by