Introduzione
Un servizio è un programma (.exe o .sys) che viene eseguito in background e non richiede interazione da parte dell’utente.
In Windows le informazioni relative ai servizi sono salvate al percorso del Registro di Sistema HKLM\SYSTEM\CurrentControlSet\Services, il quale contiene una chiave per ogni servizio.
Nella prima parte di questa serie sui servizi vedremo come crearne uno il cui file binario sarà un eseguibile (.exe), partendo da una veloce infarinatura fino all’installazione.
Struttura di un servizio
L’applicazione che andremo a scrivere sarà di tipo console, ovvero priva di GUI, così come una qualsiasi altra applicazione eseguibile da riga di comando ma che, per essere considerata un servizio valido, deve rispettare i requisiti richiesti da Windows.
Dall’immagine che segue si può da subito notare che il Processo del servizio utilizza due thread per funzionar correttamente: il main thread e il ervice thread.

Il main thread esegue il codice della funzione main (Service Entry Point) che si occupa di inizializzare una struttura SERVICE_TABLE_ENTRY:
typedef struct _SERVICE_TABLE_ENTRYW {
LPWSTR lpServiceName;
LPSERVICE_MAIN_FUNCTIONW lpServiceProc;
} SERVICE_TABLE_ENTRYW, *LPSERVICE_TABLE_ENTRYW;
Questa struttura contiene il nome del servizio e la sua funzione principale (ServiceMain).
La variabile di tipo SERVICE_TABLE deve essere un array che può contenere più di una coppia di valori, se lo stesso processo serve più servizi, ma la cui ultima voce deve corrispondere a un valore NULL, che sta ad indicare la fine dell’array.
SERVICE_TABLE_ENTRY entries[]: {
{lpServiceName1, lpServiceProc1},
{nullptr}
};
Successivamente invoca la funzione StartServiceControlDispatcher che connette il main thread al Service Control Manager (SCM), rendendolo di fatto il service control dispatcher thread del processo.
Il suo compito sarà, da quel momento in poi, la gestione delle richieste di modifica dello stato del servizio (avvio, arresto, sospensione…) provenienti dal Service Control Manager.
if(!StartServiceCtrlDispatcher(entries){
return 1;
}
Giunti a questo punto il main thread avvia il service thread che esegue il codice della funzione principale del servizio, la ServiceMain.
La prima operazione che il service thread deve eseguire è la registrazione, tramite la funzione RegisterServiceCtrlHandler, di un handler che contiene il codice per gestire le richieste del SCM.
Successivamente notifica al SCM che il servizio è in stato di avvio (SERVICE_START_PENDING) e infine che è attivo (SERVICE_RUNNING) tramite la funzione SetServiceStatus e la struttura SERVICE_STATUS.
Il Service Control Manager è “impaziente” e deve essere notificato entro pochi secondi se il servizio è in avvio.
Qualora l’inizializzazione delle risorse necessarie richieda più tempo, è necessario indicare il tempo rimanente o invocare con cadenza regolare che il servizio è in fase di avvio (SERVICE_START_PENDING).
typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
Scrittura del servizio
Quanto esposto sopra è un breve accenno ai passaggi fondamentali che l’applicazione deve seguire affinché Windows lo possa registrare come servizio ed eseguirlo senza errori.
Per avere una idea, il codice sarà così:
// Headers necessari
#include <Windows.h>
// Variabili globali
WCHAR g_ServiceName[] = L"NomeDelServizio";
SERVICE_STATUS_HANDLE g_ServiceHandle;
SERVICE_STATUS g_ServiceStatus;
// Funzione wrapper attorno a SetServiceStatus
void SetStatus(DWORD state) {
g_ServiceStatus.dwCurrentState = state;
SetServiceStatus(g_ServiceHandle, &g_ServiceStatus);
}
// Funzione handler che gestisce le richieste del SCM
void WINAPI ServiceHandler(DWORD dwControl){
switch (dwControl) {
case SERVICE_CONTROL_STOP:
SetStatus(SERVICE_STOPPED);
break;
}
}
// Funzione principale del service thread
void WINAPI ServiceMain(DWORD dwNumServicesArgs, LPWSTR* lpServiceArgVectors) {
// Registrazione della funzione handler
g_ServiceHandle = RegisterServiceCtrlHandler(g_ServiceName, ServiceHandler);
// Servizio in avvio
SetStatus(SERVICE_START_PENDING);
// Qualora fosse necessario inizializzare delle risorse il codice va inserito qui
// Il servizio è avviato
SetStatus(SERVICE_RUNNING);
}
// Funzione principale del processo eseguita nel main thread
int main(){
// Inizializzazione della variabile di tipo SERVICE_TABLE_ENTRY
SERVICE_TABLE_ENTRY entries[] = {
{g_ServiceName, ServiceMain},
{nullptr}
};
// Il servizio viene eseguito nel suo processo e accetta le richieste di arresto
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
// Avvia il service thread, in caso di errore chiude il processo
if(!StartServiceCtrlDispatcher(entries))
return 1;
return 0;
}
Per compilare il codice è necessario avere a disposizione Visual Studio (anche edizione Community) con il SDK di Windows e le librerie per la compilazione (link alla guida dal sito Microsoft).
La compilazione in modalità Debug non consente la registrazione del servizio ed è quindi necessario selezionare la modalità Release, come mostrato nel video che segue.
A questo punto al percorso predefinito C:\Users\<nome utente>\sources\repos\<nome soluzione>\x64\Release si trovano il file eseguibile compilato e il file .pdb (utile per effettuare il debug del file).
Distribuzione, installazione e verifica
Il metodo più semplice per installare manualmente il servizio prevede di copiare il file eseguibile nella macchina test e utilizzare il comando sc da prompt dei comandi (cmd.exe) eseguito come amministratore.
Il comando per l’installazione è sc create <nome del servizio> type= own binPath= <percorso del file eseguibile>.
Da notare che gli spazi dopo type= e binPath= sono necessari.
Se il comando ha successo significa che il servizio è stato creato ma, per impostazione predefinita, non è ancora in esecuzione.
Per avviarlo è richiesto un input come sc start <nome del servizio>.

Come si vede dallo screenshot, il servizio è in esecuzione.
Essendo stato creato correttamente lo ritroviamo sia tra l’elenco dei servizi disponibili in Gestione dei servizi (services.msc) sia nel Registro di sistema.


Non avendo specificato diversamente, Windows crea il servizio in avvio manuale (Start 3) e in esecuzione sotto l’utente LocalSystem.
Per un maggiore controllo sulla creazione del servizio occorre scrivere una applicazione client che utilizzi l’API CreateService:
SC_HANDLE CreateServiceW(
[in] SC_HANDLE hSCManager,
[in] LPCWSTR lpServiceName,
[in, optional] LPCWSTR lpDisplayName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwServiceType,
[in] DWORD dwStartType,
[in] DWORD dwErrorControl,
[in, optional] LPCWSTR lpBinaryPathName,
[in, optional] LPCWSTR lpLoadOrderGroup,
[out, optional] LPDWORD lpdwTagId,
[in, optional] LPCWSTR lpDependencies,
[in, optional] LPCWSTR lpServiceStartName,
[in, optional] LPCWSTR lpPassword
);
Come si vede dal prototipo è possibile specificare il tipo di avvio (dwStartType), sotto quale utente deve essere eseguito (lpServiceStartName), il tipo di servizio (dwServiceType) e l’ordine di avvio nel caso di dipendenze (lpLoadOrderGroup).
Per fermare il servizio è sufficiente utilizzare il comando sc stop <nome del servizio> o il pulsante Arresta da Gestione dei servizi.
Conclusione e anticipazioni
In questa prima parte sono stati fissati i principali concetti riguardo ai servizi in Windows.
Nelle parti che seguiranno il servizio sarà ampliato in un progetto più ampio che includerà la creazione di file, la creazione di mailsllot, la creazione di un client e l’utilizzo di ACL (Access Control List).
Riferimenti
- https://learn.microsoft.com/en-us/windows/win32/services/service-entry-point
- https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_table_entryw
- https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicectrldispatcherw
- https://learn.microsoft.com/en-us/windows/win32/services/service-control-manager
- https://learn.microsoft.com/en-us/windows/win32/services/service-servicemain-function
- https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-registerservicectrlhandlerw
- https://learn.microsoft.com/en-us/windows/win32/services/service-control-handler-function
- https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-setservicestatus
- https://learn.microsoft.com/en-us/windows/win32/services/service-status-transitions
- https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status
- https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk
- https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-createservicew