2012-09-05 2 views
4

Я нашел много частичных ответов, но ничего действительно не достаточно.Преобразование приложения командной строки C# в службу Windows

Корпус: Приложение - это приложение командной строки без взаимодействия с пользователем, за исключением возможности получить нажатие клавиши для входа, чтобы остановить, это уже написано таким образом, который отключает даже это, когда не запускается как Environment.UserInteractive == true.

Я использую Visual Studio 2010.

Проблема в том, мне нужно, чтобы преобразовать это приложение для службы окон. Является ли это «просто» созданием нового файла класса в качестве службы и вызвать ли его методы запуска и остановки существующего приложения?

Как насчет установщика (установщик по умолчанию MSI по умолчанию), может ли быть обновлен существующий проект установщика для обработки Сервиса?

Я испортил это раньше и закончил установку, которая отказывалась устанавливать, поскольку она продолжала обнаруживать службу как уже установленную, останавливая процесс установки, а затем быстро откатывая все. Обнаруженная служба была той, которую она только что установила.

ответ

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", "[email protected]"); 
         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 
    } 
} 
+2

Это гораздо больше, чем я надеялся. Благодарю. –

+0

Очень всеобъемлющий! Мне очень нравится низкотехнологичный способ установки сервиса; отлично, если вам не нужен пакет установщика MSI. Если вам действительно нужна MSI, остерегайтесь решений, связанных с проектами VS Setup, поскольку они ушли в VS 2012. Wix сложна, но есть неплохая информация в Интернете о том, как это сделать. – Olly

+0

Спасибо за это предупреждение Олли. –

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(). Конечно, вам, возможно, придется слегка отредактировать код, чтобы убедиться, что в нем нет строк для чтения.

Теперь вы должны создать и установить. Как вы можете это сделать, можно найти здесь: http://msdn.microsoft.com/en-us/library/zt39148a%28v=vs.100%29.aspx

Я надеюсь, что это помогло: D

С наилучшими пожеланиями

ROXAS

1

Keep C# application running

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), а затем просто заменяем файлы, если вы обновляете код.

+0

Я уже делаю что-то подобное, но я все равно проголосую за него, так как это хорошая информация. –

+0

@ A.Grandt ах, извините. довольно дикий.; P – Killnine

+0

Что он, конечно, все еще читает :) –

Смежные вопросы