Converting a c# commandline app to a Windows servi

2019-02-07 08:57发布

I found plenty of partial answers, but nothing really sufficient.

The case: App is a working command line app, with no user interaction, except for the ability to receive an key press on enter to stop, this is already written in a way that disables even that when not run as Environment.UserInteractive == true.

I'm using Visual Studio 2010.

The problem is I need to convert this app to a windows service. Is it "just" making a new class file as a service, and have it call my start and stop methods on the existing application?

How about the installer (VS' default msi installer), can the existing installer project be "upgraded" to handle the Service installation as well?

I messed with this before, and ended up with an installer that kept refusing to install, as it kept detecting the service as already being installed, stopping the install process by then promptly rolling back everything. The service it detected were the one it had just installed.

3条回答
看我几分像从前
2楼-- · 2019-02-07 09:22

Keep C# application running

public partial class DemoService : ServiceBase
{
    static void Main(string[] args)
    {
        DemoService service = new DemoService();

        if (Environment.UserInteractive)
        {
            service.OnStart(args);
            Console.WriteLine("Press any key to stop program");
            Console.Read();
            service.OnStop();
        }
        else
        {
            ServiceBase.Run(service);
        }
    }

Check the link above. I provide some code as well as a link describing using a console for double-duty as Console and Service. I will use a console project and check for UserInteractive before running as a service. This way you can debug as if it's a console, but install it as a service on a production server.

With regards to installing, I don't have experience with installing from .msi, but we use a batch script to install the service (using sc.exe) and then its just a matter of replacing files if you update the code.

查看更多
霸刀☆藐视天下
3楼-- · 2019-02-07 09:30

To run a console app as either a Windows Service or a Console application, write a single console application and use command line arguments to determine if you should run directly or start up the service. Include an installer/uninstaller to install as a windows service with the right command line arguments.

Here's a base class we use that provides this functionality.

using System;
using System.Collections;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Windows.Forms;
using Microsoft.Win32;

namespace Console40
{
    public abstract class AbstractService : ServiceBase
    {
        public static AbstractService Current { get; private set; }

        protected virtual string HelpTextPattern
        {
            get
            {
                #region Help Text

                return
                    @"
USAGE

    {0} [command]

    WHERE [command] is one of

        /console   - run as a console application, for debugging
        /service   - run as a windows service
        /install   - install as a windows service
        /uninstall - uninstall windows service

";

                #endregion
            }
        }

        public abstract string DisplayName { get; }

        public ServiceExecutionMode ServiceExecutionMode { get; private set; }

        protected abstract Guid UninstallGuid { get; }

        protected virtual string UninstallRegKeyPath
        {
            get
            {
                return @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
            }
        }

        protected AbstractService(string serviceName)
        {
            ServiceName = serviceName;
            if (Current != null)
            {
                throw new InvalidOperationException(String.Format(
                         "Service {0} is instantiating but service {1} is already instantiated as current.  References to AbstractService.Current will only point to the first service.",
                         GetType().FullName,
                         Current.GetType().FullName));
            }
            Current = this;
        }

        public void Run(string[] args)
        {
            Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;

            if (args.Length == 0 && Debugger.IsAttached)
            {
                args = new[] { "/console" };
            }

            if (args.Length == 0)
            {
                Console.WriteLine(HelpTextPattern, Path.GetFileName(GetType().Assembly.CodeBase));
            }
            else
            {
                switch (args[0].ToLower())
                {
                    case "/service":
                        ServiceExecutionMode = ServiceExecutionMode.Service;
                        Run(new[] { this });
                        break;

                    case "/console":
                        ServiceExecutionMode = ServiceExecutionMode.Console;
                        Console.WriteLine("Starting Service...");
                        OnStart(new string[0]);
                        OnStartCommandLine();
                        OnStop();
                        break;

                    case "/install":
                        ServiceExecutionMode = ServiceExecutionMode.Install;
                        InstallService();
                        break;

                    case "/uninstall":
                        ServiceExecutionMode = ServiceExecutionMode.Uninstall;
                        UninstallService();
                        break;

                    case "/uninstallprompt":
                        ServiceExecutionMode = ServiceExecutionMode.Uninstall;
                        if (ConfirmUninstall())
                        {
                            UninstallService();
                            InformUninstalled();
                        }
                        break;

                    default:
                        if (!OnCustomCommandLine(args))
                        {
                            Console.WriteLine(HelpTextPattern, Path.GetFileName(GetType().Assembly.CodeBase));
                        }
                        break;
                }
            }
        }

        protected override void OnStart(string[] args)
        {
            OnStartImpl(args);

            AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainUnhandledException;
        }

        protected virtual void OnStartCommandLine()
        {
            Console.WriteLine("Service is running...  Hit ENTER to break.");
            Console.ReadLine();
        }

        protected abstract void OnStartImpl(string[] args);

        void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // do something useful here, log it..
        }

        protected override void OnShutdown()
        {
            Stop();
        }

        protected override void OnStop()
        {
            OnStopImpl();
        }

        protected abstract void OnStopImpl();

        protected virtual bool OnCustomCommandLine(string[] args)
        {
            // for extension
            return false;
        }

        private void InstallService()
        {
            GetInstaller(".InstallLog").Install(new Hashtable());
            InstallServiceCommandLine();
            CreateUninstaller();
        }

        private void InstallServiceCommandLine()
        {
            string keyParent = @"SYSTEM\CurrentControlSet\Services\" + ServiceName;
            const string VALUE_NAME = "ImagePath";

            try
            {
                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(keyParent, true))
                {
                    if (key == null)
                    {
                        throw new InvalidOperationException("Service not found in registry.");
                    }

                    var origPath = key.GetValue(VALUE_NAME) as string;
                    if (origPath == null)
                    {
                        throw new Exception("HKLM\\" + keyParent + "\\" + VALUE_NAME + " does not exist but was expected.");
                    }

                    key.SetValue(VALUE_NAME, origPath.Replace("\"\"", "\"") + " /service");
                }
            }
            catch (Exception ex)
            {
                throw new Exception(
                    "Error updating service command line after installation.  Unable to write to HKLM\\" + keyParent, ex);
            }
        }

        private void CreateUninstaller()
        {
            using (RegistryKey parent = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
            {
                if (parent == null)
                {
                    throw new Exception(String.Format("Uninstall registry key '{0}' not found.", UninstallRegKeyPath));
                }
                try
                {
                    RegistryKey key = null;

                    try
                    {
                        string guidText = UninstallGuid.ToString("B");
                        key = parent.OpenSubKey(guidText, true) ??
                              parent.CreateSubKey(guidText);

                        if (key == null)
                        {
                            throw new Exception(String.Format("Unable to create uninstaller '{0}\\{1}'", UninstallRegKeyPath, guidText));
                        }

                        Assembly asm = GetType().Assembly;
                        Version v = asm.GetName().Version;
                        string exe = "\"" + asm.CodeBase.Substring(8).Replace("/", "\\\\") + "\"";

                        key.SetValue("DisplayName", DisplayName);
                        key.SetValue("ApplicationVersion", v.ToString());
                        key.SetValue("Publisher", "B-Line Medical");
                        key.SetValue("DisplayIcon", exe);
                        key.SetValue("DisplayVersion", v.ToString(2));
                        key.SetValue("URLInfoAbout", "http://www.blinemedical.com");
                        key.SetValue("Contact", "support@blinemedical.com");
                        key.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd"));
                        key.SetValue("UninstallString", exe + " /uninstallprompt");
                    }
                    finally
                    {
                        if (key != null)
                        {
                            key.Close();
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(
                        "An error occurred writing uninstall information to the registry.  The service is fully installed but can only be uninstalled manually through the command line.",
                        ex);
                }
            }
        }

        private bool ConfirmUninstall()
        {
            string title = "Uninstall " + DisplayName;
            string text = "Are you sure you want to remove " + DisplayName + " from your computer?";
            return DialogResult.Yes ==
                   MessageBox.Show(text, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                                   MessageBoxDefaultButton.Button2);
        }

        private void InformUninstalled()
        {
            string title = "Uninstall " + DisplayName;
            string text = DisplayName + " has been uninstalled.";
            MessageBox.Show(text, title, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void UninstallService()
        {
            GetInstaller(".UninstallLog").Uninstall(null);
            RemoveUninstaller();
        }

        private TransactedInstaller GetInstaller(string logExtension)
        {
            var ti = new TransactedInstaller();

            ti.Installers.Add(new ServiceProcessInstaller
            {
                Account = ServiceAccount.LocalSystem
            });

            ti.Installers.Add(new ServiceInstaller
            {
                DisplayName = DisplayName,
                ServiceName = ServiceName,
                StartType = ServiceStartMode.Automatic
            });

            string basePath = Assembly.GetEntryAssembly().Location;
            String path = String.Format("/assemblypath=\"{0}\"", basePath);
            ti.Context = new InstallContext(Path.ChangeExtension(basePath, logExtension), new[] { path });

            return ti;
        }

        private void RemoveUninstaller()
        {
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
            {
                if (key == null)
                {
                    return;
                }
                try
                {
                    string guidText = UninstallGuid.ToString("B");
                    RegistryKey child = key.OpenSubKey(guidText);
                    if (child != null)
                    {
                        child.Close();
                        key.DeleteSubKey(guidText);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(
                        "An error occurred removing uninstall information from the registry.  The service was uninstalled will still show up in the add/remove program list.  To remove it manually delete the entry HKLM\\" +
                        UninstallRegKeyPath + "\\" + UninstallGuid, ex);
                }
            }
        }
    }

    public enum ServiceExecutionMode
    {
        Unknown,
        Service,
        Console,
        Install,
        Uninstall,
        Custom
    }
}
查看更多
何必那么认真
4楼-- · 2019-02-07 09:35

Best thing for you to do is to start a new Project as a windows service. In this new project you will find Service1.cs and this is the file that will be run from start. the following code will be int the file:

namespace WindowsService1
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
        InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
        }

        protected override void OnStop()
        {
        }
    }
}

It isnt that hard to figure out what to do from here. Simply add your classes to the project and make sure that you copy your main code in the the OnStart() function. Of course you might have to slightly edit the code to make sure it has no readlines in it.

Now you must create and installer. How you can do this can be found here: http://msdn.microsoft.com/en-us/library/zt39148a%28v=vs.100%29.aspx

I hope this helped :D

Kind Regards

RoXaS

查看更多
登录 后发表回答