Чи є простіший спосіб перейти через код, ніж запустити службу через диспетчер управління службою 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
}
}
Це матиме "перевагу", що ви можете просто запустити додаток за допомогою подвійного клацання (OK, якщо вам це дійсно потрібно), і ви можете просто натиснути 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 Debugger . Він запитує, чи хочете ви налагоджувати за допомогою видаленого налагоджувача. Перш ніж натиснути Yes, виберіть, що не хочете відкривати новий екземпляр (2-й варіант) - новий екземпляр тут не буде корисним, оскільки вихідний код не відображатиметься. Таким чином, ви вибираєте екземпляр Visual Studio, який ви відкрили раніше:
Після того, як ви натиснули Yes, через деякий час Visual Studio покаже жовту стрілку прямо у рядку, де є Debugger.Launch
виписка, і ви зможете налагодити свій код (метод MyInitOnStart
, який містить вашу ініціалізацію).
Натискання F5негайно продовжує виконання, поки не буде досягнуто наступного підготовленого пункту розриву.
Підказка: Щоб продовжувати роботу служби, виберіть Налагодження -> Від'єднати всі . Це дозволяє запустити клієнта, який спілкується з сервісом після його правильного запуску, і ви закінчите налагоджувати код запуску. Якщо натиснути Shift+F5 (зупинити налагодження), послуга припинить. Замість цього слід скористатись панеллю управління службою, щоб зупинити її.
Зауважте, що
Якщо ви створили Release, тоді код налагодження автоматично видаляється і послуга працює нормально.
Я використовую 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
Я залишаю свою оригінальну відповідь нижче для нащадків.
Мої служби, як правило, мають клас, який інкапсулює Таймер, оскільки я хочу, щоб служба регулярно перевіряла, чи є якась робота для цього.
Ми створюємо новий клас та зателефонуємо 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).
Я перестала робити це таким чином, оскільки іноді ми забули побудувати у версії та мати перерву налагодження в додатку, що працює на клієнтській демонстрації (бентежно!).
#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 бібліотеку .
Створіть консольний додаток, а потім налаштуйте налаштування у вашому Main
class Program
{
static void Main(string[] args)
{
HostFactory.Run(x =>
{
// setup service start and stop.
x.Service<Controller>(s =>
{
s.ConstructUsing(name => new Controller());
s.WhenStarted(controller => controller.Start());
s.WhenStopped(controller => controller.Stop());
});
// setup recovery here
x.EnableServiceRecovery(rc =>
{
rc.RestartService(delayInMinutes: 0);
rc.SetResetPeriod(days: 0);
});
x.RunAsLocalSystem();
});
}
}
public class Controller
{
public void Start()
{
}
public void Stop()
{
}
}
Щоб налагодити службу, просто натисніть F5 у візуальній студії.
Щоб встановити службу, введіть 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 я поєдную GFlags та .reg-файл, створений regedit.
Або збережіть наведені нижче фрагменти та замініть servicename.exe на потрібне ім'я виконавця.
debugon.reg:
Версія редактора реєстру Windows 5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Параметри виконання файлів зображень \ servicename.exe] "GlobalFlag" = "0x00000000" "Налагоджувач" = "vsjitdebugger.exe"
debugoff.reg:
Версія редактора реєстру Windows 5.00 [HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Параметри виконання файлів зображень \ servicename.exe] "GlobalFlag" = "0x00000000"
Для звичайного програмування дрібних матеріалів я зробив дуже простий трюк, щоб легко налагодити службу:
Після запуску послуги я перевіряю параметр командного рядка "/ debug". Якщо служба викликається з цим параметром, я не роблю звичайний запуск сервісу, а замість цього запускаю всіх слухачів і просто показую скриньку повідомлень "Налагодження в процесі роботи, натисніть ОК, щоб закінчити".
Отже, якщо моя служба запускається звичайним способом, вона почне працювати як служба, якщо її запустити з параметру / налагодження командного рядка, вона буде діяти як звичайна програма.
У VS я просто додам / налагоджую як параметр налагодження та запускаю службову програму безпосередньо.
Таким чином я можу легко налагоджувати проблеми для більшості дрібних проблем. Звичайно, деякі речі все-таки потрібно буде налагодити як сервіс, але на 99% це досить добре.
Я використовую варіацію відповіді JOP. Використовуючи параметри командного рядка, ви можете встановити режим налагодження в 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
Просто поставте свій обід налагоджувача куди завгодно і приєднайте Visualstudio до запуску
#if DEBUG
Debugger.Launch();
#endif
Також вам потрібно запустити VS як адміністратора, і вам потрібно дозволити, що процес може автоматично налагоджуватися налагодженням різного користувача (як пояснено тут ):
reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f
Скористайтеся проектом C # шаблону служби Windows для створення нового додатка служби https://github.com/HarpyWar/windows-service-template
Там консольний / сервісний режим автоматично виявляється, включений автоматичний інсталятор / деінсталятор вашої послуги та кілька найбільш використовуваних функцій.
Ось простий метод, який я використовував для тестування служби, без додаткових методів "Налагодження" та з інтегрованими тестами VS Unit.
[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 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