Ultimamente ero alle prese con un nuovo progetto nel quale ho sviluppato un componente per il NAP di Windows in linguaggio C++.
Quando ho iniziato a cimentarmi a scrivere all'interno degli eventi di Windows, ho tristemente scoperto che non era così semplice come avviene in managed code (per esempio C#).
In questo link viene spiegato in modo molto semplice come si scrive negli eventi di Windows utilizzando C++ unmanaged code, io cercherò di aggiungere qualche info in più per facilitare la comprensione di questo argomento.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363680(v=vs.85).aspx
L'approccio di base è il seguente:
Per definire gli eventi in una DLL partiamo da un file di testo dove elenchiamo gli eventi da creare in un formato ben preciso, e salvare questo file con nome MYEVENTPROVIDER:MC, ecco un esempio:
; // MyEventProvider.mc ; // This is the header section.SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS Informational=0x1:STATUS_SEVERITY_INFORMATIONAL Warning=0x2:STATUS_SEVERITY_WARNING Error=0x3:STATUS_SEVERITY_ERROR )FacilityNames=(System=0x0:FACILITY_SYSTEM Runtime=0x2:FACILITY_RUNTIME Stubs=0x3:FACILITY_STUBS Io=0x4:FACILITY_IO_ERROR_CODE )LanguageNames=(English=0x409:MSG00409); // The following are the categories of events.MessageIdTypedef=WORDMessageId=0x1SymbolicName=NETWORK_CATEGORYLanguage=EnglishNetwork Events.MessageId=0x2SymbolicName=DATABASE_CATEGORYLanguage=EnglishDatabase Events.MessageId=0x3SymbolicName=UI_CATEGORYLanguage=EnglishUI Events.; // The following are the message definitions.MessageIdTypedef=DWORDMessageId=0x100Severity=ErrorFacility=RuntimeSymbolicName=MSG_INVALID_COMMANDLanguage=EnglishThe command is not valid..MessageId=0x101Severity=ErrorFacility=SystemSymbolicName=MSG_BAD_FILE_CONTENTSLanguage=EnglishFile %1 contains content that is not valid..MessageId=0x102Severity=WarningFacility=SystemSymbolicName=MSG_RETRIESLanguage=EnglishThere have been %1 retries with %2 success! Disconnect fromthe server and try again later..MessageId=0x103Severity=InformationalFacility=SystemSymbolicName=MSG_COMPUTE_CONVERSIONLanguage=English%1 %%4096 = %2 %%4097. .; // The following are the parameter strings */MessageId=0x1000Severity=SuccessFacility=SystemSymbolicName=QUARTS_UNITSLanguage=Englishquarts%0.MessageId=0x1001Severity=SuccessFacility=SystemSymbolicName=GALLONS_UNITSLanguage=Englishgallons%0.
ATTENZIONE: Il punto definisce la fine del messaggio o della categoria definita.
E' possibile definire più linguaggi e di conseguenza definire i messaggi nelle varie lingue, ogni messaggio contiene una SEVERITY, FACILITY, SYMBOLICNAME (che è l'identificativo che andremo ad utilizzare per scrivere il messaggio nel log degli eventi), LANGUAGE e infine il messaggio stesso che può contenere dei parametri %1 %2 e così via.
Una volta scritto il file con la definizione degli eventi, dobbiamo generare la DLL e l'header file che utilizzeremo nel nostro progetto.
Per compilare il file, abbiamo bisogno di tre comandi: mc.exe, rc.exe e link.exe
I primi due sono presenti all'interno del Windows SDK mentre il terzo all'interno di visual studio.
Eseguire i comandi nel seguente ordine:
mc -U myeventprovider.mc
Compilare il file di risorse generato dal comando precedente:
rc myeventprovider.rc
Creare la DLL a partire dal file di risorse generato dal comando precedente (il parametro noentry è necessario).
link -dll -noentry myeventprovider.res
Il seguente è il file di header che va incluso nel progetto. Da notare all'interno del file gli identificativi dei messaggi.
// MyEventProvider.mc // This is the header section.// The following are categories of events.//// Values are 32 bit values laid out as follows://// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0// +---+-+-+-----------------------+-------------------------------+// |Sev|C|R| Facility | Code |// +---+-+-+-----------------------+-------------------------------+//// where//// Sev - is the severity code//// 00 - Success// 01 - Informational// 10 - Warning// 11 - Error//// C - is the Customer code flag//// R - is a reserved bit//// Facility - is the facility code//// Code - is the facility's status code////// Define the facility codes//#define FACILITY_SYSTEM 0x0#define FACILITY_STUBS 0x3#define FACILITY_RUNTIME 0x2#define FACILITY_IO_ERROR_CODE 0x4//// Define the severity codes//#define STATUS_SEVERITY_WARNING 0x2#define STATUS_SEVERITY_SUCCESS 0x0#define STATUS_SEVERITY_INFORMATIONAL 0x1#define STATUS_SEVERITY_ERROR 0x3//// MessageId: NETWORK_CATEGORY//// MessageText://// Network Events//#define NETWORK_CATEGORY ((WORD)0x00000001L)//// MessageId: DATABASE_CATEGORY//// MessageText://// Database Events//#define DATABASE_CATEGORY ((WORD)0x00000002L)//// MessageId: UI_CATEGORY//// MessageText://// UI Events//#define UI_CATEGORY ((WORD)0x00000003L) // The following are message definitions.//// MessageId: MSG_INVALID_COMMAND//// MessageText://// The command is not valid.//#define MSG_INVALID_COMMAND ((DWORD)0xC0020100L)//// MessageId: MSG_BAD_FILE_CONTENTS//// MessageText://// File %1 contains content that is not valid.//#define MSG_BAD_FILE_CONTENTS ((DWORD)0xC0000101L)//// MessageId: MSG_RETRIES//// MessageText://// There have been %1 retries with %2 success! Disconnect from// the server and try again later.//#define MSG_RETRIES ((DWORD)0x80000102L)//// MessageId: MSG_COMPUTE_CONVERSION//// MessageText://// %1 %%4096 = %2 %%4097. //#define MSG_COMPUTE_CONVERSION ((DWORD)0x40000103L) // The following are the parameter strings *///// MessageId: QUARTS_UNITS//// MessageText://// quarts%0//#define QUARTS_UNITS ((DWORD)0x00001000L)//// MessageId: GALLONS_UNITS//// MessageText://// gallons%0//#define GALLONS_UNITS ((DWORD)0x00001001L)
Per registrare "MyEventProvider" come source event, modificare il registro del sistema manualmente, in particolare:
La seguente tabella mostra i valori da inserire nelle varie chiavi da creare:
Questo numero normalmente i tipi di eventi ecco il bitmask:
Una volta compilata la DLL e registrata nel registro di sistema, possiamo testare il tutto creando una funzione che ci semplifichi la scrittura del codice.
La seguente è una funzione di esempio che ho creato e che è possibile utilizzare nel proprio codice.
#include "stdafx.h"
#include <windows.h>
#include "MyEventProvider.h" // Questo include è fondamentale per riconoscere i MessageId, è l'header generato durante la compilazione della DLL */
#define PROVIDER_NAME L"MyProvider" // Questo è il nome dato al nostro event provider, è possibile cambiarlo*/
int WriteLog(DWORD Type, WORD Category, DWORD Msg, int Dim, LPCWSTR* Param)
{
int err = 0;
HANDLE hEventLog = NULL;
// The source name (provider) must exist as a subkey of Application.
hEventLog = RegisterEventSource(NULL, PROVIDER_NAME);
if (NULL == hEventLog)
err = GetLastError();
goto cleanup;
}
// This event includes user-defined data as part of the event. The event message
// does not use insert strings.
if (!ReportEvent(hEventLog, Type, Category, Msg, NULL, Dim, 0, Param, NULL))
cleanup:
if (hEventLog)
DeregisterEventSource(hEventLog);
return err;
Per testare il codice, creare un progetto C++, per esempio console application:
// This event uses insert strings.
pInsertStrings[0] = L"3";
pInsertStrings[1] = L"0";
if (WriteLog(EVENTLOG_WARNING_TYPE, NETWORK_CATEGORY, MSG_RETRIES, 2, (LPCWSTR*)pInsertStrings) != 0)
wprintf(L"ReportEvent failed with 0x%x for event 0x%x.\n", GetLastError(), MSG_RETRIES);
Con questo esempio si scrive un evento di warning di categoria Network Events, con messaggio There have been 3 retries with 0 success! Disconnect fromthe server and try again later.