views:

73

answers:

4

I'll start with an example: Apache web server (under Windows) has a nice feature: it can be both run as a standalone application (with current users privileges), and that it can be installed and run as a windows service directly (as local system account), using same executable.

In order for application to be run as a standalone app, all it needs to do is along the lines of having static public Main() in some public class.

In order for application to be installable and runnable as service, it has to implement ServiceBase and Installer classes in certain way. But, if application like this is run as standalone app, it will show message box.

How can this Apache-like mode of operation be achieved? I believe solution is simple, but I don't really have an idea where to start.

Piece of code that follows is used to invoke service. Can it be modified to allow standalone usage?

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service() // defined elsewhere as Service : ServiceBase
        };
        ServiceBase.Run(ServicesToRun);
    }
}

My language of choice is C#.

Edit: Currently, I have abstracted common code into separate assembly (let's call it Library.dll), and I have two executables: Console.exe and Service.exe, which are standalone and windows service applications, respectively, and both are just means to invoking Library.dll.

My goal is to merge those two executables into one, that will still call to Library.dll.

A: 

In the example you site, I'm pretty confident the Apache app is written in C or C++. For that, you would need a ServiceMain function. If you execute it like a normal program, main gets called. If you point the service control manager at it, ServiceMain gets called instead.

Regarding C#, can't say I know about that. If I had to write a service in c#, I suppose I would start here - http://msdn.microsoft.com/en-us/library/bb483064.aspx

Ed
+1  A: 

You should really have all of your functionality abstracted in a library. The fact that it happens to be run from a Windows Service should not matter. In fact, if you had a faced class called ServiceFrontEnd that had a Start() and Stop() - the Windows Service application could call that, and so could a command-line app, a windows app, or whatever.

What you're describing here just needs more abstraction. The functionality of "the service" doesn't need to be tightly-coupled to how a Windows Service happens to operate. hope that helps

Robert Seder
What I have right now is a library that contains all logic. I invoke same library from standalone console application and from service application. What I am looking for is a way to merge two executables into one. So, Console.exe and Service.exe both call Library.dll, and I want to have single ConsoleAndService.exe, that calls Library.dll :)
mr.b
The entry-point for a console application is a static Main() method. The entry-point for the service is a class that inherits from ServiceBase - and the OnStart() and OnStop() are called. There should be no conflict. But empirically if you find that there is, I don't think it's unreasonable to have separate executables. After all, they will run in entirely different ways. Hope that helps
Robert Seder
Of course, it's perfectly acceptable to have separate executables. I was just looking for a way to merge them if possible. If not, well, it will continue to work as it already does.
mr.b
+2  A: 

In C#, an easy way to do it is to require a command line argument to run it as a service. If the argument isn't there, then run your form/console app. Then just have your installer include the argument in the executable path when installing the service so it looks like so:

C:\MyApp\MyApp.exe -service

It would look something like this:

static void Main(string[] args)
{
    foreach (string arg in args)
    {
        //Run as a service if our argument is there
        if (arg.ToLower() == "-service")
        {
            ServiceBase[] servicesToRun = new ServiceBase[] { new Service1() };
            ServiceBase.Run(servicesToRun);
            return;
        }
    }

    //Run the main form if the argument isn't present, like when a user opens the app from Explorer.
    Application.Run(new Form1());
}

This is just an example to give you an idea, there are probably cleaner ways to write this code.

Joe Doyle
That's what I was looking into before, but I couldn't find a way to pass an argument to installutil so that service would start with some argument. Another way to do it would be to require argument to run interactively, but that's not so handy. See my own answer to see how I did it in the end. Very simple.
mr.b
Anyhow, can you tell me how to pass argument to installutil, so that I know in future?
mr.b
+2  A: 

After some digging, I have finally looked under .NET hood (System.ServiceProcess.ServiceBase.Run method), only to find that it checks Environment.UserInteractive bool to make sure that executable is NOT run interactively.

Oversimplified solution that works for me:

class Program
{
    static void Main(string[] args)
    {
        if (!Environment.UserInteractive)
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
            { 
                // Service.OnStart() creates instance of MainLib() 
                // and then calls its MainLib.Start() method
                new Service()
            };
            ServiceBase.Run(ServicesToRun);
            return;
        }

        // Run in a console window
        MainLib lib = new MainLib();
        lib.Start();
        // ...
    }
}
mr.b
Beware: Environment.UserInteractive is only used to check if there is a graphical desktop session and the result is only used to determine whether to show a dialog or a console message. UserInteractive will also return true when running as a service that is allowed to interact with the desktop. The real check is done within native methods.
Ishmaeel
@Ishmaeel: thanks, good point. Didn't know these two modes were related. Do you have an example of native methods that can check for that?
mr.b
Apparently, ServiceBase just calls StartServiceCtrlDispatcher function (http://msdn.microsoft.com/en-us/library/ms686324(VS.85).aspx) and a nonzero return value (probably) means you are not launched as a service. Of course, it's not useful as a check because it is the very operation that you want to decide whether to execute or not. I haven't been able to find a proper check function yet.
Ishmaeel