Существует ли более простой способ пошагового выполнения кода, чем запуск службы через диспетчер управления службами Windows и последующее присоединение отладчика к потоку? Это довольно громоздко, и мне интересно, есть ли более простой подход.
Существует ли более простой способ пошагового выполнения кода, чем запуск службы через диспетчер управления службами 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
, кроме как запуск (неинтерактивная) служба?
System.Diagnostics.Debugger.IsAttached
вместо Environment.UserInteractive
.
Когда я создал новый сервисный проект несколько недель назад, я нашел этот пост. Хотя было много замечательных предложений, я так и не нашел решение, которое хотел: возможность вызывать классы обслуживания 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);
}
Это весь необходимый код, но я также написал пошаговое руководство с объяснениями.
walk through
), это убедиться, что вы вошли в свойства проекта и изменили тип вывода на Console Application
перед тем, как пытаться скомпилировать и запустить. Найдите это в Project Properties -> Application -> Output type -> Console Application
. Кроме того, для того, чтобы это работало правильно для меня, мне пришлось запустить приложение с помощью start
команды. Пример: C:\"my app name.exe" -service
не будет работать для меня. Вместо этого я использовалC:\start /wait "" "my app name.exe" -service
Иногда важно проанализировать, что происходит во время запуска службы.Присоединение к процессу здесь не помогает, потому что вы недостаточно быстры, чтобы подключить отладчик во время запуска службы.
Краткий ответ: я использую следующие 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 ». появляться. Нажмите, как показано на скриншоте: 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 строки в существующий сервисный код, что позволит вам быстро получить контроль и отладку.
base.RequestAdditionalTime(600000)
служба управления службами не будет прерывать службу в течение 10 минут, если она не вызывает в base.OnStart(args)
течение этого промежутка времени). Кроме того, я помню, что UAC также будет прерван, если вы не введете учетные данные администратора через некоторое время (я не знаю, сколько именно секунд, но я думаю, что вы должны ввести его в течение минуты, иначе UAC прерывает работу) , который завершит сеанс отладки.
Обычно я инкапсулирую логику службы в отдельном классе и запускаю ее из класса «бегун». Этот класс бегуна может быть реальной службой или просто консольным приложением. Итак, ваше решение имеет (по крайней мере) 3 проекта:
/ConsoleRunner
/....
/ServiceRunner
/....
/ApplicationLogic
/....
Это видео на 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
раздел вызовет логику
ОБНОВИТЬ
Этот подход, безусловно, самый простой:
http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx
Я оставляю свой оригинальный ответ ниже для потомков.
Мои сервисы, как правило, имеют класс, который инкапсулирует Timer, так как я хочу, чтобы сервис регулярно проверял, есть ли какая-то работа для него.
Мы обновляем класс и вызываем StartEventLoop () во время запуска службы. (Этот класс также можно легко использовать из консольного приложения.)
Приятным побочным эффектом этого дизайна является то, что аргументы, с помощью которых вы устанавливаете таймер, могут использоваться для задержки перед тем, как служба фактически начнет работать, так что у вас будет время подключить отладчик вручную.
ps Как вручную прикрепить отладчик к запущенному процессу ...?
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
// do something
для завершения требуется более 30 минут?
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
}
OnStart
это protected
и вы не можете изменить уровень доступа :(
Я использовал переключатель командной строки, который запускал программу как службу или как обычное приложение. Затем в своей среде IDE я установил переключатель так, чтобы я мог шагать по своему коду.
С некоторыми языками вы можете определить, работает ли он в IDE, и выполнить это переключение автоматически.
Какой язык вы используете?
Используйте библиотеку 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.
Я думаю, что это зависит от того, какую операционную систему вы используете, Vista гораздо сложнее подключить к Сервисам из-за разделения между сессиями.
Два варианта, которые я использовал в прошлом:
Надеюсь это поможет.
Мне нравится иметь возможность отлаживать каждый аспект моего сервиса, включая любую инициализацию в 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) Запустите реальный сервис и наслаждайтесь отладкой.
Когда я пишу сервис, я помещаю всю сервисную логику в проект dll и создаю два «хоста», которые обращаются к этой dll, один - сервис Windows, а другой - приложение командной строки.
Я использую приложение командной строки для отладки и присоединяю отладчик к реальному сервису только для ошибок, которые не могу воспроизвести в приложении командной строки.
Если вы используете этот подход, просто помните, что вы должны тестировать весь код во время работы в реальном сервисе, в то время как инструмент командной строки является хорошим средством отладки, это другая среда и он не ведет себя точно так же, как реальный сервис.
При разработке и отладке службы Windows я обычно запускаю ее как консольное приложение, добавляя параметр запуска / console и проверяя его. Делает жизнь намного проще.
static void Main(string[] args) {
if (Console.In != StreamReader.Null) {
if (args.Length > 0 && args[0] == "/console") {
// Start your service work.
}
}
}
Для отладки Windows Services я объединяю GFlags и файл .reg, созданный regedit.
Или сохраните следующие фрагменты и замените servicename.exe на нужное имя исполняемого файла.
debugon.reg:
Редактор реестра Windows, версия 5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Параметры выполнения файла изображения \ servicename.exe] "GlobalFlag" = "0x00000000" "Debugger" = "vsjitdebugger.exe"
debugoff.reg:
Редактор реестра Windows, версия 5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Параметры выполнения файла изображения \ servicename.exe] "GlobalFlag" = "0x00000000"
Для рутинного мелкого программирования я сделал очень простой трюк, чтобы легко отладить мой сервис:
При запуске службы я проверяю параметр командной строки "/ debug". Если служба вызывается с этим параметром, я не делаю обычный запуск службы, а вместо этого запускаю все прослушиватели и просто отображаю окно сообщения «Выполняется отладка, нажмите ОК для завершения».
Поэтому, если мой сервис запускается обычным способом, он запускается как сервис, если он запускается с параметром командной строки / debug, он будет действовать как обычная программа.
В VS я просто добавлю / debug в качестве параметра отладки и сразу запущу сервисную программу.
Таким образом, я легко отлаживаю для большинства мелких добрых задач Конечно, некоторые вещи все равно нужно будет отлаживать как сервис, но для 99% это достаточно хорошо.
Я использую вариант ответа JOP. Используя параметры командной строки, вы можете установить режим отладки в IDE со свойствами проекта или через диспетчер служб Windows.
protected override void OnStart(string[] args)
{
if (args.Contains<string>("DEBUG_SERVICE"))
{
Debugger.Break();
}
...
}
Для устранения неполадок в существующей программе 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
Просто поместите свой обед отладчика в любом месте и присоедините 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 # для создания нового приложения-службы https://github.com/HarpyWar/windows-service-template.
Есть автоматически обнаруженный консольный / сервисный режим, автоустановщик / деинсталлятор вашего сервиса и несколько наиболее часто используемых функций.
Вот простой метод, который я использовал для тестирования сервиса, без каких-либо дополнительных методов «отладки» и с интегрированными 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 });
}
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
}
}
У вас есть два варианта отладки.
Пожалуйста , обратитесь ЭТИМ сообщением в блоге , что я создал для этой темы.
Просто вставьте
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();
когда вы запустите вашу программу.
Наилучшим вариантом является использование пространства имен 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