This might seem to be an age old story, but I thought my experience is still worth sharing, for those who are yet to face it J….
While we were customizing the CSS CloudBuddy Personal for our customer, there was a requirement to add an automated scheduler that alerts the users on the tasks that they define at a specified time. The tasks can include anything like alerts on
- Backing up data to the cloud,
- Retrieving backed up data,
- Manipulating with the backed up data etc.
What I did:
I started working on a prototype model of this scheduler, which would do nothing but just throw a message box on the user’s desktop alerting him on the action that he might need to perform at that time. The goal was to have a working model which would do the tasks automatically upon the user’s approval and it involved processing of some complex GUI components.
What problems I experienced:
I had Windows XP installed while I developed this scheduler and I had no problem at all invoking the message box at the specified time, of course with “Allow service to interact with the desktop” enabled (NOT RECOMMENDED). I used it just for an experiment; it was a prototype, after all. The reason why I had to enable “Allow service to interact with the desktop” was because windows services are configured not to allow any GUI components being invoked from the service.
In Windows Vista and Windows 7, when my service tried to show a message box, the service controller popped up its own alert box which prompted me whether or not to show the GUI component (my message box)… I felt this was odd enough for me to accept this alert and then view the message that I wanted my service to show, because, going down the line, my scheduler would be required to do actions automatically without user interaction. And hence I started hunting the web on why there is this difference between a service in Windows XP and Windows Vista / Windows 7.
Alternative(s) that I could have implemented:
- Develop a normal windows application that alerts the user at the scheduled times.
- But this solution was not as good as how easy it is because this involves running the application in the user space all the time.
- Have this application run at windows startup. – But this option might serve to be disastrous because there is every chance that the user may remove it from startup accidentally.
- Do not alert the user – Just perform the tasks automatically! – Never a great solution if the requirement IS to alert the user before performing the task.
What solution I found:
In Windows XP, there was only one session in which all the applications would run, whether they are windows services or windows applications. However, from Windows Vista, Microsoft decided to separate windows services and hence programmed them to run isolated from other user space applications. For this to happen, there were 2 separate sessions (and subsequent sessions as and when many users log in to the same workstation) that were created:
- Session 0 – This is where the windows services would run.
- Session 1 (to n) – This is where the normal windows applications would run.
Read the following link for more information on session isolation: http://msdn.microsoft.com/en-us/library/bb756986.aspx
Hence, it was no longer easy to run a windows service that needs invoking GUI components. So what do we do? We need to invoke the GUI components as a desktop user. That might seem deadly difficult. Yes. It is indeed difficult, had we not had P/Invoke. Now that there is a provision in C# to invoke some of the system commands available in a set of C++ dll’s, nothing seems to be difficult at all… All you have to do is to call a few of these native methods that‘ll do the trick for you.
There is a method called CreateProcessAsUser() that can be invoked from a windows service. This method takes as input, the token information of the user that you wish to execute the process, the process name along with other parameters. A detailed C# implementation of this function can be found in this blog: http://veryblue.wordpress.com/code-snippets/pinvoke-createprocessasuser/
How I implemented the solution:
I developed a small windows application which on getting executed would show a message box that showed an appropriate message. From the windows service, I used CreateProcessAsUser() to execute this application and passed as arguments to the process, the message that I wanted to show. Of course, I could have used WTSendMessage() function to draw a message box on the user’s desktop. But as I had written earlier that this is just a prototype of a much complex scheduler that I was building which would, in future, involve many GUI components. Using CreateProcessAsUser() which is more versatile, saved me a lot of trouble.