3

I am a little new to the Microsoft world. I have read this answer and many other links and I'm aware that after Vista, it is not possible for windows service to interact with desktop.

However, I have kind of an emergency where I have to find a workaround quick. I need my windows service to somehow open a browser with a URL (any kind of an ugly hack will do for now). The answer states :

you need to write a separate agent application, which will be automatically started on user login, and with which your service will communicate. Then the agent can start the browser or do whatever else you need.

Can someone please explain to me how I could do this in a simple way? How would that service talk to this 'agent'? What is this 'agent' exactly?

Any links or suggestions would be highly appreciated.

EDIT: Currently. my service tries to run the following code: System.Diagnostics.Process.Start("www.google.com");

which, I found out later, does not work

Community
  • 1
  • 1
Ayrton Senna
  • 3,735
  • 5
  • 34
  • 52

4 Answers4

6

Yes, from a service you can run code on the desktop... The matter is on which desktop? because there may be various users logged at the same time (for example in a Windows Server there may be various users logged remotely).

If use Process.Start from a service as in your example:

System.Diagnostics.Process.Start("www.google.com");

The program will not appear in the desktop of any user, because the service is not associated with any of the desktops of the users.


The solution is to detect which user is logged locally and then execute the program as that user (and under the privileges of that user). For that I want to link you to another of my answers where I demonstrate how to invoke a program from a service. In that answer I present the code needed for this technique.

Note: That answer was about an screen capture, still the technique I describe is to call a program in the session of an user, and that method works from a service. So it applies to your problem.

You can use this method to execute the desired browser directly. As an alternative you can use it to invoke your service executable, where you can use System.Environment.UserInteractive to detect if the executable is not running as a service or not. If you pass the url as execution argument to your service executable then you can open it with the default browser using Process.Start just like your example above.

Note: The code was tested on Windows 7.

Community
  • 1
  • 1
Theraot
  • 31,890
  • 5
  • 57
  • 86
4

There is no "quick way" to do this, I'm afraid. (And since the service can't interact with the user's desktop, even if it could launch the browser the user couldn't see it anyway.)

The "agent application" you've mentioned is a totally separate application which runs under the user's desktop on login. It would have to use some method of inter-process communication (named pipes, TCP/IP, etc.) to communicate with the service.

The reason that services don't interact with the desktop is for reasons of security. There's no "quick workaround" available, and there shouldn't be one. There's also no "simple way" to circumvent that change (and again, there shouldn't be - if you can bypass security in a simple way, it's not much security).

Ken White
  • 123,280
  • 14
  • 225
  • 444
  • I had the same problem but this link helped me: http://asprosys.blogspot.ro/2009/03/allow-service-to-interact-with-desktop.html – Cornel Urian Dec 12 '13 at 13:17
2

use shellexecute using pinvoke http://www.pinvoke.net/default.aspx/shell32.shellexecute

C# Signature:
public enum ShowCommands : int
{
    SW_HIDE         = 0,
    SW_SHOWNORMAL       = 1,
    SW_NORMAL       = 1,
    SW_SHOWMINIMIZED    = 2,
    SW_SHOWMAXIMIZED    = 3,
    SW_MAXIMIZE     = 3,
    SW_SHOWNOACTIVATE   = 4,
    SW_SHOW         = 5,
    SW_MINIMIZE     = 6,
    SW_SHOWMINNOACTIVE  = 7,
    SW_SHOWNA       = 8,
    SW_RESTORE      = 9,
    SW_SHOWDEFAULT      = 10,
    SW_FORCEMINIMIZE    = 11,
    SW_MAX          = 11
}

[DllImport("shell32.dll")]
static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);





 // Asks default mail client to send an email to the specified address.
    ShellExecute( IntPtr.Zero, "open", "mailto:support@microsoft.com", "", "", ShowCommands.SW_SHOWNOACTIVATE    );

    // Asks default browser to visit the specified site.
    ShellExecute( IntPtr.Zero, "open", "http://channel9.msdn.com", "", "", ShowCommands.SW_SHOWNOACTIVATE );

    // Opens default HTML editing app to allow for edit of specified file
    ShellExecute( IntPtr.Zero, "edit", @"c:\file.html", "", "", ShowCommands.SW_SHOWNOACTIVATE );
   //Modified by Aljaz: Replaced the last zero in these calls with 4  otherwise it wouldn't show anything
   // 0 stands for SW_HIDE contant, which means execute but don't show the window which is probably not 
   // what we want.
Nahum
  • 6,959
  • 12
  • 48
  • 69
  • This looks simple. I'm surprised I did not come across this anywhere in my quick research. Any drawbacks or something that I am missing? I will give this a try – Ayrton Senna Mar 24 '13 at 06:53
  • 2
    @AyrtonSenna "Any drawbacks"? Yes: Since you are running from a service, the program invoked with shellexecute will not be in the desktop of the user, therfore it will not be visible. By the way, Proccess.Start is the equivalent of shellexecute in .NET that's why it's rare to have shellexecute in C#. – Theraot Mar 24 '13 at 06:59
1

If you need something urgent - I'd try to put message into MSMQ from the service, and write some simple client that is waiting for queue messages, and proceed them.
Update:
Actually, if you need something very quick - you can even write to some file, and write simple desktop (winforms?) application to read this file in pool (each 5 sec?), and open browser when required.

evgenyl
  • 7,837
  • 2
  • 27
  • 32
  • MSMQ isn't "quick" if you don't have it implemented already in your app. – Ken White Mar 24 '13 at 06:40
  • I do like the idea of writing to file. In fact, the windows service is a simple one that just keeps polling a URI for new updates (every 5 secs). Can I replace the service itself and make the application do this in the background? Should this happen in a thread? Like I said, I am new to the Microsoft technologies – Ayrton Senna Mar 24 '13 at 06:49
  • 1
    It depends on what are you trying to accomplish. If you want this to happen nevertheless, user is logged in, or not - you have something like service. if you need this ONLY when user logged in - yes, you can do it from application. You can use Threading.Timer to schedule operations. Also, you may be find useful to "hide" your application form (may be leave it to Tray). – evgenyl Mar 24 '13 at 06:59
  • Yes, I think I would want this when a user is logged in and also probably hidden. Really appreciate your help. – Ayrton Senna Mar 24 '13 at 07:08
  • There are some really good responses and I will probably choose something as an answer by tomorrow depending on what works for me. – Ayrton Senna Mar 24 '13 at 07:10