4

我找到了很多部分答案,但没有什么足够的。

案例:应用程序是一个工作命令行应用程序,没有用户交互,除了能够在输入时接收按键以停止,这已经以一种即使不作为 Environment.UserInteractive == 运行时也会禁用的方式编写真的。

我正在使用 Visual Studio 2010。

问题是我需要将此应用程序转换为 Windows 服务。是否“只是”将一个新的类文件作为服务,并让它在现有应用程序上调用我的启动和停止方法?

安装程序(VS 的默认 msi 安装程序)怎么样,现有的安装程序项目可以“升级”以处理服务安装吗?

我之前搞砸了这个,最后得到了一个安装程序,它一直拒绝安装,因为它一直检测到服务已经安装,停止安装过程,然后迅速回滚所有内容。它检测到的服务是它刚刚安装的服务。

4

3 回答 3

11

要将控制台应用程序作为 Windows 服务或控制台应用程序运行,请编写单个控制台应用程序并使用命令行参数来确定您应该直接运行还是启动服务。包括一个安装程序/卸载程序,以使用正确的命令行参数作为 Windows 服务安装。

这是我们使用的提供此功能的基类。

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
    }
}
于 2012-09-05T13:09:10.233 回答
1

保持 C# 应用程序运行

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

检查上面的链接。我提供了一些代码以及描述使用控制台作为控制台和服务的双重职责的链接。我将使用控制台项目并在作为服务运行之前检查 UserInteractive。这样您就可以像控制台一样进行调试,但可以将其作为服务安装在生产服务器上。

关于安装,我没有从 .msi 安装的经验,但我们使用批处理脚本来安装服务(使用 sc.exe),然后如果您更新代码,只需替换文件即可。

于 2012-09-05T13:11:50.987 回答
0

对您来说最好的办法是启动一个新项目作为 Windows 服务。在这个新项目中,您将找到 Service1.cs,这是将从头开始运行的文件。以下代码将在文件中:

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

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

        protected override void OnStop()
        {
        }
    }
}

从这里弄清楚要做什么并不难。只需将您的类添加到项目中,并确保将主代码复制到 OnStart() 函数中。当然,您可能需要稍微编辑代码以确保其中没有 readlines。

现在您必须创建和安装程序。如何做到这一点可以在这里找到:http: //msdn.microsoft.com/en-us/library/zt39148a%28v=vs.100%29.aspx

我希望这会有所帮助:D

亲切的问候

RoXaS

于 2012-09-05T13:08:49.290 回答