Area di riferimento
- Implementing service processes, threading, and application domains in a .NET Framework application
- Implement, install, and control a service
- Inherit from ServiceBase class
- ServiceInstaller and ServiceProcessInstaller class
- SessionChangeDescription structure and SessionChangeReason enumeration
Servizi di Windows
I servizi di Windows sono processi che vengono eseguiti in background e non hanno interfaccia grafica. I servizi rappresentano un modo ideale per implementare applicazioni che devono essere costantemente in esecuzione e che non necessitano dell'intervento dell'utente. Esempi classici sono le applicazioni server come Microsoft SQL Server.
I servizi possono essere eseguiti all'avvio del sistema operativo (anche prima del log on) oppure possono essere avviati/fermati manualmente. In Windows XP e Vista è possibile gestire i servizi di sistema da "Pannello di Controllo" -> "Strumenti di amministrazione" -> "Servizi" oppure si può utilizzare il comando net start "servizio" e net stop "servizio":
Vediamo come la realizzazione di servizi si differenzia dalla realizzazione di normali applicazioni:
- Non è posibile lanciare il servizio all'interno dell'IDE ma questo deve essere installato e avviato
- Il debugging è più complicato
- Eventuali errori dovranno essere segnalati nell'Event Log di Window piuttosto che tramite una interfaccia grafica
- Il contesto di sicurezza in cui sono eseguiti i servizi è generalmente diverso rispetto a quello dell'account utente. E' importante lanciare un servizio con i minori privilegi possibili in modo da minimizzare i danni che potrebbe provocare.
All'interno dell'IDE Microsoft Visual Studio 2005 Professional è presente un template che crea automaticamente lo scheletro necessario all'implementazione di un servizio windows.
Il codice generato per l'avvio del servizio è il seguente:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
// More than one user Service may run within the sameprocess. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new ServiceBase[] { new Service1() };
ServiceBase.Run(ServicesToRun);
}
}
Il metodo statico Run della classe ServiceBase serve per mandare in esecuzione all'interno di un processo uno o più servizi. Un servizio è un oggetto che deriva dalla classe ServiceBase, che ridefinisce tutti o alcuni dei seguenti metodi:
All'interno di questi metodi è possibile inserire qualsiasi codice che rispetti i privilegi posseduti dal processo.
Ecco un esempio di un servizio che ogni 10 secondi esegue una qualche azione:
public partial class Service1 : ServiceBase
{
private Timer _timer;
public Service1()
{
InitializeComponent();
// Imposto il nome del servizio
this.ServiceName = "MioServizio";
// Imposto che il servizio può essere messo in pausa e gestire lo shutdown
this.CanPauseAndContinue = true;
this.CanShutdown = true;
_timer = new Timer(10000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
}
protected override void OnStart(string[] args)
{
_timer.Start();
}
protected override void OnStop()
{
_timer.Stop();
}
protected override void OnPause()
{
_timer.Stop();
}
protected override void OnContinue()
{
_timer.Start();
}
protected override void OnShutdown()
{
_timer.Stop();
}
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
// Codice da eseguire periodicamente...
}
}
Per offrire la possibilità di installare un servizio il .NET Framework fornisce le classi ServiceInstaller e ServiceProcessInstaller. La classe ServiceInstaller mantiene le informazioni sul servizio mentre la classe ServiceProcessInstaller mantiene le impostazioni relative al contesto di sicurezza in cui sarà eseguio il servizio. Grazie a Visual Studio tutto il codice necessario può essere generato cliccando "Add Installer" dopo aver fatto click con il tasto destro del mouse nella modalità design view del servizio.
Basta solamente impostare le seguenti cose:
- StartType di ServiceInstaller a "Automatic", "Manual" o "Disabled"
In questo modo si specifica se il servizio dovrà essere avviato automaticamente all'avvio del sistema oppure no.
- Description e DisplayName di ServiceInstaller
- Account di ServiceProcessInstaller a "LocalService", "NetworkService", "LocalSystem" o "User"
Con questa proprietà si specifica il contesto di sicurezza entro il quale opererà il servizio.
- Nelle proprietà del progetto impostare lo "Startup Object" cioè la classe Program
Il codice generato dal designer è il seguente:
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
}
partial class ProjectInstaller
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// serviceInstaller1
//
this.serviceInstaller1.DisplayName = "MioServizio";
this.serviceInstaller1.ServiceName = "MioServizio";
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
#endregion
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
}
A questo punto è possibile utilizzare l'utility InstallUtil.exe presente nella directory C:\Windows\Microsoft.NET\Framework\v2.0.50727 (che consiglio di inserire nella variabile di ambiente PATH) per installare/disinstallare manualmente il servizio dalla linea di comando.
Per installare il servizio di nome "MioServizio":
InstallUtil MioServizio.exe
Per disinstallare il servizio di nome "MioServizio":
InstallUtil /u MioServizio.exe