Service windows dans une dll lancé par svchost.exe

Description

Ce code source montre comment implémenter un service Windows dans une DLL afin qu'il soit exécuté par un processus hôte svchost.exe. Au démarrage de Windows, le système examine une clé de la base de registres pour grouper des services dans diférentes instances de svchost.exe. Notre service fera partie du groupe netsvcs. Ce dernier présente l'avantage de s'exécuter en LocalSystem. Notre DLL exporte trois fonctions : ServiceMain(), obligatoire, InstallService() et RemoveService(). Les deux dernières servent à installer/désinstaller le service grâce à l'utilisation de RunDll32 en ligne de commandes. A noter que l'invite de commandes doit être lancé en mode administrateur. On peut mettre le fichier RacppService.dll dans le dossier System32 puis, pour installer le service, taper la ligne suivante:
rundll32 racppservice.dll InstallService
Pour supprimer le service on tape:
rundll32 racppservice.dll RemoveService
Si on choisit un autre emplacement que System32, il faudra taper le chemin complet de la dll. L'installation ou la suppression de notre service nécessite un redémarrage de Windows.
Tous les détails de l'installation et de la configuration de notre service figurent dans le code de la fonction InstallService().
Notre service utilise un timer pour écrire, toutes les minutes, dans un fichier log c:\racppservice.log, la date et l'heure du déclenchement du timer. Ceci est juste pour montrer le bon fonctionnement du service. Pour tous les détails, regarder le code source.
Le code est réalisé en c sous Visual C/C++ 2005 mais il est facilement adaptable pour les autres outils.
Pour préserver la clarté du code, la gestion des erreurs est minimale.
Le service a tété testé sans aucun problème sous Windows XP, Vista et 7.
La dll RacppService.dll est fournie dans le zip si vous vouler tester.
Voilà, j'espère n'avoir rien oublié.
Tous les commentaires ou remarques sont les bienvenus.

Source / Exemple :


#define  _WIN32_WINNT 0x0500 // Pour Windows 2000 et supérieur
#include <windows.h>

// Déclarations globales:
HINSTANCE hDll;
SERVICE_STATUS_HANDLE   hServiceStatusHandle; 
SERVICE_STATUS          ServiceStatus; 
HANDLE                  hEvent,hFile;
char buff[MAX_PATH]; 

// Fonction d'installation du service:
int WINAPI InstallService()
{
	HKEY hkService,hkSvchost;
	SC_HANDLE schScm ,schService;
	char* pData;
	DWORD dwError=0, dwLen,dwSize = 0;   
	// Ouvrir le gestionnaire de services:
	schScm = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if(schScm)
	{
		// Créer notre service:
		schService = CreateService(schScm, "RacppService", 0, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, "%SystemRoot%\\System32\\svchost.exe -k netsvcs", 0, 0, 0, 0, 0); 
		if(schService)
		{
			// Ajouter une description à notre service:
			SERVICE_DESCRIPTION sd;
			sd.lpDescription = "Service dans une DLL lancé par svchost.exe";
			ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &sd);
			// Ajouter la sous-clé Parameters à la clé de notre service:
			RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\RacppService\\Parameters", 0, 0, REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS, 0, &hkService, 0);   
			// Récupérer le chemin complet de notre Dll:
			GetModuleFileName(hDll, buff, sizeof(buff));
			// Affecter le chemin de la Dll à la sous-clé Parameters sous le nom ServiceDll:
			RegSetValueEx(hkService, "ServiceDll", 0, REG_EXPAND_SZ, (BYTE*)buff, lstrlen(buff)+1);
			// Fermer la clé de notre service:
			RegCloseKey(hkService);
			// Ouvrir la clé indiquant la liste des groupes d'instances de svchost.exe:
			RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", 0, KEY_ALL_ACCESS, &hkSvchost); 
			// Obtenir la longueur du contenu du groupe netsvcs:
			RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, 0, &dwSize); 
			// Fixer le nom de notre service pour la liste des services du groupe netsvcs:
			lstrcpy(buff,"RacppService");
			dwLen=lstrlen(buff);
			// Allouer la mémoire nécessaire pour la nouvelle liste des noms de services du groupe netsvcs:
			pData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwSize + dwLen + 1);
			if(pData)
			{
				char *pNames;
				// Récupérer la liste des noms de services du groupe netsvcs:
				RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, (BYTE*)pData, &dwSize);
				// Vérifier si le nom existe
				for (pNames = pData; *pNames; pNames=strchr(pNames,0)+1) if(!lstrcmpi(pNames, buff)) break;   
				// L'ajouter s'il n'existe pas:
				if (*pNames == 0)
				{
					memcpy(pData + dwSize - 1, buff, dwLen + 1);
					RegSetValueEx(hkSvchost, "netsvcs", 0, REG_MULTI_SZ, (BYTE*)pData, dwSize + dwLen + 1);
				}
				else dwError=ERROR_ALREADY_EXISTS;
				// Libérer la mémoire allouée:
				HeapFree(GetProcessHeap(),0,pData);
			}
			else dwError=ERROR_NOT_ENOUGH_MEMORY;
			// Fermer la clé svchost:
			RegCloseKey(hkSvchost);
			// Fermer le handle de notre service:
			CloseServiceHandle(schService); 
		}
		// Fermer le handle du gestionnaire de services:
		CloseServiceHandle(schScm);
	}
	// Retourner dwError si non nul, sinon GetLastError():
	return dwError? dwError : GetLastError();
}

// Fonction de désinstallation du service:
DWORD WINAPI RemoveService()
{
	HKEY hkSvchost;
	SC_HANDLE schScm ,schService;
	char* pData;
	DWORD dwError=0,dwSize = 0;   
	// Ouvrir le gestionnaire de services:
	schScm = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if(schScm)
	{
		// Ouvrir notre service:
		schService=OpenService(schScm,"RacppService",SERVICE_ALL_ACCESS);
		if(schService)
		{
			// Supprimer le service:
			if(DeleteService(schService))
			{
				// Ouvrir la clé indiquant la liste des groupes d'instances de svchost.exe:
				RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Svchost", 0, KEY_ALL_ACCESS, &hkSvchost); 
				// Obtenir la longueur du contenu du groupe netsvcs:
				RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, 0, &dwSize); 
				// Fixer le nom de notre service pour la liste des services du groupe netsvcs:
				lstrcpy(buff,"RacppService");
				// Allouer la mémoire nécessaire pour tous les noms de services du groupe netsvcs:
				pData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,dwSize);
				if(pData)
				{
					char *pNames;
					// Récupérer la liste des noms de services du groupe netsvcs:
					RegQueryValueEx(hkSvchost, "netsvcs", 0, 0, (BYTE*)pData, &dwSize);
					// Vérifier si le nom existe:
					for (pNames = pData; *pNames; pNames=strchr(pNames,0)+1) if(!lstrcmpi(pNames,buff)) break;   
					// Le supprimer s'il existe:
					if (*pNames)
					{
						char* pNext;
						pNext=strchr(pNames,0)+1;
						memcpy(pNames,pNext,dwSize-(pNext-pData));
						RegSetValueEx(hkSvchost, "netsvcs", 0, REG_MULTI_SZ, (BYTE*)pData, dwSize -(lstrlen(buff)+1));
					}
					else dwError=ERROR_NO_SUCH_MEMBER;
					// Libérer la mémoire allouée:
					HeapFree(GetProcessHeap(),0,pData);
				}
				else dwError=ERROR_NOT_ENOUGH_MEMORY;
				// Fermer la clé svchost:
				RegCloseKey(hkSvchost);
			}
			// Fermer le handle de notre service:
			CloseServiceHandle(schService); 
		}
		// Fermer le handle du gestionnaire de services:
		CloseServiceHandle(schScm);
	}
	// Retourner dwError si non nul, sinon GetLastError():
	return dwError? dwError : GetLastError();
}

// Fonction HandlerEx communiquant avec le gestionnaire de services:
DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
	switch(dwControl) 
	{
	case SERVICE_CONTROL_STOP:
	case SERVICE_CONTROL_SHUTDOWN:
		// Mettre le service à l'état "En cours d'arrêt":
		ServiceStatus.dwWaitHint      = 1000;
		ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
		SetServiceStatus(hServiceStatusHandle, &ServiceStatus );
		// Mettre l'Event à l'état signalé:
		SetEvent(hEvent);
		return NO_ERROR;
	default:
		ServiceStatus.dwWaitHint = 0;
		break;
	}
	SetServiceStatus(hServiceStatusHandle,  &ServiceStatus);// Obligatoire selon MSDN
	return NO_ERROR;
}

// Fonction Callback appelée toutes les minutes notre timer:
VOID CALLBACK TimerProc( LPVOID lpParameter,BOOLEAN TimerOrWaitFired)
{
	SYSTEMTIME st;
	DWORD dwLen;
	// Se positionner à la fin du fichier log:
	SetFilePointer(hFile,0,0,FILE_END);
	// Former la ligne à écrire dans le fichier log:
	lstrcpy(buff,"Le timer de RacppService a été déclenché le ");
	GetLocalTime(&st);
	GetDateFormat(0,0,&st,"dd/MM/yyyy",buff+lstrlen(buff),12);
	lstrcat(buff," à ");
	GetTimeFormat(0,0,&st,"HH:mm:ss",buff+lstrlen(buff),12);
	lstrcat(buff,"\r\n");
	// Ecrire la ligne formée dans le fichier log:
	WriteFile(hFile,buff,lstrlen(buff),&dwLen,0);
	return;
}

// ServiceMain
VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
{
	hServiceStatusHandle = RegisterServiceCtrlHandlerEx("RacppService", HandlerEx, NULL); 
	if (hServiceStatusHandle)
	{
		// Initialiser notre structure SERVICE_STATUS:
		ServiceStatus.dwServiceType        = SERVICE_WIN32;
		ServiceStatus.dwControlsAccepted   = 0;
		ServiceStatus.dwWin32ExitCode      = 0; 
		ServiceStatus.dwServiceSpecificExitCode = 0; 
		ServiceStatus.dwCheckPoint         = 0;
		// Mettre le service dans l'état "En cours de démarrage":
		ServiceStatus.dwWaitHint           = 1000;
		ServiceStatus.dwCurrentState       = SERVICE_START_PENDING; 
		SetServiceStatus(hServiceStatusHandle, &ServiceStatus);
		// Créer un Event avec "non signalé" comme état initial: 
		hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (hEvent)
		{  
			// Ouvrir notre fichier log ou le créer si inexistant:
			hFile=CreateFile("C:\\racppservice.log", GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0);
			if(hFile!=INVALID_HANDLE_VALUE)
			{
				// Créer et lancer notre timer:
				HANDLE hTimerQueue,hTimer;
				hTimerQueue=CreateTimerQueue();
				CreateTimerQueueTimer(&hTimer,hTimerQueue,TimerProc,0,0,60000,WT_EXECUTEINTIMERTHREAD);
				// Mettre le service dans l'état "Démarré":
				ServiceStatus.dwWaitHint           = 0;  
				ServiceStatus.dwCurrentState       = SERVICE_RUNNING; 
				ServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN ;
				SetServiceStatus(hServiceStatusHandle, &ServiceStatus);
				// Attendre que l'Event soit à l'état signalé:
				WaitForSingleObject(hEvent, INFINITE);
				// Détruire notre timer:
				DeleteTimerQueueTimer(hTimerQueue,hTimer,0);
				DeleteTimerQueueEx(hTimerQueue,0);
				// Fermer le handle de notre fichier log:
				CloseHandle(hFile);
			}
			// Fermer le handle de l'Event:
			CloseHandle(hEvent);
		}
		// Mettre le service dans l'état "Arrêté":
		ServiceStatus.dwWaitHint = 0; 
		ServiceStatus.dwCurrentState = SERVICE_STOPPED;
		SetServiceStatus(hServiceStatusHandle, &ServiceStatus);
	}
	return;
}

// DllMain
BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
{
	// Obtenir le HINSTANCE de notre dll:
	hDll=hInst;
	return TRUE;
}

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.