Отладка службы windows c

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

Зачем это всё?

Если вы взялись за написание службы, то встаёт вопрос об её отладке. В интернетах в основном пишут про способ, который заключается в подключении отладчика к уже запущенному процессу службы, про альтернативу которому я и хочу рассказать. Т.к. служба — это не совсем обычный процесс Windows, просто запустить её из Visual Studio у вас не получится.

Установка службы

При попытке запуска вам будет показано сообщение, что, мол, служба запуститься не может и вам нужно использовать installutil.exe для её установки.

Windows Service Start Failure message

Для установки службы вам потребуется консоль Visual Studio, которая находится по пути

Пуск → Программы → Visual Studio 2008 → Visual Studio Tools → Visual Studio 2008 Command Prompt

(в зависимости от версии установленной VS путь может отличаться)
После запуска консоли переходим в директорию, в которую собирается ваша служба в режиме Debug и устанавливаем её:

installutil.exe /i <имя вашей сборки со службой>

Более вам installutil.exe не потребуется. Для удобства запускаем консоль управления службами Windows:

services.msc

Теперь консоль Visual Studio можно закрыть.
Переходим к самому интересному :-)

Отладка службы

Способ №1 (подключение отладчика)

  1. Запустите службу с помощью консоли управления службами Windows.
  2. В Visual Studio выберите

    Tools → Connect To Process

    в появившемся диалоговом окне выберите имя процесса службы и нажмите кнопку Attach

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

Способ №2 (подключение отладчика из исходного кода)

В код модуля program.cs, сразу после начала метода Main() добавляем следующее:

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

Этот код подключает отладчик к процессу и компилируется только в режиме DEBUG. Теперь для отладки службы её нужно запускать не из Visual Studio, а с помощью консоли управления службами (или команды net start). Сразу после запуска появится диалоговое окно выбора отладчика:

Выбираете запущенный экземпляр Visual Studio, нажимаете Yes и наслаждаетесь отладкой!

Способ №3 (запуск с параметрами командной строки)

предложил Gwynbleidd

Есть еще способ — предусматривать возможность запуска службы как консольного приложения. Например, в командной строке передавать /console, и если этот флаг установлен, стартовать приложение как консольное, если нет — как службу.

Преимущества такого подхода:
Очень просто отлаживать, по сути дела, как обычное консольное приложение. В консоль можно выводить отладочную информацию

Спасибо komr за помощь в отладке текста :-)

While developing a Windows Service, it is important to be able to debug it like you would a regular desktop or web application.

Debugging Windows Services isn’t quite as straightforward as debugging standard Windows, Console, or Web applications. However, when developing a .NET Windows Service it isn’t difficult to set up your project to support debugging and there are a few different options available to achieve this.

This article focuses on the options for debugging a Windows Service while you are developing it using Visual Studio, as opposed to when the service is running on a production system.

In the sections that follow I cover a number of different options that will enable you to hit breakpoints in your Windows Service application code.

Windows Service basics

To start with, it is important to understand how Windows Services operate in regards to hosting.

Windows Services must be run within the context of the Services Control Manager.

The Service Control Manager hosts and manages the execution of Windows Services. Among other things it allows services to be stopped and started, both manually and automatically.

Service registration

When developing a Windows Service using C#, as per most .NET applications you will notice that there is still a static Main method that acts as the program entry point.

The key difference from a normal application is that the first thing that typically happens in the Main method is the registration of the service (or services) that you want to run within the context of the Service Control Manager.

This is shown in the code sample below.

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
    var servicesToRun = new ServiceBase[]
    {
        new MyService()
    };
 
    ServiceBase.Run(servicesToRun);
}

The body of the Main method can be simplified further from the default .NET Windows Service template code, if you are only registering one service, as per the code below.

var service = new MyService();
ServiceBase.Run(service);

After the code calls the Run method, a dialog box similar to the following will appear.

Windows Service Start Failure dialog box

Windows Service Start Failure dialog box

The above message indicates that you can’t start the service unless it has been installed. This means that by default you can’t debug the service from Visual Studio as you would a normal application.

To start a Windows Service you first need to install it using a service installation utility. However, this typically isn’t required when you are still in the development phase of your project.

Service installation

Before moving on to the next section, if you do want to install your Windows Service there are two main options.

InstallUtil

Firstly, you can use InstallUtil, as recommended by the dialog box message shown further above.

InstallUtil "C:\Path To My Service Project\bin\Debug\MyService.exe"

To uninstall an existing service, just insert the /u or /uninstall switch into the command, as follows.

InstallUtil /u "C:\Path To My Service Project\bin\Debug\MyService.exe"

InstallUtil can be accessed as above when using the Developer Command Prompt for Visual Studio, or if you have added the program to your Path.

The InstallUtil executable is usually found at the following location.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe

SC utility

Alternatively, you can use the standard sc utility from the command line which is available as standard on practically any modern Windows device.

sc create MyService binPath="C:\Path To My Service Project\bin\Debug\MyService.exe"

To uninstall a service specify delete instead of create within your command.

sc delete MyService

The sc utility works just fine for simple service install and uninstall requirements.

Debugging approaches

Now let’s look at some of the debugging approaches that we can take.

Debug service method

Probably the easiest option for adding debugging support to a Windows Service is to create a debugging entry point method in the service class we have implemented.

We can then control access to the debugging entry point via a conditional compiler pragma check.

Below is an example of how the updated body of the Main method would look using this approach.

var service = new MyService();
 
#if DEBUG
 
// Debug the Windows Service.
service.OnDebug();
Thread.Sleep(Timeout.Infinite);
 
#else

// Run the Windows Service.
ServiceBase.Run(service);
 
#endif

Note the code highlighted in grey. This indicates code that is not compiled for the current solution configuration.

The above code first of all creates a new instance of our service object.

If the DEBUG constant is defined, which it will be by default for the ‘Debug’ solution configuration, then the OnDebug method will be called. Underneath, the OnDebug method calls the OnStart method on the service object.

Lastly, the Sleep method call waits for an infinite amount of time, allowing the service code to continue execution until debugging has been stopped.

The OnDebug method within the service class is implemented as follows.

/// <summary>
/// Entry point into the service to allow for debugging.
/// </summary>
internal void OnDebug()
{
    OnStart();
}

As you can see, the OnDebug method simply calls the OnStart method to kick off our service.

The OnStart method is defined as follows.

/// <summary>
/// Executes when a Start command is sent to the service.
/// </summary>
/// <param name="args">Data passed by the start command</param>
protected override void OnStart(string[] args = null)
{
    Debug.WriteLine("Service started!");
}

The args parameter has been updated with a default value of null to allow the OnStart method to be called without any arguments being passed to it.

Of course, in a real-world scenario, the body of the OnStart method will usually be much more interesting than the basic WriteLine method call shown in the above example.

Now that you have a debugging entry point you can add a breakpoint to your Main, OnDebug, or OnStart method to commence your debugging session from there.

Conditional attribute check

If you don’t like inline compiler pragmas, you may instead consider using the Conditional attribute.

With this approach, we can swap out the #if, #else, and #endif pragma checks for regular if and else conditional statements.

// Check if we are debugging.
bool debugMode = false;
CheckDebugMode(ref debugMode);
 
if (debugMode)
{
    // Debug the Windows Service.
service.OnDebug();
Thread.Sleep(Timeout.Infinite); } else {     // Run the Windows Service.     ServiceBase.Run(service); }

The key part of the above code is the call to the CheckDebugMode method, which is defined as follows.

/// <summary>
/// Checks if debug mode should be enabled.
/// </summary>
/// <param name="debugMode">The debug mode to set according to the current solution configuration</param>
[Conditional("DEBUG")]
private static void CheckDebugMode(ref bool debugMode)
{
    debugMode = true;
}

The CheckDebugMode method is decorated with the Conditional attribute. This means that the CheckDebugMode method will only be executed if the DEBUG constant is defined.

There are a number of restrictions placed on methods that use the Conditional attribute. For example, they must return void and cannot contain out parameters, hence the somewhat verbose method definition.

However, this approach does have benefits over the compiler pragmas since you can always verify that your code is currently compiling regardless of what your selected solution configuration is e.g. ‘Debug’ or ‘Release’.

User interactive mode check

A slight variation on the previous approaches is to check if the current process is running in user interactive mode, rather than relying on compiler constants.

var service = new MyService();
 
if (Environment.UserInteractive)
{
    // Debug the Windows Service.
    service.OnDebug();
Thread.Sleep(Timeout.Infinite); } else {     // Run the Windows Service.     ServiceBase.Run(service); }

The UserInteractive property will return false when a process is running within a host that does not allow user interface components to be displayed.

Examples of this include IIS processes and Windows Service processes which are not allowed to show any UI by default.

This code is much simpler than the compiler pragmas and Conditional attributes.

Please note, however, that this approach may not be desirable if you do not want your application to be runnable in production outside the context of the Service Control Manager. The above code will allow your application to be run manually by simply ‘double-clicking’ the compiled service executable.

Attach to Process

It is possible to attach the Visual Studio debugger to a running Windows Service.

This can be beneficial when you are nearing the end of development work on your project and you want to test that your application is operating correctly when it is running as a bonafide Windows Service.

It is also useful if you are experiencing issues in production that can only be reproduced when your Windows Service is running within the context of the Service Control Manager.

Before continuing, it is important to note that in order to get meaningful information when debugging you’ll need to attach to a Debug build of your Windows Service that includes the debug symbol (.pdb) files.

To debug a running Windows Service, first of all, make sure that your service has been started.

Then from the toolbar at the top of Visual Studio select Debug –> Attach to Process…

This will launch the ‘Attach to Process’ dialog, as per the screenshot below.

Attach to Process dialog

Attach to Process dialog

Since Windows Services typically run as one of the built-in Windows accounts, tick the ‘Show processes from all users’ checkbox at the bottom-left of the ‘Attach to Process’ dialog.

Now find the service process you wish to connect to within the ‘Available processes’ list, select it, then press the ‘Attach’ button.

The debugger should now be attached to the process and your breakpoints should be hit as they normally would.

Summary

In summary, I have explained how Windows Services are hosted by the Service Control Manager, making them less straightforward to debug compared to standard Windows and Console applications.

I have covered how to install Windows Services using either the InstallUtil or sc utilities.

Lastly, I have demonstrated a few different ways in which you can add support for debugging Windows Services from Visual Studio.

Let me know what your favourite approach is in the comments!

Skip to content

A service project created in Visual Studio cannot be directly started in the debugger. A small tweak enables direct running of the service and gets rid of the need to deploy the service to test it.

From the first time I tried developing a Windows Service in C++ back in the good old days before .NET I’ve always found them awkward to debug. When developing I want to have my environment setup with multiple startup projects so that I can open a solution, hit F5 and have everything required running. If the solution contains a service a tedious compile-install-start service-attach debugger procedure is required.

Last week when I created a service I decided to find a better way, a way that enables the simple “press F5 to get everything running” approach. Since a service project already compiles to an .exe file, it can be changed to check if it is run as a service or interactively during startup. If running interactively, a reflection hack is used to call each service’s OnStart method.

The first step is to change the service application to be a console app instead of a windows app. I you prefer to use WinForms or WPF for the interactive GUI you should leave it as it is, but that would make a background service dependent on UI libraries which I don’t like. Besides, the GUI is not the essential part of this app. It’s target at developers so a console app will do.

First the Main function has to be modified to check if it is run interactively or as a service.

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

A check is done for Environment.UserInteractive to choose between interactive mode and normal service mode. This might cause problems with services running in interactive mode. (I haven’t tested and don’t intend to – because a service running interactively is often a security problem. If you have to communicate with the user, build a small client app that is auto started instead.)

The real work is done in the RunInteractive helper method. It calls the OnStart method of each service. Then it waits for a keypress before calling OnStop of each service. The OnStart and OnStop methods are protected and not intended to be called from the outside. Since this is a debug aid I think it is okay to use reflection to call the nonpublic methods.

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);
}

With the service library set up this way it can be set to start together with other projects automatically when starting a debug session in Visual Studio. Enabling everything to start with one button (F5) really speeds up the coding and debugging.

Introduction

Building Windows services can be challenging, especially for developers whom never written a Window service before. Most documentation including Microsoft warns that a developer should not attach to a process unless you know what the process is and understand the consequences of attaching to and possibly killing that process. This is true when running any project that code not reviewed. This changes slightly when the developer wrote code and wants to debug the service. This article provides details on debugging a Windows service after there has been a code review.

Shows dialog for debugging a service

Installing the service

Use Installutil.exe The Installer tool is a command-line utility that allows you to install and uninstall server resources by executing the installer components in specified assemblies.

Alternate method to install is with the following tool which was written in .NET Framework, not .NET Core but if needed would be easy to convert to .NET Core Framework yet see zero reasons for this.

Project setup

Open the service solution in Visual Studio; open the window service to debug, make sure the project is setup for debug under project properties, Build tab. “Define DEBUG constant” must be checked and the Configuration ComboBox must be set to “Active (Debug)” or “Debug”.

Debug point

Place the following into the OnStart event of the service.



#if DEBUG
  Debugger.Launch();
#endif


Enter fullscreen mode

Exit fullscreen mode

With the service project as the startup project, click “start” in the IDE toolbar. When the breakpoint is hit on Windows 7 a prompt appears, answer yes to debug then select the open instance of Visual Studio to use to debug. Once this has been done Visual Studio will break on Debugger.Launch. Step through the code and notice there is no difference in debugging any other project type.

Windows 8 and higher have tighten security so that when Debugger.Launch is hit there will be additional prompts to jump through which can become a nuance when there is a need for many debug sessions as this is the best method to figure out why code which looks good is not working as expected.

The solution for Windows 8 and higher is to first open RegEdit to



HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry


Enter fullscreen mode

Exit fullscreen mode

Change the value from 1 to 0. This will invoke a prompt from Windows indicating for the change to take affect this computer requires a reboot. Once the system has restarted, open Visual Studio to the Windows Service solution and start the service. At this point the additional security prompts will not appear.

The solution mentioned for Windows 8 and higher is not documented by Microsoft; this information was obtained by the Microsoft team responsible for the Visual Studio debugger and may change in updates to Windows operating system, not Visual Studio.

Warning

Disabling UAC (as done by altering the registry value above) is not advisable as this raises chances of security breach to a computer. This should only be done when the service source is not from an unknown source. Only use the registry setting mentioned above for the time it takes to write code for a service followed by enabling UAC when finished.

Summary

Using techniques presented allow developers to debug window services on Windows 7 and higher machines. Using techniques for Windows 8 and above allow less security prompts by lowering UAC (User Account Control) which means the computer is less safe thus important to enable the setting in the registry back to it’s default.

When I started working on windows services, I had some difficulty with how to install it, what are necessary functionalities, how to debug it etc. I am sharing my leaning from a beginner’s point of view that how to create a service from scratch.

Part 1 – Creating a Windows Service Project

You can select Windows Service template on the new project screen in the windows section to create a windows service project.

Create Project Dialog

This is what it first looks like after you create the project.

This is what it first looks like after you create the project.

I changed the default code in the class to the code below, and added a timer as the functionality is to be performed at regular intervals. and custom event log entry to track service actions.

Timer serviceTimer = new Timer();
public PracticeService()
{
    InitializeComponent();
    if (!System.Diagnostics.EventLog.SourceExists("PracticeSource"))
    {
        System.Diagnostics.EventLog.CreateEventSource("PracticeSource","PracticeLog");
    }
    eventLog1.Source = "PracticeSource";
    eventLog1.Log = "PracticeLog";
}

protected override void OnStart(string[] args)
{
    eventLog1.WriteEntry("Practice Service Started", EventLogEntryType.Information);
    InitializeTimer();
}

protected override void OnStop()
{
    eventLog1.WriteEntry("Practice Service Stopped", EventLogEntryType.Information);
    serviceTimer.Dispose();
}

private void InitializeTimer()
{
    try
    {
        if (serviceTimer != null)
        {
            serviceTimer.AutoReset = true;
            serviceTimer.Interval = Convert.ToDouble(60 * 1000);
            serviceTimer.Enabled = true;
            serviceTimer.Elapsed += serviceTimer_Elapsed;
            eventLog1.WriteEntry("Service Timer Initialized", EventLogEntryType.Information);
        }
    }
    catch (Exception ex)
    {
        eventLog1.WriteEntry(ex.Message, EventLogEntryType.Error);
    }
}
protected void serviceTimer_Elapsed(object sender,ElapsedEventArgs e)
{
    //Your Functionality
    eventLog1.WriteEntry("Elapsed Event Called", EventLogEntryType.Information);
}

To install the service with necessary information in itself we need to add an installer file to this project. An installer class can be found under General tab while adding items to the project.

Installer Class

Add the components ServiceProcessInstaller and ServiceInstaller to the Installer Class from the toolbox and initialize their properties

Installer Class Design View

//
// PracticeServiceInstaller
//
this.PracticeServiceInstaller.Description = "This service is created for practice purposes.";
this.PracticeServiceInstaller.DisplayName = "Practice Service";
this.PracticeServiceInstaller.ServiceName = "PracticeService";
this.PracticeServiceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// PracticeServiceProcessInstaller
//
this.PracticeServiceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.PracticeServiceProcessInstaller.Password = null;
this.PracticeServiceProcessInstaller.Username = null;

Above is the code in the designer file of installer class after initializing the properties. If the service requires special permissions you can use a user with same permissions.

Now build the project in Release mode and we are ready for deployment. For debugging build the project in Debug mode.

Part 2 – Installing a Windows Service

To install the service copy the contents of project output folder(debug/release) to the deployment folder in my case it is “G:\Deployments\PracticeService”

Deployment Folder

Now to install the service open command prompt go to .NET framework folder. As we have entered all the required information regarding service in installer, we won’t be needed to enter any information here and installation will be smooth. We run InstallUtil.exe program to install our service, it takes ‘/i’ as first argument for installation and ‘/u’ for uninstall. The second argument is the path to the service, in my case it is “G:\Deployments\PracticeService\PracticeService.exe”.

After installation our service is now visible in Services Manager Screen

Services Manager

When we start the service and let it run for few minutes we can see the PracticeLog created in the Event Viewer. Here we can see the event happening where we have written entry in the event log like serviceTimer_Elpased event.

Event Viewer

Part 3 – Debugging windows service.

Debugging windows service is as easy as debugging any other program. You just have to attach the Visual Studio Debugger to your service. The service may not get displayed at first because by default it shows the service running of current user. To display you service in the debugger just check the checkbox labeled “Show processes from all users”, then you can debug your code. Make sure that the service deployed should be built in Debug mode not the release mode.

Debugging

If you want to debug the service code without deploying it, there is another way by adding a module to the project and calling the service “OnStart” function in it. Set it as the start-up object and you can debug by running the project the usual way.

The information provided here is easily available on the internet if you search for it, I just wanted to accumulate all the this information at one place so as to have a step by step way for a beginner to start working with Windows Services.

About Utkarsh Puranik

Software Engineer by Profession, Gamer by Nature, Techy by Attitude and a Good Person at Heart

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Windows 7 с автозагрузкой на флешку
  • Пропал курсор на ноутбуке как вернуть windows 10 в исходное состояние
  • Intel nuc драйвера windows 10
  • Установка днс сервера на windows 7
  • 0x800736b1 как исправить windows 7