简体   繁体   中英

Converting a Console Application to a service?

I'm looking for different ways with strengths/weaknesses for converting a console application we are using long term in to a windows service. We use something called java service wrapper for ActiveMQ, and I believe people have told me you can wrap anything with it. That's not saying that you should wrap anything with it though; we've had our issues with this setup.

The console app is a .NET console application that by default logs a lot of info to the console, though this is configurable.

Any reccomendations?

Should we just rebuild it in Visual Studio as a service? Use a wrapper? Which one?

I would be tempted to create an empty windows service project, and just grab the bits that deal with a service; it isn't a lot - a few references and some of the code in Main . You can actually have your existing console work both as a service and as a console - either by checking the args to Main and using (for example) a "-console" switch, or I believe you can check Environment.UserInteractive .

If it is in "console" mode, run your code as you do now; if it is in service mode, run the code you grabbed from the template project.

For info, you can also have the same exe work as the installer/uninstaller for the service! I do this with "-install" / "-uninstall" switches. For an example, see here .

Vici WinService will turn a console app into a self-installing Windows Service. It's open-source and you can download the source code. Even if you don't want to use the library, you can still get some ideas from it.

I ran into this same issue, I ended up writing my own wrapper, it's only good for the simplest situations but it does have it's perks. You can find the tool here: http://runasservice.com . Some of the perks include the fact that you can code your application as a console application which is easy to test and run in the IDE. Setting it up a service involves one simple command so you don't have to edit your application. Also you can install the service multiple times with different names, which you might want to do if you want to run each with different parameters.

Like I said though it's only covers the simplest scenarios, applications that are already essentially services. That is they run continuously. I'm sure there are a lot of other services out there that give you a lot more options.

Personally I don't think it's particularly difficult to convert a console application, but it can be a hassle to test. At the end though it depends on how much control you want. If it's a really important service for your company, then I would say convert it.

Some thoughts:

Create windows service with VS 2005

install .Net Service

I wrote some couple of years ago a Perl based set of executables ( theads ) etc. , which seems to have similar requirements to yours ...

Some stuff to keep in mind:

  • do have debuggin switch ( you should have one when something goes really wrong )
  • output to both console and files ( try log4net for example )
  • build your logging with future regex parsing in mind
  • if there are some inderdependant processes , learn how to kill them , stop and restart them
  • if there are some inderdependant processes try to communicate to them

Here is a small Console example to output to db, file and console with 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" />

What is your long-term usage scenario? A windows service might suffice...but Windows Server 2008/IIS7 provide some intriguing new ways of hosting and activating "services" via Windows Activation Services. A Windows Service will always be running, and may require some special coding. With WAS, you can write your host as a normal WCF service, and have it activated on demand when requests come in, and deactivated when it is not used. Other options also exist...such as MSMQ hosting and instantiation, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM