Как отлаживать службы Windows в Visual Studio?

85

Можно ли отлаживать службы Windows в Visual Studio?

Я использовал код вроде

System.Diagnostics.Debugger.Break();

но он дает некоторую ошибку кода, например:

Я получил две ошибки события: eventID 4096 VsJITDebugger и «Служба не ответила на запрос запуска или управления своевременно».

PawanS
источник

Ответы:

124

Используйте следующий код в OnStartметоде обслуживания :

System.Diagnostics.Debugger.Launch();

Выберите вариант Visual Studio во всплывающем сообщении.

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

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Чираг
источник
9
Не забудьте запустить VS от имени администратора. Тогда он будет доступен в списке.
Майкл
1
Может кто-нибудь уточнить, что подразумевается под всплывающим сообщением? Когда / как это появляется?
Майк
@Mike, установи и запусти сервис, он появится.
Harshit
@Mike, это диалоговое окно Windows, которое появляется на интерактивном (авторизованном) рабочем столе и спрашивает, хотите ли вы выбрать приложение для отладки процесса. Если вы выберете VS, он запустит отладчик и подключится к процессу
Крис Джонсон,
63

Вы также можете попробовать это.

  1. Создайте службу Windows, установите и запустите…. То есть в вашей системе должны быть запущены службы Windows.
  2. Пока ваша служба работает, перейдите в меню « Отладка» , нажмите « Присоединить процесс» (или «Процесс в старой Visual Studio»).
  3. Найдите свою работающую службу, а затем убедитесь, что выбраны Показать процесс из всех пользователей и Показать процессы во всех сеансах , если нет, выберите его.

введите описание изображения здесь

  1. Нажмите Закрепить кнопку
  2. Нажмите ОК
  3. Нажмите Close
  4. Установите точку останова в желаемом месте и дождитесь выполнения. Он будет выполнять отладку автоматически, когда ваш код достигнет этой точки.
  5. Помните, поместите точку останова в доступное место , если это onStart (), остановите и снова запустите службу

(После долгих поисков я нашел это в разделе «Как отлаживать службы Windows в Visual Studio».)

PawanS
источник
2
Эта опция не отображается в обновлении 5
VS2013
1
Но вам нужно запустить свой Vs-2017 от имени администратора
чожа раджан
1
Я пробовал это. Он работал с точкой останова в onStop, но не с onStart, потому что, когда служба останавливается, отладчик
отключается
22

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

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

Лассе В. Карлсен
источник
ОУ !! ctrl + C, затем ctrl + V, я имею в виду новый проект. Я это делаю только я. Разве невозможно прикрепить какой-либо процесс к отладке или любому другому варианту, а не к отдельному проекту?
PawanS
1
Конечно, это возможно, но гораздо проще разработать службу Windows, если убрать служебную часть во время разработки.
Лассе В. Карлсен
хммм ... это хороший способ, но он просто удваивает работу. Я думал, что существует любой другой способ.
PawanS
9
Я не понимаю, как это «удвоит» работу. Конечно, это добавит небольшие накладные расходы на создание дополнительного проекта и выделение кода в сервисе для третьего проекта, но в остальном вы бы не сделали копию кода, вы бы переместили ее, и укажите ссылку на этот проект.
Лассе В. Карлсен
3
^ + 1. Это вдвое больше, чем управление сервисом, что практически равно нулю с точки зрения времени разработки, и вы делаете это только один раз. Отладка сервиса БОЛЬШАЯ - лучше сделайте двойной запуск из командной строки. Проверьте Google на наличие предопределенных классов-оболочек, позволяющих это сделать (они используют отражение для имитации класса обслуживания start / stop no teh). час работы, тонны экономии, чистый убыток: отрицательный - вы выигрываете время.
TomTom
14

Либо это, как было предложено Лассе В. Карлсеном, либо настройте цикл в своей службе, который будет ждать подключения отладчика. Самый простой - это

while (!Debugger.IsAttached)
{
    Thread.Sleep(1000);
}

... continue with code

Таким образом, вы можете запустить службу, а внутри Visual Studio выбрать «Присоединить к процессу ...» и присоединиться к своей службе, которая затем возобновит нормальное выполнение.

Паули Эстерё
источник
куда я должен поместить приведенный выше код ... и в процессе присоединения я получаю свою службу с именем disable
PawanS
3
мы тоже использовали такой код if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Кирилл Коваленко
этот код должен быть как можно раньше, прежде чем будет запущен любой код, который вы хотите отлаживать.
Pauli Østerø
@Pawan: In Start/ OnStart()я думаю
абатищев
@Kirill: используйте тильды, чтобы выделить код внутри комментариев, напримерfoo(bar)
abatishchev
7

Учитывая, что у ServiceBase.OnStartнего есть protectedвидимость, я пошел по маршруту отражения, чтобы добиться отладки.

private static void Main(string[] args)
{
    var serviceBases = new ServiceBase[] {new Service() /* ... */ };

#if DEBUG
    if (Environment.UserInteractive)
    {
        const BindingFlags bindingFlags =
            BindingFlags.Instance | BindingFlags.NonPublic;

        foreach (var serviceBase in serviceBases)
        {
            var serviceType = serviceBase.GetType();
            var methodInfo = serviceType.GetMethod("OnStart", bindingFlags);

            new Thread(service => methodInfo.Invoke(service, new object[] {args})).Start(serviceBase);
        }

        return;
    }
#endif

    ServiceBase.Run(serviceBases);
}

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

ta.speot.is
источник
Поскольку OnStart должен возвращаться быстро, вам не нужно делать это в другом потоке. Однако, если служба не запускает другой поток, ваш процесс немедленно завершится.
Мэтт Коннолли
@MattConnolly В последнем случае: при необходимости я изменяю приведенный выше код, чтобы запустить поток переднего плана, который спит вечно (до нормальной обработки).
ta.speot. 06
Это должен быть настоящий ответ. Красиво работает!
lentyai
4

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

Ниже мой рабочий код. Я последовал подходу, предложенному Microsoft.

Добавьте этот код в program.cs:

static void Main(string[] args)
{
    // 'If' block will execute when launched through Visual Studio
    if (Environment.UserInteractive)
    {
        ServiceMonitor serviceRequest = new ServiceMonitor();
        serviceRequest.TestOnStartAndOnStop(args);
    }
    else // This block will execute when code is compiled as a Windows application
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new ServiceMonitor()
        };
        ServiceBase.Run(ServicesToRun);
    }
}

Добавьте этот код в класс ServiceMonitor.

internal void TestOnStartAndOnStop(string[] args)
{
    this.OnStart(args);
    Console.ReadLine();
    this.OnStop();
}

Теперь перейдите в « Свойства проекта» , выберите вкладку «Приложение» и выберите « Тип вывода» как «Консольное приложение» при отладке или «Приложение Windows» при отладке, перекомпилируйте и установите службу.

Введите описание изображения здесь

Кумар Чандракету
источник
1
есть ли способ установить вывод для консольного приложения в отладочном и оконном приложении в выпуске?
кофифус 05
3

Можно сделать консольное приложение. Я использую эту mainфункцию:

    static void Main(string[] args)
    {
        ImportFileService ws = new ImportFileService();
        ws.OnStart(args);
        while (true)
        {
            ConsoleKeyInfo key = System.Console.ReadKey();
            if (key.Key == ConsoleKey.Escape)
                break;
        }
        ws.OnStop();
    }

Мой ImportFileServiceкласс точно такой же, как и в моем приложении службы Windows, за исключением наследника ( ServiceBase).

Керрубин
источник
это в том же проекте или я другой проект для этого консольного приложения
PawanS
Это 2 разных проекта с похожими классами. В моем случае это простая служба, в которой дублируется только класс ImportFileService. Когда я хочу разработать / протестировать, я использую consoleapp, а затем копирую / вставляю. Как сказал Лассе В. Карлсен, это программа отладки, вся логика (бизнес) сосредоточена на третьем проекте.
kerrubin
Разве OnStart не защищен?
Джо Филлипс
Да, есть. Вот почему я сказал «кроме наследника ( ServiceBase).». Мне легче отлаживать консольное приложение, но я понимаю, если это не убедит всех.
kerrubin 07
3

Я использую отличный пакет Nuget под названием ServiceProcess.Helpers.

И цитирую ...

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

Все это с помощью одной строчки кода.

http://windowsservicehelper.codeplex.com/

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

Кристоф Чанг
источник
Спасибо, что поделился! Это было, безусловно, самое простое решение!
Веллингтон Занелли
2

Вы также можете попробовать метод System.Diagnostics.Debugger.Launch () . Это помогает переместить указатель отладчика в указанное место, и вы сможете отлаживать код.

Перед этим шагом установите service.exe с помощью командной строки командной строки Visual Studio - installutil projectservice.exe.

Затем запустите свою службу из Панели управления -> Администрирование -> Управление компьютером -> Служба и приложение -> Службы -> Имя вашей службы.

Абхишек Шривастава
источник
2

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

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }
Ричард Хауэллс
источник
2

Я использую /Consoleпараметр в проекте Visual Studio DebugStart OptionsCommand line arguments :

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
         var runMode = args.Contains(@"/Console")
             ? WindowsService.RunMode.Console
             : WindowsService.RunMode.WindowsService;
         new WinodwsService().Run(runMode);
    }
}


public class WindowsService : ServiceBase
{
    public enum RunMode
    {
        Console,
        WindowsService
    }

    public void Run(RunMode runMode)
    {
        if (runMode.Equals(RunMode.Console))
        {
            this.StartService();
            Console.WriteLine("Press <ENTER> to stop service...");
            Console.ReadLine();

            this.StopService();
            Console.WriteLine("Press <ENTER> to exit.");
            Console.ReadLine();
        }
        else if (runMode.Equals(RunMode.WindowsService))
        {
            ServiceBase.Run(new[] { this });
        }
    }

    protected override void OnStart(string[] args)
    {
        StartService(args);
    }

    protected override void OnStop()
    {
        StopService();
    }

    /// <summary>
    /// Logic to Start Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StartService(params string[] args){ ... }

    /// <summary>
    /// Logic to Stop Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StopService() {....}
}
Шон М
источник
2

Я нашел этот вопрос, но думаю, что нет четкого и простого ответа.

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

Я нашел эти блестящие руководства, которые делают это:

Начните с изменения проектов Output typeна Console Application.

Введите описание изображения здесь

Измените свой, Program.csчтобы он выглядел так:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        // Startup as service.
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };

        if (Environment.UserInteractive)
        {
            RunInteractive(ServicesToRun);
        }
        else
        {
            ServiceBase.Run(ServicesToRun);
        }
    }
}

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

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);
}
Огглас
источник
Отличный код! Просто, эффективно. +1. Но так же легко я сделал это приложение Forms. Я действительно ненавижу консольные приложения. Кроме того, вы можете легко реализовать кнопку формы для каждого события службы.
Роланд
1

К сожалению, если вы пытаетесь отладить что-либо в самом начале работы службы Windows, «присоединение» к запущенному процессу не сработает. Я пробовал использовать Debugger.Break () в процедуре OnStart, но в 64-битном приложении, скомпилированном в Visual Studio 2010, команда break просто выдает такую ​​ошибку:

System error 1067 has occurred.

На этом этапе вам необходимо настроить параметр «Выполнение файла образа» в вашем реестре для исполняемого файла. На настройку уходит пять минут, и работает она очень хорошо. Вот статья Microsoft, в которой приведены подробности:

Как: запустить отладчик автоматически

Брайан
источник
1

Попробуйте собственную командную строку для событий после сборки в Visual Studio .

Попробуйте добавить это в пост-сборку:

@echo off
sc query "ServiceName" > nul
if errorlevel 1060 goto install
goto stop

:delete
echo delete
sc delete "ServiceName" > nul
echo %errorlevel%
goto install

:install
echo install
sc create "ServiceName" displayname= "Service Display Name" binpath= "$(TargetPath)" start= auto > nul
echo %errorlevel%
goto start

:start
echo start
sc start "ServiceName" > nul
echo %errorlevel%
goto end

:stop
echo stop
sc stop "ServiceName" > nul
echo %errorlevel%
goto delete

:end

Если ошибка сборки с таким сообщением Error 1 The command "@echo off sc query "ServiceName" > nul, Ctrl+ Cзатем Ctrl+ Vсообщение об ошибке в Блокноте и посмотрите последнее предложение сообщения.

Можно сказать exited with code x. Найдите здесь код какой-либо распространенной ошибки и посмотрите, как ее исправить.

1072 -- Marked for deletion → Close all applications that maybe using the service including services.msc and Windows event log.
1058 -- Can't be started because disabled or has no enabled associated devices → just delete it.
1060 -- Doesn't exist → just delete it.
1062 -- Has not been started → just delete it.
1053 -- Didn't respond to start or control → see event log (if logged to event log). It may be the service itself throwing an exception.
1056 -- Service is already running → stop the service, and then delete.

Подробнее о кодах ошибок здесь .

И если ошибка сборки с таким сообщением,

Error    11    Could not copy "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". Exceeded retry count of 10. Failed.    ServiceName
Error    12    Unable to copy file "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". The process cannot access the file 'bin\Debug\ServiceName.exe' because it is being used by another process.    ServiceName

откройте cmd, а затем попробуйте сначала убить его с помощью taskkill /fi "services eq ServiceName" /f

Если все в порядке, F5должно быть достаточно его отладки.

asakura89
источник
0

В OnStartметоде делаем следующее.

protected override void OnStart(string[] args)
{
    try
    {
        RequestAdditionalTime(600000);
        System.Diagnostics.Debugger.Launch(); // Put breakpoint here.

        .... Your code
    }
    catch (Exception ex)
    {
        .... Your exception code
    }
}

Затем запустите командную строку от имени администратора и введите следующее:

c:\> sc create test-xyzService binPath= <ProjectPath>\bin\debug\service.exe type= own start= demand

Вышеупомянутая строка создаст test-xyzService в списке служб.

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

c:\> sc start text-xyzService

Чтобы остановить службу:

c:\> sc stop test-xyzService

Чтобы удалить или удалить:

c:\> sc delete text-xyzService
user3942119
источник
0

Отладка службы Windows через http (протестировано с VS 2015 Update 3 и .Net FW 4.6)

Во-первых, вы должны создать консольный проект в своем решении VS (Добавить -> Новый проект -> Консольное приложение).

В новом проекте создайте класс ConsoleHost с этим кодом:

class ConsoleHost : IDisposable
{
    public static Uri BaseAddress = new Uri(http://localhost:8161/MyService/mex);
    private ServiceHost host;

    public void Start(Uri baseAddress)
    {
        if (host != null) return;

        host = new ServiceHost(typeof(MyService), baseAddress ?? BaseAddress);

        //binding
        var binding = new BasicHttpBinding()
        {
            Name = "MyService",
            MessageEncoding = WSMessageEncoding.Text,
            TextEncoding = Encoding.UTF8,
            MaxBufferPoolSize = 2147483647,
            MaxBufferSize = 2147483647,
            MaxReceivedMessageSize = 2147483647
        };

        host.Description.Endpoints.Clear();
        host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress ?? BaseAddress);

        // Enable metadata publishing.
        var smb = new ServiceMetadataBehavior
        {
            HttpGetEnabled = true,
            MetadataExporter = { PolicyVersion = PolicyVersion.Policy15 },
        };

        host.Description.Behaviors.Add(smb);

        var defaultBehaviour = host.Description.Behaviors.OfType<ServiceDebugBehavior>().FirstOrDefault();
        if (defaultBehaviour != null)
        {
            defaultBehaviour.IncludeExceptionDetailInFaults = true;
        }

        host.Open();
    }

    public void Stop()
    {
        if (host == null)
            return;

        host.Close();
        host = null;
    }

    public void Dispose()
    {
        this.Stop();
    }
}

А это код класса Program.cs:

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        var baseAddress = new Uri(http://localhost:8161/MyService);
        var host = new ConsoleHost();
        host.Start(null);
        Console.WriteLine("The service is ready at {0}", baseAddress);
        Console.WriteLine("Press <Enter> to stop the service.");
        Console.ReadLine();
        host.Stop();
    }
}

Такие конфигурации, как строки подключения, следует скопировать в файл App.config проекта консоли.

Чтобы запустить консоль, щелкните правой кнопкой мыши проект консоли и выберите «Отладка» -> «Начать новый экземпляр».

mggSoft
источник
0

Просто добавьте подрядчика в свой класс обслуживания (если у вас его еще нет). Ниже вы можете проверить и посмотреть пример для Visual Basic .net.

Public Sub New()
   OnStart(Nothing) 
End Sub

После этого щелкните проект правой кнопкой мыши и выберите « Отладка -> Начать новый экземпляр ».

Педро Мартин
источник