Пытаюсь установить службы Windows — Что не так с этим кодом?

#c# #windows-services #installation

#c# #windows-services #установка

Вопрос:

Я создаю проект службы Windows в VS2010, который содержит несколько служб. Я попытался найти способ установить его без сложного установщика. Но, похоже, он откатывается и не работает.

Вот Program.cs:

 static class Program
{
    static void Main(string[] args)
    {
        bool install = false, uninstall = false, console = false;
        WindowsServiceInstaller inst = new WindowsServiceInstaller();

        if (args.Length > 0)
        {
            foreach (string arg in args)
            {
                switch (arg)
                {
                    case "-i":
                    case "-install":
                        install = true;
                        break;
                    case "-u":
                    case "-uninstall":
                        uninstall = true;
                        break;
                    case "-c":
                    case "-console":
                        console = true;
                        break;
                    default:
                        Console.Error.WriteLine("Argument not expected: "   arg);
                        break;
                }
            }
        }

        if (uninstall)
        {
            inst.InstallServices(false, args);
        }

        if (install)
        {
            inst.InstallServices(true, args);
        }

        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            // scans Email table for outbound email jobs; uses multiple threads to lock and work on data in Email table
              new EmailLogScanner()

            // generates email digest of document status on secheduled basis; single thread
            , new EmailStatusDigester() 

            // keeps Fax table and third-party fax service accounts synchronized; uses a fixed nb of threads, one thread syncs one account at a time
            , new FaxSynchronizer()     
        };

        if (console)
        {

            foreach (IDebuggableService srv in ServicesToRun)
            {
                string[] strs = new string[] { String.Empty };
                srv.DebugStart(strs);
            }

            Console.WriteLine("Press any key to terminate...");
            Console.ReadKey();

            foreach (IDebuggableService srv in ServicesToRun)
            {
                srv.DebugStop();
            }

            Console.WriteLine("Service has exited.");
        }
        else
        {
            ServiceBase.Run(ServicesToRun);
        }
    }
}
  

Вот WindowsServiceInstaller.cs:

 [RunInstaller(true)]
public class WindowsServiceInstaller : Installer 
{
    public WindowsServiceInstaller()
    {
        ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
        serviceProcessInstaller.Account = ServiceAccount.NetworkService;
        serviceProcessInstaller.Username = null;
        serviceProcessInstaller.Password = null;
        Installers.Add(serviceProcessInstaller);

        ServiceInstaller emailLogScannerInstaller = new ServiceInstaller();
        emailLogScannerInstaller.DisplayName = "Email Scanner";
        emailLogScannerInstaller.StartType = ServiceStartMode.Automatic;
        emailLogScannerInstaller.ServiceName = "EmailLogScanner"; // must match the ServiceBase ServiceName property
        emailLogScannerInstaller.Description = "Scan for and sends out pending emails in stack.";
        Installers.Add(emailLogScannerInstaller);

        ServiceInstaller emailStatusDigesterInstaller = new ServiceInstaller();
        emailStatusDigesterInstaller.DisplayName = "Status Digester";
        emailStatusDigesterInstaller.StartType = ServiceStartMode.Automatic;
        emailStatusDigesterInstaller.ServiceName = "EmailDigester";
        emailStatusDigesterInstaller.Description = "Prepares document status email digests.";
        Installers.Add(emailStatusDigesterInstaller);

        ServiceInstaller faxSynchronizerInstaller = new ServiceInstaller();
        faxSynchronizerInstaller.DisplayName = "Fax Synchronizer";
        faxSynchronizerInstaller.StartType = ServiceStartMode.Automatic;
        faxSynchronizerInstaller.ServiceName = "FaxSynchronizer";
        faxSynchronizerInstaller.Description = "Synchronizes database with external fax service(s).";
        Installers.Add(faxSynchronizerInstaller);           

    }

    public void InstallServices(bool doInstall, string[] args)
    {
        try
        {
            using (AssemblyInstaller aInstaller = new AssemblyInstaller(typeof(Program).Assembly, args))
            {
                IDictionary state = new Hashtable();
                aInstaller.UseNewContext = true;
                try
                {
                    if (doInstall)
                    {
                        aInstaller.Install(state);
                        aInstaller.Commit(state);
                    }
                    else
                    {
                        aInstaller.Uninstall(state);
                    }
                }
                catch
                {
                    try
                    {
                        aInstaller.Rollback(state);
                    }
                    catch { }
                    throw;
                }
            }
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine(ex.Message);
        }
    }

}
  

Зарегистрированные выходные данные (когда я запускаю daemon.exe -i в командном окне от имени администратора) показывают текст ниже. Кроме того, я получаю диалоговое окно «не удается запустить службу из командной строки»:

 Installing assembly 'C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.exe'.
Affected parameters are:
   i = 
   assemblypath = C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.exe
   logfile = C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.InstallLog
Installing service EmailLogScanner...
Service EmailLogScanner has been successfully installed.
Creating EventLog source EmailLogScanner in log Application...
See the contents of the log file for the C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.exe assembly's progress.
The file is located at C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.InstallLog.
Rolling back assembly 'C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.exe'.
Affected parameters are:
   logtoconsole = 
   i = 
   assemblypath = C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.exe
   logfile = C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.InstallLog
Restoring event log to previous state for source EmailLogScanner.
Service EmailLogScanner is being removed from the system...
Service EmailLogScanner was successfully removed from the system.
  

ОБНОВЛЕНИЕ: Когда я комментирую блок try … catch вокруг ‘aInstaller.Строка Install (state)’, я получаю немного другой вывод:

 Installing assembly 'C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.exe'.
Affected parameters are:
   i =
   assemblypath = C:UsersxxxDocuments~BusinessProjectsDa
emonbinReleaseDaemon.exe
   logfile = C:UsersxxxDocuments~BusinessProjectsDaemon
binReleaseDaemon.InstallLog
Installing service EmailLogScanner...
Creating EventLog source EmailLogScanner in log Application...
Source EmailLogScanner already exists on the local computer.
  

Это потому, что у меня уже настроены источники журнала событий? Если да, то как мне пропустить этот шаг в AssemblyInstaller? Если нет, то что? 🙂

Комментарии:

1. И что The file is located at C:UsersxxxDocuments~BusinessProjectsDaemonbinReleaseDaemon.InstallLog сказал?

2. На самом деле, последний блок выше — это то, что записано в файл InstallLog.

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

4. Спасибо, вы заставили меня заметить, что было не так.

Ответ №1:

Вы должны поместить это

 if (EventLog.SourceExists("YourEventSourceName"))
    EventLog.DeleteEventSource("YourEventSourceName");
  

когда начнется установка службы.

Комментарии:

1. На самом деле, моя проблема была вариантом этого. В сборке у меня есть три разных службы. В каждой службе у меня есть код, который создал EventSource в конструкторе ServiceBase. Похоже, что ServiceInstaller не проверяет, прежде чем попытается создать EventSource, и, следовательно, конфликт. Я перенес код-нарушитель в другой метод, чтобы я мог создать исходный код, если служба была запущена через консоль.