【问题标题】:Converting a Console Application to a service?将控制台应用程序转换为服务?
【发布时间】:2009-05-31 06:15:33
【问题描述】:

我正在寻找具有优势/劣势的不同方法来将我们长期使用的控制台应用程序转换为 Windows 服务。我们为 ActiveMQ 使用了一种叫做 java 服务包装器的东西,我相信人们告诉我你可以用它来包装任何东西。这并不是说你应该用它包裹任何东西。我们遇到了这个设置的问题。

控制台应用程序是一个 .NET 控制台应用程序,默认情况下会将大量信息记录到控制台,尽管这是可配置的。

有什么推荐吗?

我们是否应该在 Visual Studio 中将其重新构建为服务?使用包装器?哪一个?

【问题讨论】:

    标签: .net windows-services


    【解决方案1】:

    我很想创建一个空的 Windows 服务项目,然后只获取处理服务的部分;这不是很多 - 一些参考和Main 中的一些代码。您实际上可以让您现有的控制台both作为服务作为控制台工作-通过检查参数Main并使用(例如)“-console "开关,或者我相信你可以检查Environment.UserInteractive

    如果它处于“控制台”模式,请像现在一样运行您的代码;如果它处于服务模式,则运行您从模板项目中获取的代码。

    对于信息,您可以拥有与该服务的安装程序/卸载程序相同的 exe 工作!我使用“-install”/“-uninstall”开关来做到这一点。例如,see here

    【讨论】:

      【解决方案2】:

      Vici WinService 会将控制台应用程序转变为自安装的 Windows 服务。它是开源的,您可以下载源代码。即使您不想使用该库,您仍然可以从中获得一些想法。

      【讨论】:

        【解决方案3】:

        一些想法:

        Create windows service with VS 2005

        install .Net Service

        几年前我写了一组基于 Perl 的可执行文件 (theads) 等,这似乎与你的要求相似......

        注意事项:

        • 确实有调试开关(当真的出现问题时你应该有一个)
        • 输出到控制台和文件(例如尝试 log4net)
        • 在构建日志时考虑到未来的正则表达式解析
        • 如果有一些不依赖的进程,学习如何杀死它们,停止并重新启动它们
        • 如果有一些不依赖的进程尝试与它们通信

        这是一个使用 log4net 输出到数据库、文件和控制台的小控制台示例

            using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;
            using log4net;
            using log4net.Config;
            using NUnit.Framework;
        
            namespace ExampleConsoleApplication
            {
                [TestFixture]
                class TestClass
                {
        
                //private static readonly ILog logger =
                //     LogManager.GetLogger ( typeof ( TestClass ) );
        
                private static readonly log4net.ILog logger = log4net.LogManager.GetLogger ( System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType );
        
                    static void Main ( string[] args )
                    {
        
                        Console.WriteLine ( " START " );
                        #region LoggerUsage
                        DOMConfigurator.Configure (); //tis configures the logger 
                        logger.Debug ( "Here is a debug log." );
                        logger.Info ( "... and an Info log." );
                        logger.Warn ( "... and a warning." );
                        logger.Error ( "... and an error." );
                        logger.Fatal ( "... and a fatal error." );
        
                        #endregion LoggerUsage
                        TestClass objTestClass = new TestClass();
                        objTestClass.TestMethodNameOK ();
                        objTestClass.TestMethodNameNOK ();
        
                        Console.WriteLine ( " END HIT A KEY TO EXIT " );
                        Console.ReadLine ();
                        } //eof method 
        
                    [SetUp]
                    protected void SetUp ()
                    {
                        //Add Here the Initialization of the objects 
                    }
                    [Test ( Description = "Add here the description of this test method " )]
                    protected void TestMethodNameOK ()
                    { 
                        //build ok use case scenario here - e.g. no exception should be raced '
                        //Vegetable newCarrot = pool.GetItemByPropertyValue<Vegetable> ( "WriongByPurpose", "Orange" );
                        //Assert.IsInstanceOfType ( typeof ( Vegetable ), newCarrot );
                        //Assert.AreSame ( newCarrot, carrot );
                        //logger.Info ( " I got the newCarrot which is " + newCarrot.Color );
        
                    } //eof method 
        
                    [Test ( Description = "Add here the description of this test method " )]
                    protected void TestMethodNameNOK ()         //e.g. the one that should raze Exception
                    {
                        //build ok use case scenario here - e.g. no exception should be raced '
                        //Vegetable newCarrot = pool.GetItemByPropertyValue<Vegetable> ( "WriongByPurpose", "Orange" );
                        //Assert.IsInstanceOfType ( typeof ( Vegetable ), newCarrot );
                        //Assert.AreSame ( newCarrot, carrot );
                        //logger.Info ( " I got the newCarrot which is " + newCarrot.Color );
        
                    } //eof method 
        
                } //eof class 
        
            } //eof namespace 
        
        
        
        
        
            #region TheAppConfig
        //    <?xml version="1.0" encoding="utf-8" ?>
        //<configuration>
        //  <configSections>
        //    <section name="log4net"
        //         type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
        //  </configSections>
        
        //  <log4net>
        //    <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
        //      <param name="File" value="Program.log" />
        //      <param name="AppendToFile" value="true" />
        //      <layout type="log4net.Layout.PatternLayout">
        //        <!--<param name="Header" value="======================================" />
        //        <param name="Footer" value="======================================" />-->
        //        <param name="ConversionPattern" value="%d [%t] %-5p - %m%n" />
        //      </layout>
        //    </appender>
        
        //    <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
        //      <mapping>
        //        <level value="ERROR" />
        //        <foreColor value="Red" />
        //      </mapping>
        //      <mapping>
        //        <level value="DEBUG" />
        //        <foreColor value="HighIntensity" />
        //      </mapping>
        //      <mapping>
        //        <level value="INFO" />
        //        <foreColor value="Green" />
        //      </mapping>
        //      <mapping>
        //        <level value="WARN" />
        //        <foreColor value="Yellow" />
        //      </mapping>
        //      <mapping>
        //        <level value="FATAL" />
        //        <foreColor value="White" />
        //        <backColor value="Red" />
        //      </mapping>
        
        //      <layout type="log4net.Layout.PatternLayout">
        //        <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
        //      </layout>
        //    </appender>
        
        
        //    <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
        //      <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.2.10.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        //      <connectionString value="data source=ysg;initial catalog=DBGA_DEV;integrated security=true;persist security info=True;" />
        //      <commandText value="INSERT INTO [DBGA_DEV].[ga].[tb_Data_Log] ([Date],[Thread],[Level],[Logger],[Message]) VALUES (@log_date, @thread, @log_level, @logger, @message)" />
        
        //      <parameter>
        //        <parameterName value="@log_date" />
        //        <dbType value="DateTime" />
        //        <layout type="log4net.Layout.PatternLayout" value="%date{yyyy'.'MM'.'dd HH':'mm':'ss'.'fff}" />
        //      </parameter>
        //      <parameter>
        //        <parameterName value="@thread" />
        //        <dbType value="String" />
        //        <size value="255" />
        //        <layout type="log4net.Layout.PatternLayout" value="%thread" />
        //      </parameter>
        //      <parameter>
        //        <parameterName value="@domainName" />
        //        <dbType value="String" />
        //        <size value="255" />
        //        <layout type="log4net.Layout.PatternLayout" value="%user" />
        //      </parameter>
        //      <parameter>
        //        <parameterName value="@log_level" />
        //        <dbType value="String" />
        //        <size value="50" />
        //        <layout type="log4net.Layout.PatternLayout" value="%level" />
        //      </parameter>
        //      <parameter>
        //        <parameterName value="@logger" />
        //        <dbType value="String" />
        //        <size value="255" />
        //        <layout type="log4net.Layout.PatternLayout" value="%logger" />
        //      </parameter>
        //      <parameter>
        //        <parameterName value="@message" />
        //        <dbType value="String" />
        //        <size value="4000" />
        //        <layout type="log4net.Layout.PatternLayout" value="%message" />
        //      </parameter>
        //    </appender>
        //    <root>
        //      <level value="ALL" />
        //      <appender-ref ref="LogFileAppender" />
        //      <appender-ref ref="AdoNetAppender" />
        //      <appender-ref ref="ColoredConsoleAppender" />
        //    </root>
        //  </log4net>
        //</configuration>
            #endregion TheAppconfig
        
            //this is the xml added replace here your log4net and Nunit paths
            //<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
            //  <SpecificVersion>False</SpecificVersion>
            //  <HintPath>..\..\..\Log4Net\log4net-1.2.10\bin\net\2.0\release\log4net.dll</HintPath>
            //</Reference>
            //<Reference Include="nunit.framework, Version=2.4.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL" />
        

        【讨论】:

          【解决方案4】:

          您的长期使用场景是什么? Windows 服务可能就足够了……但是 Windows Server 2008/IIS7 提供了一些有趣的新方法来通过 Windows 激活服务托管和激活“服务”。 Windows 服务将始终运行,并且可能需要一些特殊编码。使用 WAS,您可以将主机编写为普通的 WCF 服务,并在请求进入时按需激活它,并在不使用时将其停用。还存在其他选项...例如 MSMQ 托管和实例化等。

          【讨论】:

            【解决方案5】:

            我遇到了同样的问题,我最终编写了自己的包装器,它只适用于最简单的情况,但它确实有好处。您可以在此处找到该工具:http://runasservice.com。一些好处包括您可以将应用程序编码为易于在 IDE 中测试和运行的控制台应用程序。将其设置为服务涉及一个简单的命令,因此您不必编辑您的应用程序。您还可以使用不同的名称多次安装该服务,如果您想使用不同的参数运行每个服务,您可能需要这样做。

            就像我说的,虽然它只涵盖最简单的场景,即本质上已经是服务的应用程序。那就是它们连续运行。我相信还有很多其他服务可以为您提供更多选择。

            我个人认为转换控制台应用程序并不是特别困难,但测试起来可能很麻烦。最后,尽管这取决于您想要多少控制。如果它对贵公司来说是一项非常重要的服务,那么我会说转换它。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-06-17
              • 1970-01-01
              • 1970-01-01
              • 2011-04-22
              • 1970-01-01
              相关资源
              最近更新 更多