Вопрос

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

Это было полезно?

Решение

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

Обновить: В качестве альтернативы #if DEBUG прагмы, вы также можете использовать Conditional("DEBUG_SERVICE") атрибут.

[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
  }
}

Это имело бы то "преимущество", что вы можете просто запустить свое приложение с помощью двойного щелчка (хорошо, если вам это действительно нужно) и что вы можете просто нажать F5 в Visual Studio (без необходимости изменять параметры проекта, чтобы включить это /console Вариант).

Технически, 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);
    }
}

Тот Самый RunInteractive помощник использует отражение для вызова защищенного 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);
}

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

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

Короткий ответ таков: я использую следующее 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, но должен быть действителен и для других сред.


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

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

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

UACPrompt

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

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

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

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

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

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

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

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

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

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

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

Это Видео Фабио Скопеля на YouTube довольно хорошо объясняет, как отладить службу Windows...сам метод выполнения этого начинается в 4:45 на видео...

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

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()
    {
    }

Как это работает

По сути, вы должны создать public void OnDebug() это вызывает OnStart(string[] args) поскольку он защищен и недоступен снаружи.Тот Самый void Main() программа добавлена с помощью #if препроцессор с #DEBUG.

Visual Studio определяет DEBUG если проект скомпилирован в режиме отладки.Это позволит разделу отладки (ниже) выполняться при выполнении условия true

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

И он будет работать точно так же, как консольное приложение, как только все пойдет нормально, вы сможете сменить режим Release и обычный else раздел вызовет логику

Обновить

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

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

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


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

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

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

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

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

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

#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
}

Вы также можете запустить службу через командную строку (sc.exe).

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

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

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

На каком языке вы говорите?

Используйте Верхняя полка библиотека.

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

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.

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

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

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

Надеюсь, это поможет.

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

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

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

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

Я делаю это, создавая второй сервис в том же проекте для использования для отладки.Служба отладки, запущенная как обычно (т. е.в плагине services 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) Добавьте реальную службу И службу отладки в установщик проекта для проекта service:

enter image description here

Обе службы (реальная и отладочная) включаются при добавлении выходных данных проекта службы в проект установки для службы.После установки оба сервиса появятся в плагине service.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.
        }
    }
}

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

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

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

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


debugon.reg:

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.reg:

Windows Registry Editor Version 5.00

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

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

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

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

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

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

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

Я использую вариант ответа ДЖОПА.Используя параметры командной строки, вы можете установить режим отладки в IDE с помощью свойств проекта или через диспетчер служб Windows.

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

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

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

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

#if DEBUG
    Debugger.Launch();
#endif

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

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

Используйте шаблон службы Windows C # project для создания нового приложения-службы 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top