Есть ли более простой способ выполнить код, чем запустить службу через диспетчер управления службами Windows, а затем добавить отладчик в поток? Это довольно громоздко, и мне интересно, есть ли более простой подход.
Простой способ отладки службы Windows
Ответ 1
Если я хочу быстро отладить сервис, я просто добавляю 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 */
}
Там код будет включен только во время отладочных сборок. Несмотря на это, может быть полезно создать отдельную конфигурацию сборки для отладки службы.
Ответ 2
Я также считаю наличие отдельной "версии" для нормального выполнения и как сервис - это путь, но действительно ли нужно выделять отдельный ключ командной строки для этой цели?
Не могли бы вы просто сделать:
public static int Main(string[] args)
{
if (!Environment.UserInteractive)
{
// Startup as service.
}
else
{
// Startup as application
}
}
У этого была бы "польза", что вы можете просто запустить свое приложение с помощью doubleclick (хорошо, если вам это действительно нужно), и что вы можете просто нажать F5 в Visual Studio (без необходимости изменять настройки проекта чтобы включить этот параметр /console
).
Технически, Environment.UserInteractive
проверяет, установлен ли флаг WSF_VISIBLE
для текущей станции окна, но есть ли какая-либо другая причина, по которой она вернет false
, кроме того, что будет выполняться как (неинтерактивная) служба
Ответ 3
Когда я создал новый сервисный проект несколько недель назад, я нашел этот пост. Хотя есть много замечательных предложений, я все еще не нашел решение, которое я хотел: возможность вызова методов классов обслуживания 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
Я обычно делаю инкапсуляцию логики службы в отдельный класс и начинаю с класса "бегун". Этот класс runner может быть фактическим сервисом или просто консольным приложением. Таким образом, ваше решение имеет (по крайней мере) 3 проекта:
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
Ответ 5
Иногда важно проанализировать, что происходит во время запуска сервиса. Присоединение к процессу здесь не помогает, потому что вы недостаточно быстры, чтобы подключить отладчик во время запуска службы.
Краткий ответ: я использую следующие 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
службу в "ручном" режиме (используя либо утилиту InstallUtil
из командной строки VS, либо запустите подготовленный вами проект установщика службы). Откройте Visual Studio перед запуском службы и загрузите решение, содержащее исходный код службы - установите дополнительные точки останова, как вам требуется в Visual Studio, - затем запустите службу через панель управления службами.
Из-за кода Debugger.Launch
это вызовет диалоговое окно "Произошло необработанное исключение Microsoft.NET Framework в Servicename.exe". появляться. Нажмите Yes, debug Servicename.exe, как показано на скриншоте:
Впоследствии, особенно в Windows 7 UAC может предложить ввести учетные данные администратора. Введите их и перейдите к Yes:
После этого появляется хорошо известное окно отладчика Visual Studio Just-In-Time. Он спросит вас, хотите ли вы отладить с помощью удаленного отладчика. Прежде чем нажать Yes, выберите, что вы не хотите открывать новый экземпляр (2-й вариант) - новый экземпляр здесь не будет полезен, поскольку исходный код не будет отображаться. Таким образом, вы выбираете экземпляр Visual Studio, который вы открыли ранее:
После того как вы нажали Yes, через некоторое время Visual Studio покажет желтую стрелку прямо в строке, где находится оператор Debugger.Launch
и вы сможете отлаживать свой код (метод MyInitOnStart
, который содержит вашу инициализацию).
Нажатие F5 немедленно продолжает выполнение, пока не будет достигнута следующая подготовленная вами точка останова.
Подсказка: чтобы сохранить работу сервиса, выберите " Отладка" → "Отключить все". Это позволяет запустить клиент, обменивающийся данными со службой после того, как он правильно запустился и вы закончили отладку кода запуска. Если вы нажмете Shift + F5 (прекратить отладку), это приведет к прекращению обслуживания. Вместо того, чтобы сделать это, вы должны использовать панель управления службами, чтобы остановить его.
Обратите внимание, что
-
Если вы создаете Релиз, то код отладки автоматически удаляется и служба работает нормально.
-
Я использую
Debugger.Launch()
, который запускает и присоединяет отладчик. Я также протестировалDebugger.Break()
, который не работал, поскольку при запуске службы отладчик еще не подключен (что вызвало "Ошибка 1067: процесс неожиданно завершился".). -
RequestAdditionalTime
устанавливает более длительный тайм-аут для запуска службы (он не задерживает сам код, но немедленно продолжит выполнение оператораDebugger.Launch
). В противном случае время ожидания по умолчанию для запуска службы слишком короткое, и запуск службы завершается неудачно, если вы не вызываетеbase.Onstart(args)
достаточно быстро из отладчика. Практически, тайм-аут в 10 минут позволяет избежать появления сообщения "служба не отвечает..." сразу после запуска отладчика. -
Как только вы привыкнете к этому, этот метод очень прост, потому что он просто требует, чтобы вы добавили 4 строки к существующему сервисному коду, что позволит вам быстро получить контроль и отладку.
Ответ 6
Это видео на YouTube от Фабио Скопеля объясняет, как довольно хорошо отлаживать службу Windows... фактический способ сделать это начинается в 4:45 в видео...
Вот код, описанный в видео... в файле Program.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()
{
}
Как это устроено
По сути, вы должны создать public void OnDebug()
который вызывает OnStart(string[] args)
поскольку он защищен и недоступен снаружи. Программа void Main()
добавлена с препроцессором #if
с #DEBUG
.
Visual Studio определяет DEBUG
если проект скомпилирован в режиме отладки. Это позволит выполнить раздел отладки (ниже), когда условие выполнено
Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
И он будет работать так же, как консольное приложение, как только все пойдет хорошо, вы можете изменить режим Release
а обычный раздел else
вызовет логику
Ответ 7
UPDATE
Этот подход является самым простым:
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).
Я прекратил это делать, потому что иногда мы забудем строить в Release и отлаживать отладчик в приложении, работающем на демо-клиенте (смущаем!).
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
#endif
Ответ 8
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
}
Ответ 9
Вы также можете запустить службу через командную строку (sc.exe).
Лично я запустил код как автономную программу на этапе отладки, и когда большинство ошибок будут сглажены, перейдите в режим работы как сервис.
Ответ 10
То, что я делал, это иметь коммутатор командной строки, который запускает программу либо как сервис, либо как обычное приложение. Затем, в моей среде IDE, я бы установил переключатель, чтобы я мог выполнить мой код.
На некоторых языках вы можете определить, работает ли она в среде IDE, и выполнять этот переключатель автоматически.
Какой язык вы используете?
Ответ 11
Я думаю, что это зависит от того, какую ОС вы используете, Vista гораздо сложнее подключить к сервисам из-за разделения между сеансами.
Два варианта, которые я использовал в прошлом:
- Используйте GFlags (в средствах отладки для Windows), чтобы настроить постоянный отладчик для процесса. Это существует в разделе реестра "Параметры файла изображения" и невероятно полезно. Я думаю, вам нужно настроить настройки службы, чтобы включить "Взаимодействие с рабочим столом". Я использую это для всех типов отладки, а не только для служб.
- Другой вариант - немного отделить код, чтобы часть сервиса была взаимозаменяема при обычном запуске приложения. Таким образом, вы можете использовать простой флаг командной строки и запускать как процесс (а не службу), что значительно облегчает его отладку.
Надеюсь, что это поможет.
Ответ 12
Используйте библиотеку TopShelf.
Создайте консольное приложение и настройте настройку в главном
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 install"
Затем вы можете запустить и остановить службу в диспетчере служб Windows.
Ответ 13
Когда я пишу службу, я помещаю всю логику обслуживания в проект dll и создаю два "хоста", которые вызывают в эту DLL, одна из них - служба Windows, а другая - приложение командной строки.
Я использую приложение командной строки для отладки и прикрепляю отладчик к реальной службе только для ошибок, которые я не могу воспроизвести в приложении командной строки.
Я использую этот подход, просто помню, что вам нужно протестировать весь код во время работы в реальной службе, в то время как инструмент командной строки - хорошая отладочная помощь, это другая среда, и она не ведет себя точно как реальная служба.
Ответ 14
Мне нравится уметь отлаживать каждый аспект моей службы, включая любую инициализацию в 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) Добавьте реальный сервис и службу отладки в установщик проекта для проекта службы:
Обе службы (реальная и отладочная) включаются при добавлении вывода проекта службы в проект установки для службы. После установки обе службы появятся в плагине MMC service.msc.
3) Запустите службу отладки в MMC.
4) В Visual Studio присоедините отладчик к процессу, запущенному службой отладки.
5) Запустите реальный сервис и наслаждайтесь отладкой.
Ответ 15
При разработке и отладке службы Windows я обычно запускаю ее как консольное приложение, добавляя параметр запуска/консоли и проверяя это. Делает жизнь намного легче.
static void Main(string[] args) {
if (Console.In != StreamReader.Null) {
if (args.Length > 0 && args[0] == "/console") {
// Start your service work.
}
}
}
Ответ 16
Как насчет Debugger.Break() в первой строке?
Ответ 17
Для отладки служб Windows я объединяю GFlags и файл .reg, созданный regedit.
- Запустите GFlags, указав exe-name и vsjitdebugger
- Запустите regedit и перейдите в папку, где GFlags устанавливает свои параметры.
- Выберите "Экспорт ключа" в меню файла
- Сохраните этот файл где-нибудь с расширением .reg
- В любое время, когда вы хотите отладить службу: doubleclick в файле .reg
- Если вы хотите остановить отладку, дважды щелкните по второму .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"
Ответ 18
Для рутинного программирования небольших вещей я сделал очень простой трюк, чтобы легко отладить мой сервис:
При запуске службы я проверяю параметр командной строки "/debug". Если служба вызывается с этим параметром, я не запускаю обычный запуск службы, а вместо этого запускаю всех слушателей и просто показываю сообщение "Выполняется отладка, нажмите" ОК "для завершения".
Итак, если моя служба запускается обычным способом, она запускается как служба, если она запущена с параметром командной строки /debug, она будет действовать как обычная программа.
В VS я просто добавлю/отлаживаю как параметр отладки и запускаю сервисную программу напрямую.
Таким образом, я могу легко отлаживать большинство проблем с небольшим видом. Конечно, некоторые вещи еще нужно отлаживать как сервис, но на 99% это достаточно хорошо.
Ответ 19
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
Ответ 20
Я использую вариант ответа JOP. Используя параметры командной строки, вы можете установить режим отладки в среде IDE с помощью свойств проекта или с помощью диспетчера служб Windows.
protected override void OnStart(string[] args)
{
if (args.Contains<string>("DEBUG_SERVICE"))
{
Debugger.Break();
}
...
}
Ответ 21
Это довольно немного после вопроса, но может быть полезно для использования в будущем.
Если вы можете, я бы предложил использовать отличный проект TopShelf. Это значительно облегчает разработку и отладку Windows Service, и добавление упрощает развертывание.
Проверьте это: http://topshelf-project.com/
Ответ 22
Для устранения неполадок в существующей программе Windows Service используйте "Debugger.Break()", как предполагали другие ребята.
Для новой программы Windows Service я предложил бы использовать метод Джеймса Майкла Харе http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template-redux.aspx
Ответ 23
Просто положите обед отладчика где угодно и прикрепите 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
Ответ 24
Используйте шаблон службы Windows С# для создания нового сервисного приложения https://github.com/HarpyWar/windows-service-template
Доступен режим консоли/службы, автоматический установщик/деинсталлятор вашей службы и несколько наиболее используемых функций.
Ответ 25
Вот простой метод, который я использовал для тестирования службы без каких-либо дополнительных методов "Отладки" и с интегрированными VS Unit Tests.
[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 });
}
Ответ 26
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
}
}
Ответ 27
У вас есть две возможности для отладки.
- создать файл журнала. Лично я предпочитаю отдельный файл журнала, например текстовый файл, с использованием журнала приложений или журнала событий. Но это будет дорого стоить от имени времени, потому что его еще трудно понять, где именно точная ошибка расположение
- Преобразование приложения в консольное приложение: это позволит вам, всем инструментам отладки, которые мы можем использовать в VS.
Пожалуйста, обратитесь ЭТО сообщение в блоге, которое я создал для этой темы.
Ответ 28
Просто вставьте
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();
при запуске вашей программы.
Ответ 29
Наилучшим вариантом является использование пространства имен System.Diagnostics.
Вложите свой код в блок 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
Ответ 30
Для решения этой проблемы создан пакет nuget: install-package WindowsService.Gui
Что делает пакет?
Это помогает путем создания пользовательского интерфейса Play/Stop/Pause при работе с подключенным отладчиком, но также позволяет устанавливать и запускать службу Windows в среде служб Windows. Все это с одной строкой кода! Что такое Service Helper Будучи тем, кто много пишет для Windows Services, может быть неприятно иметь дело с головными болями, связанными с отладкой сервисов. Часто это включает в себя уловки, взломы и частичные обходные пути для проверки всего вашего кода. У разработчиков Windows Services не было опыта "только что нажал F5".
Service Helper решает эту проблему, вызывая отображение пользовательского интерфейса, если подключен отладчик, имитирующий (как можно ближе) среду служб Windows.
Проект github находится здесь: https://github.com/wolfen351/windows-service-gui
Как пользоваться?
Самый простой способ получить Windows Service Helper в вашем проекте - использовать пакет ServiceProcess.Helpers NuGet на официальном фиде NuGet.
Просто внесите несколько изменений в типичный код "Program.cs" для вашего приложения:
using System.ServiceProcess;
using ServiceProcess.Helpers; //HERE
namespace DemoService
{
static class Program
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
//ServiceBase.Run(ServicesToRun);
ServicesToRun.LoadServices(); //AND HERE
}
}
}
Раскрытие информации: я поддерживаю этот проект