运行控制台应用程序作为服务

时间:2011-09-15 07:41:20

标签: c# console

我开发了c#应用程序,其中apllication输出类型是Console Applicatiuon。 我想将此应用程序作为服务运行。 当我从visual studio运行它或只需双击.exe时,Environment.UserInteractive始终为true。

以下是我的代码

 static void Main(string[] args)
        {
            // Get the version of the current application.
            Assembly assem = Assembly.GetExecutingAssembly();
            AssemblyName assemName = assem.GetName();
            Version ver = assemName.Version;
            // Console.WriteLine("{0}, Version {1}", assemName.Name, ver.ToString());

            Console.WriteLine("{0} version {1}", assemName.Name, ver.ToString());

            TouchService touchService = new TouchService();


            if (Environment.UserInteractive)
            {
                bool show_help = false;
                bool install_service = false;
                bool uninstall_service = false;
                string servicename = "";

                OptionSet p = new OptionSet()                  
                  .Add("h|?|help", delegate(string v) { show_help = v != null; })
                  .Add("s|servicename=", "name of installed service", delegate(string v) { servicename = v; })
                  .Add("i|install", "install program as a Windows Service. A valid servicename is needed.", delegate(string v) { install_service = v != null; })
                  .Add("u|uninstall", "uninstall program from Windows Services. A valid servicename is needed.", delegate(string v) { uninstall_service = v != null; });

                List<string> extra;
                try
                {
                    extra = p.Parse(args);
                }
                catch (OptionException e)
                {
                    Console.Write("TouchServer: ");
                    Console.WriteLine(e.Message);
                    Console.WriteLine("Try `TouchServer --help' for more information.");
                    return;
                }

                if (show_help)
                {
                    ShowHelp(p);
                    return;
                }

                else if (install_service)
                {
                    IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
                    Inst.Install(servicename, null, "Provides XML data over HTTP for Touch clients",                                                              
                                 System.ServiceProcess.ServiceAccount.NetworkService,
                                 System.ServiceProcess.ServiceStartMode.Manual);

                    return;
                }

                else if (uninstall_service)
                {
                    IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
                    Inst.Uninstall(servicename);
                    return;
                }

                // start and run the server,
                // and receive commands from the console
                else
                {

                    touchService.OnStart(args);                   
                    while(true)
                    {
                        Console.Write("TouchServer>");                        
                        string commandLine = Console.ReadLine().ToLower();

                        if (commandLine == "exit" || commandLine == "x")
                        {
                            break;
                        }
                        if (commandLine == "quit" || commandLine == "q")
                        {
                            break;
                        }

                        else if(commandLine == "version" || commandLine == "v")
                        {
                            Console.WriteLine("{0} version {1}", assem.GetName().Name, assem.GetName().Version.ToString());
                        }

                        else if (commandLine == "list" || commandLine == "l")
                        {
                            TouchServer.showURLs = (TouchServer.showURLs == false) ? true : false; 
                            Console.WriteLine("List URLs: {0}", (TouchServer.showURLs ? "active" : "inactive"));
                        }

                        else if (commandLine == "status" || commandLine == "s")
                        {
                            Console.WriteLine("{0,-20} {1,8}", "Name", "Sessions");                            
                            Console.WriteLine("----------------------------");
                            foreach (Site site in TouchServer.siteCollection.All)
                            {
                                Console.WriteLine("{0,-20} {1,8}", site.Name, site.AllSessions.Length);
                            }
                            Console.WriteLine();
                        }
                    }

                    touchService.OnStop();
                }
            }
            **else
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] 
                { 
                    new TouchService() 
                };
                ServiceBase.Run(ServicesToRun);
            }**

我如何将其作为服务运行,请帮助我。 提前致谢 桑吉塔

4 个答案:

答案 0 :(得分:16)

使用文件 - &gt;新项目 - &gt; Visual C# - &gt; Windows-&gt; Windows服务

将主代码添加到OnStart()和OnStop()事件处理程序,然后将其安装为服务:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;

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

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

        protected override void OnStop()
        {
        }
    }
}

答案 1 :(得分:7)

以下是我使用的解决方案,它在Visual Studio 2012和.NET 4.5下运行良好。当我在控制台模式下运行时,它运行正常,当我使用installutil.exe作为服务运行时,它运行正常。

主文件

档案MainPayload.cs。您可以忽略所有其他文件,并将长时间运行的代码插入此文件中。请注意CancellationTokenSource,这样服务可以在服务停止时快速退出。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
//using Gurock.SmartInspect; // Only used if we are logging using SmartInspect (see www.SmartInspect.com).
namespace Demos___Service_Plus_Console
{
    /// <summary>
    /// Main entry point for both console and Windows service.
    /// </summary>
    public class MainPayload
    {
        private readonly CancellationTokenSource _cancellationTokenSource;

        /// <summary>
        /// Constructor; do not block in this call; it is for setup only.
        /// </summary>
        public MainPayload(CancellationTokenSource cancellationTokenSource)
        {
            // Do not block in this call; it is for setup only.
            _cancellationTokenSource = cancellationTokenSource;
        }   
        /// <summary>
        /// Long running task here.
        /// </summary>
        public void Run()
        {
            while (_cancellationTokenSource.IsCancellationRequested == false)
            {
                //SiAuto.Main.LogMessage(".");
                Console.WriteLine(".");

                // This will break every N seconds, or immediately if on cancellation token.
                _cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
            }
            //SiAuto.Main.LogMessage("Exited Run().");
            Console.WriteLine("Exited Run().");
            Thread.Sleep(500); // If we remove this line, then we will miss the final few writes to the console.
        }
    }
}

支持文件

档案EntryPoint.cs。这是控制台应用程序和服务的入口点。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
//using Gurock.SmartInspect; // Only used if we are logging using SmartInspect (see www.SmartInspect.com).
namespace Demos___Service_Plus_Console
{
    internal static class EntryPoint
    {
        // Run in console mode.
        private static readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        private static Task _task;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        public static void Main(string[] args)
        {
            //SiAuto.Si.Connections = "pipe(reconnect=\"true\", reconnect.interval=\"10\", backlog.enabled=\"true\", backlog.flushon=\"debug\", backlog.keepopen=\"true\")";
            //SiAuto.Si.Enabled = true;

            if (Environment.UserInteractive)
            {
                // Make sure that we can write to the console.
                StreamWriter standardOutput = new StreamWriter(Console.OpenStandardOutput()) {AutoFlush = true};
                Console.SetOut(standardOutput);

                // If Ctrl-C is pressed in the console, we get to here.
                Console.CancelKeyPress += new ConsoleCancelEventHandler(myHandler);

                MainPayload myMain = new MainPayload(_cancellationTokenSource); // Pass the token into the task.
                _task = Task.Run(() => myMain.Run());

                // Wait for the payload task to finish.
                while (_cancellationTokenSource.IsCancellationRequested == false)
                {
                    // This will break every N seconds, or immediately if cancellation token is pinged.
                    _cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(10));
                }
            }
            else
            {
                // Run as Windows Service.
                var ServicesToRun = new ServiceBase[]
                    {
                        new ServiceController()
                    };
                ServiceBase.Run(ServicesToRun);
            }

            _task.Wait(TimeSpan.FromSeconds(10)); // Delay for console to write its final output.
        }

        static void myHandler(object sender, ConsoleCancelEventArgs args)
        {
            _cancellationTokenSource.Cancel();
            //SiAuto.Main.LogMessage("CtrlC pressed.");
            Console.WriteLine("CtrlC pressed.");
        }
    }
}

档案ProjectInstaller.cs。这是该服务的安装程序。

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Threading.Tasks;

namespace Demos___Service_Plus_Console
{
    [RunInstaller(true)]
    public partial class ProjectInstaller : System.Configuration.Install.Installer
    {
        public ProjectInstaller()
        {
            InitializeComponent();
        }

        private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
        {

        }

        private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e)
        {

        }
    }
}

档案ServiceController.cs。其中包含服务的Start()Stop()方法。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Demos___Service_Plus_Console
{
    /// <summary>
    /// When running in service mode.
    /// </summary>
    public partial class ServiceController : ServiceBase
    {
        public ServiceController()
        {
            InitializeComponent();
        }

        readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        // Initialize payload.
        private MainPayload myMain;

        protected override void OnStart(string[] args)
        {
            myMain = new MainPayload(cancellationTokenSource); // Pass the token into the task.
            Task.Run(() => myMain.Run());
        }

        protected override void OnStop()
        {
            cancellationTokenSource.Cancel();
        }
    }
}

如何安装为Windows服务

要将其安装为Windows服务,请使用installutil.exe ServiceName.exe。要卸载,请使用installutil.exe ServiceName.exe -u。这意味着您必须在Visual Studio 2012 x32 command prompt中打开Visual Studio Tools下的Start Menu。如果要在32位模式下进行编译,请使用32位命令提示符,如果要在64位模式下进行编译,请打开64位命令提示符(这意味着路径设置正确,如{{1根据其32位还是64位而具有单独的版本。

打开installutil.exe面板,然后查找名为Services的新服务。当你启动它时,如果你打开了一个日志框架,那么你会看到每秒都会在日志中输入消息。

我是如何创建此代码的

它看起来像很多代码,但它全部基于Visual Studio 2012中的ServiceController项目模板。我创建了一个新的Windows服务,然后使用Windows Service在控制台或服务。我添加了CancellationTokens以确保如果服务停止,任务也会很快停止(即使它有延迟)。

唯一的另一个技巧是右键单击&#34; Service1.cs&#34;的灰色背景。页面,then click "Add Installer" which adds the installer。如果您不这样做,那么Environment.UserInteracive会给您一个错误。

enter image description here

您还必须右键点击installutil.exe,然后选择serviceProcessInstaller1并将Properties设置为Account,否则它将ask you for username credentials when you install the service

以下是所需的额外参考资料(使用模板创建新的Windows服务时会自动添加这些参考资料):

  • System.ServiceProcess
  • System.Configuration.Install

您会注意到LocalServiceGurock.SmartInspect的引用。如果要观察正在运行的服务创建的日志,可以使用此或其他内容,如NLog或log4net。

答案 2 :(得分:5)

仅当Environment.UserInteractive实际作为服务运行时,它才为false。双击它或从Visual Studio启动它时;它作为普通的控制台应用程序运行,桌面可用,因此Environment.UserInteractive是真的。

您可以从ConsoleService类Squiggle's code base派生您的类,以创建一个也可以作为Windows服务运行的控制台应用程序。

public class ConsoleService : ServiceBase
{
    public void RunConsole(string[] args)
    {
        Trace.Listeners.Add(new ConsoleTraceListener());
        OnStart(args);
        Trace.WriteLine(this.ServiceName + " running... Press any key to stop");
        Trace.WriteLine("");
        Console.ReadKey();
        OnStop();
    }

    public static void Run<TService>(string[] args) where TService : ConsoleService, new()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        if (Environment.UserInteractive)
        {
            try
            {
                string option = args.Length > 0 ? args[0].ToUpperInvariant() : String.Empty;
                switch (option)
                {
                    case "-I":
                    case "/I":
                        ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetCallingAssembly().Location });
                        break;
                    case "-U":
                    case "/U":
                        ManagedInstallerClass.InstallHelper(new string[] { "/U", Assembly.GetCallingAssembly().Location });
                        break;
                    default:
                        new TService().RunConsole(args);
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex.Message);
            }
        }
        else
        {
            ServiceBase[] servicesToRun = new ServiceBase[] { new TService() };
            ServiceBase.Run(servicesToRun);
        }
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        if (e.ExceptionObject is Exception)
            Trace.WriteLine(((Exception)e.ExceptionObject).Message);
    }
}

答案 3 :(得分:4)

我刚刚在Visual Studio 2013中对此进行了测试,但它确实有效。

  1. 创建新的&#34; Windows服务&#34;项目。
  2. 将项目输出类型更改为&#34;控制台应用程序&#34;
  3. 以下是我的Program.cs的样子:

    static class Program
    {
      /// <summary>
      /// </summary>
      static void Main()
      {
        if (!Environment.UserInteractive)
        {
          ServiceBase[] ServicesToRun;
          ServicesToRun = new ServiceBase[] 
              { 
                  new Service1() 
              };
          ServiceBase.Run(ServicesToRun);
        }
        else
        {
          Console.Write("Hit any key to continue...");
          Console.ReadKey();
        }
      }
    }
    

    您可以添加服务安装like this

    您可以使用以下命令安装服务:installutil.exe /i YouExeName.exe

    您可以使用以下命令卸载服务:installutil.exe /u YouExeName.exe