7

I am writing an application in C# that needs to run as a service but also have user interaction. I understand that services have no UI, etc, so I've divided up my program into a windows form application and a service that can communicate with each other.

The problem I'm having is that I need the service to make sure the windows form application is always running and restart it if it is not. I'm able to detect if it is running, and restart it with the following code on Windows 2000/XP:

System.Diagnostics.Process.Start("ExePath");

but on Vista, it runs the new process as a Local/System process which is invisible to the user. Does someone way around this? Is there some way to detect which user is currently logged on and run the new process as that user? I don't need to account for fast-user switching at this point. Something - anything - basic would suffice.

I would be grateful for any help or tips you have on the subject.

I need to clarify that I am setting the "Allow service to interact with desktop" option when the service is installed. This is what allows it to work on 2000/XP. However, Vista still has the aforementioned problem.

Kara
  • 6,115
  • 16
  • 50
  • 57
Andrew Ensley
  • 11,611
  • 16
  • 61
  • 73
  • Am I missing something? If you have a service that is always running... why does it matter if your windows form application is running? – bobwienholt Jan 06 '09 at 21:02
  • Because I need a way for the service to communicate with the user directly. There's a lot of reasons for this, but the most basic example is for upgrade notices. – Andrew Ensley Jan 06 '09 at 21:07
  • 1
    I'm with the others here... I suspect that you are taking the wrong approach to whatever problem you are trying to solve. If you *must* have a UI then you likely shouldn't be using a service model. Perhaps something that resides in the system tray...? – Sailing Judo Jan 06 '09 at 21:08
  • My form application actually does reside in the system tray. I'm open to ideas if I'm taking the wrong approach. So far, I have not been convinced of that. I need the service to be able to communicate directly with the user when certain events happen. Is there a better way to do this? – Andrew Ensley Jan 06 '09 at 21:10

8 Answers8

12

The general idea for this sort of thing is, if the user needs to interact with a service, they should launch a separate application. If you want to help them out, you can configure that separate application to start with windows by placing a shortcut in the start up menu. You can also build crash recovery into your application so it can automatically restart.

You shouldn't really rely on monitoring the forms application, what if no one is logged in? What if multiple people are logged in? It just gets messy doing things this way.

Having the service just sit there and broadcast to listeners is the way to go. When the forms application starts it can notify the service it wants to listen to events.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
Bob
  • 97,670
  • 29
  • 122
  • 130
  • As I mentioned in my comments above, I need the service to be able to notify the user of certain events directly. I'll be happy one of two ways: if there's a way to resolve the problem I described, or if there's a better way to accomplish this goal. – Andrew Ensley Jan 06 '09 at 21:12
  • 1
    You should have your forms application start up automatically. Then it will notify the service it is listening for updates. – Bob Jan 06 '09 at 21:16
  • I guess I'll have to accept that. I was really hoping there was a way to relaunch it from the service. – Andrew Ensley Jan 06 '09 at 21:21
  • This is considered the "proper" way to do things. If you wanted to be sneaky you could have an application running in the background. Use tactics to hide that and its only job would be to keep the form app alive. – Bob Jan 06 '09 at 21:26
  • 10
    I despise apps that always run and hide themselves in the background. I'm looking at you adobe, google updater, quicktime, and java. Bastards, the lot of you. –  Jan 06 '09 at 21:41
  • In all other cases I would agree with you Will, but the nature of my program means that users will try to hack it or stop it. That's why I wanted to be able to relaunch the process. The important work is done in the service, and it's secured in other ways, but I want that form running too! Oh well – Andrew Ensley Jan 06 '09 at 21:50
  • Have two apps in user space: the forms app and a hidden app. The forms app makes sure the hidden app is running and the hidden app makes sure the forms app is running. – Jeff Cuscutis Jan 06 '09 at 22:11
  • To your credit, Bob. I understood exactly that from you. I will probably have to do this eventually. Thank you for your help. – Andrew Ensley Jan 06 '09 at 22:27
3

See the question: How can a Windows Service execute a GUI application?. It addresses the same question from C/C++ (short answer: CreateProcessAsUser), but the answer's still valid (with some P/Invoke) for C#.

Community
  • 1
  • 1
Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380
  • Thanks. After looking at that answer, I think I'll go with the hidden background process approach mentioned by many helpful answers/comments. – Andrew Ensley Jan 06 '09 at 22:17
2

In this case, you will have to have a third monitor process which detects if the program fails and restart it in that case.

However, you end up with an unsolvable problem here, as the monitor process will have to be watched to make sure it doesn't get shut down, and so on, and so on, and so on.

You might want to reconsider this approach.

casperOne
  • 73,706
  • 19
  • 184
  • 253
1

To have your service run the application as a user (which seems to be what you are trying to do) you need to do the following:

System.Security.SecureString ss = new System.Security.SecureString();

foreach (char c in password)
  ss.AppendChar(c);

System.Diagnostics.Process proc = Process.Start(path, arguments, username, ss, domain);

Where:

  • path = full path (including filename) of the executable.
  • arguments = string of arguments (use an empty string is none)
  • username = The name of an user account on your server/computer
  • domain = your network domain (if your using a network account- blank if none)

Also, In order for your service to have permission to launch an application, it must be running as a service also. To do this, you need to add these lines to your service installer class:

serviceProcessInstaller.Account = ServiceAccount.User;

serviceProcessInstaller.Username = "yourdomain\\yourAccountName"; //Or just "AccountName" for local accounts..            

serviceProcessInstaller.Password = "yourPassword";
takrl
  • 6,356
  • 3
  • 60
  • 69
1

Its a tough situation. As mentioned in a couple places, if you must have a UI then technically you shouldn't be using a service. Afterall, services run without a user even logged on. If nobody is logged in, you cannot have a UI.

Normally, when I need a service needs to communicate with the outside world, there are two things I opt for. I can either place an entry in the event log, or I can drop a message in a queue.

In your case I'd use a queue. When a user logs in, you can auto start an app for them that monitors the queue. If the app is running, when the message is received, they are alerted that way as well. However, if the user closes the app then the same thing occurs... they won't know.

Sailing Judo
  • 11,083
  • 20
  • 66
  • 97
1

First, a quick answer: Does the 'Allow service to interact with desktop' option (service -> Properties -> LogOn) or specifying an account allow what you're wanting? If so, both of these can be configured on your service installer class.

Like the others, I suspect there is a better approach to this and either one of the following is true: -The code inside the service could be included in the winforms app (perhaps running in a background thread), and added to windows startup. Both will be running -The winforms app can just listen to the service when it's on, and doesn't need to be started from the service. Or similarly, the app could be added to startup.

Daniel
  • 1,516
  • 1
  • 13
  • 24
  • I've edited my original question. I have been setting that option when the service is installed. Thanks for the suggestion. I will probably explore both options. I just wish it was possible to do what I'm asking. It would make my life so much easier. – Andrew Ensley Jan 06 '09 at 21:45
0

In Windows 2000 and XP, there is an option (checkbox) on the Logon tab of the service properties window to allow the service to interact with the desktop. I believe this is what you are looking for. I just wrote a quick service in VB.NET with a Process.Start("calc.exe") and Windows Calculator opened just fine.

I'm not 100% sure this works the same way in Vista though.

Rich
  • 896
  • 6
  • 15
  • I should have been clearer in my question. I have done this. This is what allows it to launch the forms application on 2000/xp. However, Vista opens the program as a local system process and it is invisible to the user. – Andrew Ensley Jan 06 '09 at 21:34
0

Sounds like you might not need half of it running as a service (unless there's a requirement of higher privileges), as your service would need to cope with when there is no interactive user logged on as well.

Rowland Shaw
  • 37,700
  • 14
  • 97
  • 166
  • Thanks, but I do need it to be a service for privileges. I am accounting for situations when the forms app is not running or there's no user logged on (which are logically the same for right now). – Andrew Ensley Jan 06 '09 at 21:29