Converting a c# commandline app to a Windows service

后端 未结 3 544
逝去的感伤
逝去的感伤 2021-01-01 04:45

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 re

3条回答
  •  北荒
    北荒 (楼主)
    2021-01-01 05:37

    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
        }
    }
    

提交回复
热议问题