This article is devoted to the question about working with services and applications in Windows Vista. In particular we’ll consider how to start an interactive user-level application from the service and how to organize the data exchange between the service and applications. Solutions are given both for C++ and C#. This article might be useful for those who deal with the task of organizing the interaction between the service and the application on Windows Vista using both managed and native code.
1.Windows Vista, services and desktop
2. Starting interactive applications from the service
2.1 C++ code
2.2 C# code
3. Data exchange between the service and application
3.1 Text files
3.3 Named pipes
Before Vista appearance services and user applications in operating systems of Windows family could jointly use session 0. It was possible to easily open windows on the desktop of the current user directly from the service, and also to exchange data between the service and applications by means of window messages. But it became the serious security problem when the whole class of attacks appeared that used windows opened by the services to get access to the services themselves. The mechanism of counteraction to such attacks appeared only in Vista.
In Windows Vista all user logins and logouts are performed in the sessions that differ from the session 0. The possibility of opening windows on the user desktop by the services is very restricted, and if you try to start an application from the service it starts in session 0. Correspondingly if this application is interactive you have to switch to the desktop of session 0. The using of the window messages for data exchange is made considerably harder.
Such security policy is quite defensible. But what if nevertheless you need to start an interactive application on the user desktop from the service? This article describes one of the possible solution variants for this question. Moreover we’ll consider several ways of organization of data exchange between services and applications.
As soon as the service and desktop of the current user exist in the different sessions the service will have to “feign” this user to start the interactive applications. To do so we should know the corresponding login name and password or have the LocalSystem account. The second variant is more common so we’ll consider it.
So, we create the service with the LocalSystem account. First we should get the token of the current user. In order to do it we:
- get the list of all terminal sessions;
- choose the active session;
- get token of the user logged to the active session;
- copy the obtained token.
You can see the corresponding code in C++ below.
It should be mentioned that you can use WTSGetActiveConsoleSessionId() function instead of the looking over the whole list. This function returns the ID of the active session. But when I used it for the practical tasks I discovered that this function doesn’t always work while the variant with looking through the all sessions always gave the correct result.
If there are no logged in users for the current session then the function WTSQueryUserToken() returns FALSE with error code ERROR_NO_TOKEN. Naturally you can’t use the code given below in this case.
After we’ve got the token we can start an application on behalf of the current user. Pay attention that the rights of the application will correspond to the rights of the current user account and not the LocalSystem account.
The code is given below.
If the developed software will be used only in Windows Vista and later OSs then you can use CreateProcessWithTokenW() function instead of the CreateProcessAsUser(). It can be called for example in this way:
Let’s implement the same functionality in C#. We create the class ProcessStarter that will be used in some subsequent examples. Here I describe only two main methods.
Also we must mention that there is a very good article about the launching of the user-level application from the service with the LocalSystem account privivleges. It is located here: http://www.codeproject.com/KB/vista-security/VistaSessions.aspx
It remained only to solve the problem of data exchange between the service and applications. You can use a number of options: sockets, named memory mapped files, RPC and COM. Here we will consider the three easiest ways: text files, events (for C#) and named pipes (for C++).
One of the simplest solutions is to use text files. When we talk about C# development the most natural is to use XML files.
For example, we must pass some data string from the user-level application to the service. For a start, we must decide where the mediator file should be created. The location must be accessible both for the application and the service.
If the application was started with the permissions of the current logged-in user, the good solution would be to use the “My documents” folder of that user. In this case there will be no access problems from the both sides (as the LocalSystem service has the permissions to access almost everywhere).
So, let’s create the XML-file “sample.xml” in the current user’s “My Documents” folder:
Now we will create the “SampleElement” element which some useful data would be passed to:
Let’s finish the file creation:
And now the service must open that file. To have the access to it the service must get the current user’s “My Documents” folder path. In order to do it we should make an impersonation by means of the operation of getting the token described above:
Now the service can read the data from the “sample.xml” file:
Data exchange via the text files is very simple for implementation but it has a number of disadvantages. There can be not enough disk space; user can directly meddle in the data record process etc. So let’s consider another ways.
In the trivial case when we need to transmit only information of “yes/no” type (answer on the dialog window question, message if the service should be stopped or not and so on) we can use Events.
Let’s consider an example. The functioning of the some application “sample” should be paused in the certain point until the service gives a command to continue.
The “sample” application is started from the service by means of the already considered class ProcessStarter (in C#):
Now we create the global event SampleEvent in that point of the “sample” application where it should stop and wait for the command from the service. We stop the thread until the signal comes:
We open the global event SampleEvent in that point of the service where it’s necessary to send the command to the application. We set this event to the signal mode:
Application gets this signal and continues its functioning.
If we talk about big volumes of data in exchange process we can use named pipe technology. We must mention that the code below is provided in C++ as the classes for working with the named pipes in C# were introduced only since .NET Framework 3.5. If you want to know how to use these new .NET tools for working with the named pipes you can read, for example, this article
Let’s suppose that an application periodically needs to send some number of unsigned int type to the service.
In this case we can open the named pipe on the service side and then monitor its state in the separated thread to read and process data when they come. So we create the pipe DataPipe in the service code:
We also create the function to check the thread state and perform reading if required:
And finally start the separate thread with ThreadFunction() function:
Now we go to the application side and organize sending of data to the service via named pipe.
There is no one and only right solution for the problem of interaction between services and applications in Windows Vista. There are a lot of mechanisms and you should choose the proper one in correspondence with the concrete problem. Unfortunately a lot of variants of such interaction organization were left behind this article scope. The usage of some of such technologies in the terms of C# are discussed, for example, in this article: http://www.codeproject.com/KB/threads/csthreadmsg.aspx
To learn this question deeper and a lot of features of developing for Windows Vista we also recommend the book by Michael Howard, David LeBlanc - Writing Secure Code for Windows Vista (Microsoft Press, 2007).