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