Service Windows
23 Mars 2009
Résumé
Fonctionnalités: journalisation dans une base de données des accès disques correspondant à un dossier et ses sous-dossiers.
Plateforme: Windows 2000 ou supérieure.
Particularités: indépendant du framework .NET.
Solution: service windows intégrant un serveur COM (accessible par script) et utilisant une base Microsoft Jet blue; inscriptions dans le journal d'évènenements de Windows.
Technologies: C, C++, ATL, Jet blue.
Environnement de développement: Visual Studio 2008 Professionnel (Microsoft).
Références
ATL
Jet blue
Téléchargements
solution VS2008
Pas-à-Pas
Démarrer Visual Studio 2008
Menu : Fichier / Nouveau / Projet
Menu : Projet / Propriétés, Propriétés de configuration / Editeur de liens / Fichier manifeste : Changer la propriété « Niveau d’exécution UAC » de « asInvoker » en « requireAdministrator »
Dans l’explorateur de solutions, ouvrir le fichier « MyService/Fichiers sources/MyService.cpp » et ajouter la ligne suivante dans le corps de la fonction
InitializeSecurity() CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
Dans l’explorateur de solution, clic droit sur le dossier « Fichiers sources » : menu contextuel /Ajouter/ Nouvel élément
Message.mc
;//
;//le fichier 'message.h' est genere automatiquement a partir du fichier 'message.mc' par le compilateur de message (mc.exe)
;//
MessageIdTypedef=DWORD
SeverityNames=(
Success=0x0:STATUS_SEVERITY_SUCCESS
Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
Warning=0x2:STATUS_SEVERITY_WARNING
Error=0x3:STATUS_SEVERITY_ERROR
)
LanguageNames=(
French=0x040C:MSG_0x040C
)
MessageId=0
Severity=Success
Facility=Application
SymbolicName=EVENT_LOG Language=French
%1
.
MessageId=+1
Severity=Error
Facility=Application
SymbolicName=MSG_ERROR Language=French
MyService a renvoyé l'erreur suivante: %1
.
Dans l’explorateur de solution, clic droit sur le fichier « message.mc » : menu contextuel / propriétés
Propriétés de configuration / étape de génération personnalisée / Général
Propriété « Ligne de commande » : mc $(InputFileName)
Propriété « Sorties » : $(InputName).h $(InputName).rc
Dans l’explorateur de solution, clic droit sur le fichier « message.mc » : menu contextuel / Compiler
Dans l’explorateur de solution, clic droit sur le dossier « Fichiers de ressources » : menu contextuel / ajouter / élément existant…
Dans l’explorateur de solutions ouvrir le fichier MyService/Fichiers de ressources/MyService.rgs et ajouter le texte suivant à la fin
HKLM
{
NoRemove SYSTEM
{
NoRemove CurrentControlSet
{
NoRemove Services
{
NoRemove EventLog
{
NoRemove Application
{
'MyService'
{
val 'EventMessageFile' = s '%MODULE_RAW%'
val 'TypesSupported' = d 7
}
}
}
}
}
}
}
Dans l’explorateur de solution ouvrir le fichier « MyService/Fichiers d’en-tête/stdafx.h » et ajouter la ligne suivante
#include <atlstr.h>
Dans l’explorateur de solutions, ouvrir le fichier « MyService/Fichiers sources/MyService.cpp » et ajouter les lignes suivantes
CString _toto;
HRESULT _stop()
{
_AtlModule.OnStop();
return S_OK;
}
Dans la définition de la classe CMyServiceModule ajouter les lignes suivantes
HRESULT RegisterAppId(bool bService = false)
{
_HANDLE hSCM;
SC_LOCK sclLock;
SC_HANDLE hService;
SERVICE_DESCRIPTION sdBuf;
__super::RegisterAppId(bService);
sdBuf.lpDescription = L"Description of My Service";
hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hSCM == NULL)
{
LogEvent(_T("OpenSCManager FAILED"));
return FALSE;
}
sclLock = LockServiceDatabase(hSCM);
if(sclLock == NULL)
{
LogEvent(_T("LockServiceDatabase FAILED"));
return FALSE;
}
hService = OpenService(hSCM, m_szServiceName, SERVICE_CHANGE_CONFIG);
if (hService == NULL)
{
LogEvent(_T("OpenService FAILED"));
return FALSE;
}
if (!ChangeServiceConfig(hService, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL) )
{
LogEvent(_T("ChangeServiceConfig FAILED"));
return FALSE;
}
if (!ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sdBuf) )
LogEvent(_T("ChangeServiceConfig2 FAILED"));
UnlockServiceDatabase(sclLock);
::CloseServiceHandle(hService);
::CloseServiceHandle(hSCM);
return S_OK;
}
Dans l’affichage de classes, clic droit sur le nœud « MyService » : menu contextuel Ajouter / Classe…
Dans la liste « Modèles » sélectionner Objet simple ATL
Dans l’explorateur de solutions, ouvrir le fichier « MyService/Fichiers de ressources/MyObject.rgs » et ajouter la ligne suivante à la fin du dernier paragraphe (avant la dernière série d’accolades fermantes)
val AppID = s '%APPID%'
Dans l’affichage de classes, clic droit sur le nœud « MyService/IMyObject » : menu contextuel, ajouter/ propriété…
Dans l’affichage de classes, clic droit sur le nœud « MyService/IMyObject » : menu contextuel, ajouter/ méthode…
Dans l’explorateur de solutions, ouvrir le fichier « MyService/Fichiers sources/MyObject.cpp » et ajouter les lignes suivantes
extern CString _toto;
extern HRESULT _stop();
Dans le corps de la méthode CMyObject::get_MyProperty ajouter la ligne suivante
::_toto.SetSysString(pVal);
Dans le corps de la méthode CMyObject::put_MyProperty ajouter la ligne suivante
::_toto = newVal;
Dans le corps de la méthode CMyObject::stop ajouter la ligne suivante
::_stop();
Avec l’explorateur Windows , dans le dossier de la solution créer un nouveau document texte et le renommer « MyRegistry.rgs »
Copier les lignes suivantes dans le fichier créé ci-dessus :
HKEY_CURRENT_USER
{
'MyKey' = s '%MYSTRING%'
}
Dans l’affichage de ressources, faire un clic-droit sur le nœud « MyService.rc », menu contextuel
Ajouter une ressource / Importer Ouvrir le fichier « MyRegistry.rgs »
Sélectionner « REGISTRY » comme type de ressource
L’éditeur affiche le fichier « MyRegistry.rgs » en format binaire. Le fermer .
Afficher la fenêtre de propriétés (Menu Affichage /Autres fenêtres / Fenêtre Propriétés)
Dans l’affichage de ressources, sélectionner le nœud créé ci-dessus ( MyService.rc/ « REGISTRY » / IDR_REGISTRY1).
Dans la fenêtre de propriétés, changer la valeur du champ ID de « IDR_REGISTRY1 » en « IDR_MYREGISTRY »
Dans l’affichage de ressources, sélectionner le nœud racine MyService.rc puis dans la barre d’icones du menu cliquer sur la disquette pour enregistrer le fichier MyService.rc
Vérifier en double-cliquant sur le nœud MyService.rc/ « REGISTRY » / IDR_MYREGISTRY que l’éditeur ouvre bien le fichier MyRegistry au format texte.
Remarques.
- Pour vérifier la valeur du symbole de ressource « IDR_MYREGISTRY » faire clic-droit sur le nœud correspondant : menu contextuel Symboles des ressources
- Pour modifier la valeur du symbole de ressource « IDR_MYREGISTRY », le sélectionner puis dans la fenêtre de propriétés, dans le champ ID taper la valeur IDR_MYREGISTRY=200
Dans l’explorateur de solutions, ouvrir le fichier « MyService/Fichiers sources/MyService.cpp » et ajouter les lignes suivantes
HRESULT _registryMyPropertyWrite()
{
ATL::_ATL_REGMAP_ENTRY aMapEntries [] = { { L"MYSTRING", L"" }, { NULL, NULL }};
aMapEntries[0].szData = _toto;
return ATL::_pAtlModule->UpdateRegistryFromResource(IDR_MYREGISTRY, true, aMapEntries);
}
Dans l’affichage de classe ajouter une méthode à l’interface CMyObject :
Dans le fichier CMyObject.cpp, rajouter la ligne suivante
extern HRESULT _registryMyPropertyWrite();
Remplacer le corps de la méthode CMyObject::registerWriteMyProperty, par la ligne suivante
return ::_registryMyPropertyWrite();
Dans le fichier MyService.cpp, au début, en tant que déclaration de variables globales, ajouter les lignes suivantes :
HANDLE hEvent[4];
HANDLE hThread;
DWORD dwThreadId;
DWORD WINAPI MyThread(LPVOID lpParam);
Dans le fichier CMyService.cpp, ajouter les lignes suivantes au corps de la classe CMyServiceModule
CMyServiceModule()
{
m_status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
hEvent[0] = CreateEvent (NULL, FALSE, FALSE, _T("myPAUSE"));
hEvent[1] = CreateEvent (NULL, FALSE, FALSE, _T("myCONTINUE"));
hEvent[2] = CreateEvent (NULL, FALSE, FALSE, _T("mySTOP"));
hEvent[3] = CreateEvent (NULL, FALSE, FALSE, _T("mySTOPPED"));
}
~CMyServiceModule()
{
CloseHandle(hThread);
}
HRESULT Run(int nShowCmd = SW_HIDE)
{
hThread = CreateThread(NULL, 0, MyThread, this, 0, &dwThreadId); // Start worker thread (MyTread)
return __super::Run(nShowCmd);
}
void OnPause()
{
SetServiceStatus(SERVICE_PAUSE_PENDING);
SetEvent(hEvent[0]); // Pause worker thread (MyTread)
SetServiceStatus(SERVICE_PAUSED);
}
void OnContinue()
{
SetServiceStatus(SERVICE_CONTINUE_PENDING);
SetEvent(hEvent[1]); // Resume worker thread (MyTread)
SetServiceStatus(SERVICE_RUNNING);
}
void OnStop()
{
SetEvent(hEvent[2]); // Stop worker thread (MyTread)
WaitForSingleObject(hEvent[3], 500);
__super::OnStop();
}
Dans le fichier MyService.cpp, à la fin, en tant que définition d’une fonction globale, ajouter les lignes suivantes :
DWORD WINAPI MyThread(LPVOID lpParam)
{
CMyServiceModule* p;
DWORD dwWait = 0;
BOOL bCont = TRUE;
p = static_cast<CMyServiceModule*>(lpParam);
p->LogEvent(_T("MyThread run"));
while (bCont)
{
dwWait = WaitForMultipleObjects(4, hEvent, FALSE, INFINITE);
switch (dwWait - WAIT_OBJECT_0)
{
case 0: //pause
p->LogEvent(_T("MyThread pause"));
break;
case 1: //continue
p->LogEvent(_T("MyThread continue"));
break;
case 2: //stop
p->LogEvent(_T("MyThread stop"));
bCont = FALSE; break;
}
}
SetEvent(hEvent[3]);
return 0;
}