1

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!

BrunoBozic
  • 91
  • 1
  • 1
  • 6
  • I'm not sure what the question is, but Windows Services, Screen savers and other system processes run in a different UI context or different desktop. As I remember, it's possible to make the GUI run in the context of the logged in user (if there is one). – kenny Apr 02 '11 at 11:11

2 Answers2

1

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:

  1. 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.

  2. 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!

TCS
  • 5,790
  • 5
  • 54
  • 86
  • The management thought that this would be the best solution - I was against it right from the start. Meanwhile I did manage to stress out to management that since time is the #1 issue here we simply have to refactor the entire logic. – BrunoBozic Apr 04 '11 at 07:45
  • Hence, I am deploying a little application as an unattended silent Inno setup package, this helper app adds a scheduled event (on user logon) to task scheduler - that starts the application I need. – BrunoBozic Apr 04 '11 at 07:47
  • Another added benefit being - the ability to push such a package (Inno Setup) through VBScript over many machines. So basically we achieved the same thing by using a different method. Of course the management wants me to continue investigating the issue with windows services as those are still a point of interest to them, for some reason they see them as something 100% reliable (as opposed to task scheduler for an example) – BrunoBozic Apr 04 '11 at 07:49
  • In our specific case, our users will not be using "run as" because they dont know how to do this (which turns out to be good for us) and because the currenty policy forbids them from doing this (at least it should be doing that, gotta ask the admin guy). Therefore - WTSQueryUserToken ought to be enough to meet our needs. Sigh..win32API again...mixed feelings about it - on one hand it works like a charm, rock steady, well documented and stuff, on the other hand pinvoking is a little bit of a drag, especially if you dont have the method sig listed on pinvoke net :) – BrunoBozic Apr 04 '11 at 07:52
  • Why not adding some c++.net dlls to the project? It allows an easy mixture of native and managed code. That means you don't need all that p/invoke stuff and just include the proper headers! :-) – TCS Apr 04 '11 at 17:25
1

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.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • I agree. Especially on the disabling the UAC part. – BrunoBozic Apr 04 '11 at 07:54
  • Someone thought that windows services are the best place to place an application that checks a network drive for an exe and if the exe meets certain reqs, downloads it and installs it. We dont have AD here so the management wants us to design a way of deploying apps over the network. They designed the specification where a windows service periodically checks the network location for a file, downloads it if it needs to and installs it (another consideration was running a GUI with a progress bar for this). – BrunoBozic Apr 04 '11 at 07:56
  • @Bruno: Unfortunately, the person who thought that is *wrong*. It's not going to work. This is what happens when you let people who don't know anything about programming develop your specifications. Specs that are completely disconnected from the real world don't do anyone any good, even those who wrote them. It's simply **impossible** to have a GUI (with or without a progress bar) on a Windows Service. That's not what they're designed for. Reading your other comments, I'm even more convinced that a background process is the answer. Implement that instead; write your own spec if necessary. – Cody Gray - on strike Apr 04 '11 at 09:23