Более простой способ отладки службы Windows

есть ли более простой способ пройти через код, чем запустить службу через диспетчер управления службами Windows, а затем подключить отладчик к потоку? Это довольно громоздко, и мне интересно, есть ли более простой подход.

29 ответов


если я хочу быстро отладить службу, я просто падаю в Debugger.Break() там. Когда эта линия будет достигнута, она вернет меня к VS. Не забудьте убрать эту линию, когда закончите.

обновление: в качестве альтернативы #if DEBUG прагмы, вы также можете использовать .

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

на OnStart, просто вызовите этот метод:

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

там код будет включен только во время сборки отладки. В то время как вы на это, это может быть полезно создать отдельную конфигурацию сборки для отладки службы.


Я также думаю, что наличие отдельной "версии" для нормального выполнения и в качестве службы-это путь, но действительно ли необходимо выделять отдельный переключатель командной строки для этой цели?

не могли бы вы просто сделать:

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

это будет иметь "преимущество", что вы можете просто запустить приложение через doubleclick (ОК, Если вам это действительно нужно) и что вы можете просто нажать Ф5 в Visual Studio (без необходимости изменять параметры проекта, чтобы включить это ).

технически Environment.UserInteractive проверяет, является ли WSF_VISIBLE флаг установлен для текущей станции окна, но есть ли другая причина, по которой он вернется false, помимо запуска в качестве (неинтерактивной) службы?


когда я создал новый проект службы несколько недель назад я нашел этот пост. Хотя есть много отличных предложений, я все еще не нашел решение, которое хотел: возможность вызова классов обслуживания"OnStart и OnStop методы без каких-либо изменений к классам обслуживания.

решение, которое я придумал, использует Environment.Interactive выберите режим работы, как предложено другими ответами на этот пост.

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

The RunInteractive helper использует отражение для вызова защищен OnStart и OnStop методы:

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}

это все, что требуется код, но я также написал прохождение с объяснениями.


обычно я инкапсулирую логику службы в отдельный класс и запускаю ее из класса "runner". Этот класс runner может быть фактическим сервисом или просто консольным приложением. Таким образом, ваше решение имеет (по крайней мере) 3 проекта:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....

иногда важно проанализировать, что происходит во время запуска сервиса. присоединение к процессу здесь не помогает, потому что вы недостаточно быстры, чтобы подключить отладчик во время запуска службы.

короткий ответ, я использую следующие 4 строки кода для этого:

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

они вставляются в OnStart метод следующим образом:

protected override void OnStart(string[] args)
{
    #if DEBUG
       base.RequestAdditionalTime(600000); // 10 minutes timeout for startup
       Debugger.Launch(); // launch and attach debugger
    #endif
    MyInitOnstart(); // my individual initialization code for the service
    // allow the base class to perform any work it needs to do
    base.OnStart(args);
}

для тех, кто не делал этого раньше, я включил подробные подсказки ниже, потому что вы можете легко застрять. Следующие подсказки относятся к Windows 7x64 и Visual Studio 2010 Team Edition, но должен быть действителен и для других сред.


важно: разверните службу в "ручном" режиме (используя InstallUtil утилита из командной строки VS или запустить проект установщика служб вы подготовились). Откройте Visual Studio до запустите службу и загрузите решение, содержащее исходный код службы-настройте дополнительные точки останова по мере необходимости в Visual Studio - затем запустите службу через Панель Управления.

из-за Debugger.Launch код, это вызовет диалоговое окно " необработанное исключение Microsoft .NET Framework произошло в Servicename.exe." казаться. Щелчок Elevate да, debug Servicename.exe как показано на скриншоте:
FrameworkException

после этого, escpecially в Windows 7 UAC может предложить вам ввести учетные данные администратора. Введите их и нажмите да:

UACPrompt

после этого появляется хорошо известное окно отладчика Visual Studio Just-In-Time. Он спрашивает вас, Хотите ли вы отлаживать с помощью delected отладчика. Прежде чем нажать да, выберите, что вы не хотите открывать новый экземпляр (2 - й вариант) - новый экземпляр не будет полезен здесь, потому что исходный код не будет отображаться. Таким образом, вы выбираете экземпляр Visual Studio, который вы открыли ранее: VSDebuggerPrompt

после нажатия да, через некоторое время Visual Studio покажет желтую стрелку прямо в строке, где Debugger.Launch оператор и вы можете отлаживать свой код (метод MyInitOnStart, который содержит вашу инициализацию). VSDebuggerBreakpoint

клавишей Ф5 продолжает выполнение немедленно, пока не будет достигнута следующая точка останова, которую вы подготовили.

подсказка: чтобы служба продолжала работать, выберите Debug - > отсоединить все. Это позволяет запускать клиент, взаимодействующий со службой после ее правильного запуска и завершения отладки кода запуска. Если нажать Shift+Ф5 (остановить отладку), это завершит работу службы. Вместо этого, вы должны использовать Панель Управления чтобы остановить его.

Примечание это

  • если вы создаете выпуск, то код отладки автоматически удаляется, и служба работает нормально.

  • я использую Debugger.Launch(), который начинается и присоединяет отладчик. Я проверил Debugger.Break(), который не работает, потому что при запуске службы еще нет отладчика (вызывающего "ошибка 1067: процесс был неожиданно завершен.").

  • RequestAdditionalTime устанавливает более длительный тайм-аут для запуска службы (он не задерживает сам код, но сразу же продолжит работу с Debugger.Launch заявления). В противном случае по умолчанию для запуска службы слишком короткий и запуск службы завершается неудачей, если вы не вызываете base.Onstart(args) достаточно быстро из отладчика. Практически, тайм-аут 10 минут позволяет избежать того, что вы видите сообщение"служба не отвечает..." сразу после запуска отладчика.

  • как только вы привыкнете к нему, этот метод очень прост, потому что он просто требует от вас добавить 4 строки к существующему коду обслуживания, позволяющ вам быстро усиление контроля и отладки.


этой YouTube видео Фабио Скопел объясняет, как отлаживать службу Windows довольно красиво... фактический способ сделать это начинается в 4:45 в видео...

вот код, объясненный в видео... в вашей программе.cs-файл, добавьте материал для раздела отладки...

namespace YourNamespace
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
#if DEBUG
            Service1 myService = new Service1();
            myService.OnDebug();
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
#endif

        }
    }
}

в свой файл service1.cs-файл, добавьте метод OnDebug ()...

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

обновление

этот подход, безусловно, самый простой:

http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

Я оставляю свой первоначальный ответ ниже для потомков.


мои службы, как правило, имеют класс, который инкапсулирует таймер, поскольку я хочу, чтобы служба регулярно проверяла, есть ли для нее какая-либо работа.

мы создаем новый класс и вызываем StartEventLoop () во время запуск службы. (Этот класс также можно легко использовать из консольного приложения.)

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

п.С. как подключить отладчик вручную к запущенному процессу...?

using System;
using System.Threading;
using System.Configuration;    

public class ServiceEventHandler
{
    Timer _timer;
    public ServiceEventHandler()
    {
        // get configuration etc.
        _timer = new Timer(
            new TimerCallback(EventTimerCallback)
            , null
            , Timeout.Infinite
            , Timeout.Infinite);
    }

    private void EventTimerCallback(object state)
    {
        // do something
    }

    public void StartEventLoop()
    {
        // wait a minute, then run every 30 minutes
        _timer.Change(TimeSpan.Parse("00:01:00"), TimeSpan.Parse("00:30:00");
    }
}

также я делал следующее (уже упоминалось в предыдущих ответах, но с флагами условного компилятора [#if], чтобы помочь избежать его запуска в сборке выпуска).

Я перестал делать это таким образом, потому что иногда мы забывали строить в выпуске и иметь перерыв отладчика в приложении, работающем на демо-версии клиента (embarrasing!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif


static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}

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

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


раньше я использовал переключатель командной строки, который запускал бы программу либо как службу, либо как обычное приложение. Затем в моей IDE я бы установил переключатель, чтобы я мог пройти через свой код.

с некоторыми языками вы можете фактически определить, работает ли он в среде IDE, и выполнить этот переключатель автоматически.

какой язык вы используете?


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

два варианта, которые я использовал в прошлом:

  • используйте GFlags (в инструментах отладки для Windows) для настройки постоянного отладчика для процесса. Это существует в разделе реестра "параметры выполнения файла изображения" и невероятно полезно. Я думаю, вам нужно настроить параметры службы, чтобы включить "взаимодействие с рабочим столом". Я использую это для всех типов отладки, а не только услуги.
  • другой вариант-немного отделить код, чтобы сервисная часть была взаимозаменяема с обычным запуском приложения. Таким образом, вы можете использовать простой флаг командной строки и запускать как процесс (а не Службу), что значительно упрощает отладку.

надеюсь, что это помогает.


использовать TopShelf библиотека.

создайте консольное приложение, затем настройте настройку в Main

class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {

                // setup service start and stop.
                x.Service<Controller>(s =>
                {
                    s.ConstructUsing(name => new Controller());
                    s.WhenStarted(controller => controller.Start());
                    s.WhenStopped(controller => controller.Stop());
                });

                // setup recovery here
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(delayInMinutes: 0);
                    rc.SetResetPeriod(days: 0);
                });

                x.RunAsLocalSystem();
            });
        }
}

public class Controller
    {
        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

чтобы отладить службу, просто нажмите F5 в visual studio.

чтобы установить службу, введите cmd " console.exe установки"

затем можно запустить и остановить службу в диспетчере служб windows.


когда я пишу службу, я помещаю всю логику службы в проект dll и создаю два "хоста", которые вызывают эту dll, один из которых является службой Windows, а другой-приложением командной строки.

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

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


мне нравится иметь возможность отлаживать каждый аспект моей службы, включая любую инициализацию в OnStart (), все еще выполняя ее с полным поведением службы в рамках SCM... нет режима" консоль "или" приложение".

я делаю это, создавая вторую службу в том же проекте, чтобы использовать ее для отладки. Служба отладки при запуске как обычно (т. е. в плагине MMC служб) создает процесс хоста службы. Это дает вам процесс для присоединения отладчика, хотя ты еще не начал свою настоящую службу. После присоединения отладчика к процессу запустите свою реальную службу, и вы можете ворваться в нее в любом месте жизненного цикла службы, включая OnStart().

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

детали:

1) предполагая, что вы реализуете MyService создать MyServiceDebug. Добавьте оба в ServiceBase массив Program.cs вот так:

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new MyService(),
            new MyServiceDebug()
        };
        ServiceBase.Run(ServicesToRun);
    }

2) Добавьте реальную службу и службу отладки в установщик проекта для проекта службы:

enter image description here

обе службы (real и debug) включаются при добавлении выходных данных проекта службы в проект установки для службы. После установки, обе службы появятся в сервисе.MSC MMC плагин.

3) запустите службу отладки в MMC.

4) в Visual Studio присоедините отладчик к процессу, запущенному службой отладки.

5) Начните реальное обслуживание и насладитесь отладкой.


при разработке и отладке службы Windows я обычно запускаю ее как консольное приложение, добавляя параметр запуска / console и проверяя это. Делает жизнь намного проще.

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}

Как насчет отладчика.Break () в первой строке?


для отладки служб Windows я объединяю GFlags и a .файл reg, созданный regedit.

  1. запустите GFlags, указав exe-имя и vsjitdebugger
  2. запустите regedit и перейдите в место, где GFlags устанавливает свои параметры
  3. выберите "Экспорт ключа" из меню Файл
  4. Сохраните этот файл где-нибудь с помощью.расширением reg
  5. в любое время, когда вы хотите отладить службу: дважды щелкните по .reg file
  6. если вы хотите, чтобы остановить отладки, запускаем второй .reg file

или сохраните следующие фрагменты и замените servicename.exe с желаемым именем исполняемого файла.


debugon.Рег:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"
"Debugger"="vsjitdebugger.exe"

debugoff.Рег:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\servicename.exe]
"GlobalFlag"="0x00000000"

для рутинного программирования мелких вещей я сделал очень простой трюк, чтобы легко отладить мой сервис:

при запуске службы я проверяю параметр командной строки "/ debug". Если служба вызывается с этим параметром, я не делаю обычный запуск службы, а вместо этого запускаю все прослушиватели и просто отображаю messagebox "Debug in progress, нажмите ok, чтобы закончить".

поэтому, если моя служба запускается обычным способом, она начнется как Служба, если она запускается с параметр командной строки / debug он будет действовать как обычная программа.

в VS я просто добавлю / debug в качестве параметра отладки и сразу же запущу служебную программу.

таким образом, я могу легко отлаживать большинство небольших проблем. Конечно, некоторые вещи все равно нужно будет отлаживать как сервис, но для 99% этого достаточно.


#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif

Я использую вариант ответа JOP. С помощью параметров командной строки можно задать режим отладки в среде IDE со свойствами проекта или через Диспетчер служб Windows.

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}

Это довольно много после вопроса, но может быть полезно для дальнейшего использования.

Если вы можете, я бы предложил использовать отличный TopShelf. Это делает разработку и отладку службы Windows намного проще и добавляет делает развертывание несколько проще.

проверьте это: http://topshelf-project.com/


для устранения неполадок в существующей программе Службы Windows используйте отладчик.Break ()', как предлагали другие ребята.

для новой программы обслуживания Windows я бы предложил использовать метод Джеймса Майкла Хараhttp://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template-redux.aspx


просто поместите обед отладчика в любом месте и прикрепите Visualstudio при запуске

#if DEBUG
    Debugger.Launch();
#endif

Также вам нужно запустить VS как Administatrator, и вам нужно разрешить, чтобы процесс мог автоматически отлаживаться другим пользователем (как объяснено здесь):

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f

используйте проект Windows Service Template C# для создания нового приложения-службыhttps://github.com/HarpyWar/windows-service-template

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


вот простой метод, который я использовал для тестирования службы, без каких-либо дополнительных методов "отладки" и с интегрированными модульными тестами VS.

[TestMethod]
public void TestMyService()
{
    MyService fs = new MyService();

    var OnStart = fs.GetType().BaseType.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

    OnStart.Invoke(fs, new object[] { null });
}

// As an extension method
public static void Start(this ServiceBase service, List<string> parameters)
{
     string[] par = parameters == null ? null : parameters.ToArray();

     var OnStart = service.GetType().GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

     OnStart.Invoke(service, new object[] { par });
}

static class Program
{
    static void Main()
    {
        #if DEBUG

        // TODO: Add code to start application here

        //    //If the mode is in debugging
        //    //create a new service instance
        Service1 myService = new Service1();

        //    //call the start method - this will start the Timer.
        myService.Start();

        //    //Set the Thread to sleep
        Thread.Sleep(300000);

        //    //Call the Stop method-this will stop the Timer.
        myService.Stop();

         #else
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service1() 
        };

        ServiceBase.Run(ServicesToRun);
         #endif
    }
}

У вас есть два варианта для отладки.

  1. создать файл журнала: лично я предпочитаю отдельный файл журнала, как текстовый файл, а не с помощью журнала приложений или журнала событий.Но это будет стоить Вам большое от имени времени, потому что его все еще трудно понять, где точное местоположение ошибки
  2. преобразование приложения в консольное приложение : это позволит вам, все инструменты отладки, которые мы можем использовать в VS.

см. этой сообщение в блоге, которое я создал для этой темы.


просто вставить

Debugger.Break();

где-либо в вас код.

Например ,

internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static void Main()
        {
            Debugger.Break();
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }

Он ударил Debugger.Break(); при запуске программы.


лучший вариант-использовать''.

заключите код в блок if else для режима отладки и режима выпуска, как показано ниже, чтобы переключаться между режимом отладки и выпуска в visual studio,

#if DEBUG  // for debug mode
       **Debugger.Launch();**  //debugger will hit here
       foreach (var job in JobFactory.GetJobs())
            {
                //do something 
            }

#else    // for release mode
      **Debugger.Launch();**  //debugger will hit here
     // write code here to do something in Release mode.

#endif