Reputation: 91
I have designed a windows service that periodically checks whether a specific application is installed, and when it finds that it is not installed it downloads it from the shared network location, the file is a silent and unattended install exe (self installing).
I am having problems running the installer so I decided to instead of trying to run installer, run a small hello world windows forms app, just to see if this simple thing works.
After a couple of confused hours I finally discovered that the hello world app does in fact run but under a different user - specifically local machine. In the next couple of hours I found out that I had to turn off UAC (Vista/7) and allow the service to interact with the desktop. After this I finally got a prompt on my desktop that a service is trying to run something and that I had to decide whether to allow it or not.
When I press allow - I am taken to another GUI (different from my desktop) and the hello world runs normally thereafter.
Now, although this is certainly a progress I am still miles away from installing an app under the current user account.
One problem that I had was setting the windows service to run as a specific user, when I use installutil.exe on that kind of WS it prompts me for the user name and password, I enter the correct (admin privilige) data and it fails to install.
What I want to achieve is to have a windows service install a silent install package without interrupting the user in any way, the test silent install package is net framework 2.0 - I need that to install as if the user clicked on it himself.
I dont require any code (but this would be welcome), just point me in the right direction, thanks in advance!
Upvotes: 1
Views: 1014
Reputation: 244991
You'll find a sufficiently detailed explanation of why you're seeing the behavior your describe in my answers here and here, in case you're curious.
But regardless, the solution is to find an alternative approach. Windows Services can't display a user interface and they can't run in the context of a specific user. They're simply not a viable option for what you want to do. Disabling UAC is also not an acceptable option.
It's hard to imagine why you need to do this from a Windows Service in the first place. A background process that runs when a user logs in seems like the better approach. It doesn't have to have any interface at all, but it can still pop up a window or otherwise interact with the user when it so chooses.
Upvotes: 1
Reputation: 5940
I don't know how it is done in C#, but I'll explain the concept.
When you're in windows service you're application runs a SYSTEM user, because the application runs even before any login has been performed.
Also, from win7 local service accounts cannot display GUI because of major security issues like causing the application to run cmd.exe any kind of explorer that allows the user to perform things he/she doesn't suppose to do (I think you can bypass that, not sure, but it is not recommended anyways, so don't).
So you have 2 options:
create another application that will run under the user's session and you can communicate with it using RPC (like .Net remoting). You can place the application in the user's start-up.
The better way in my opinion, use CreateProcessAsUser() (I don't know how it is called in C#). Every user has a token, because you are a service, you have the permission to get the user's primary token that will allow you to create processes under the user's account. CreateProcessAsUser() creates a process and it also get a token to run the process as this user.
A few more things about tokens, to CreateProcessAsUser you need to get the user's token. The easiest way to do it from a windows service (and only windows service) is using WTSQuestUserToken. You give the function the sessionID of the user's session and you get back a primary token.
This method is NOT perfect, but it is easy and suitable for most cases. It won't work if there are 2 users under the same sessionID (sessionID == terminal server session ID), which can be done using Run As from explorer. But I think I got this answer enough complicated, so if you WTSQueryUserToken() is not good enough for you, let me know and I'll explain more thoroughly (and obviously it will be more complex).
Good Luck!
Upvotes: 1