root/trunk/bevutils/EventLogger.d

Revision 20, 11.8 kB (checked in by teales, 1 year ago)

Bevutils files initial check-in.

Line 
1 /**
2  * Authors: Steve Teale - steve.teale@britseyeview.com
3  *
4  * Date: 2007/05/19
5  * History: V0.1
6  * License: Use freely for any purpose.
7  */
8 module bevutils.eventlogger;
9  
10 import std.stdio;
11 import std.c.windows.windows;
12 import std.string;
13
14 enum FMT_MSG : uint
15 {
16     FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
17     FORMAT_MESSAGE_IGNORE_INSERTS  = 0x00000200,
18     FORMAT_MESSAGE_FROM_STRING     = 0x00000400,
19     FORMAT_MESSAGE_FROM_HMODULE    = 0x00000800,
20     FORMAT_MESSAGE_FROM_SYSTEM     = 0x00001000,
21     FORMAT_MESSAGE_ARGUMENT_ARRAY  = 0x00002000,
22     FORMAT_MESSAGE_MAX_WIDTH_MASK  = 0x000000FF
23 }
24
25 alias PVOID PSID;
26
27 extern (Windows)
28 {
29     HANDLE RegisterEventSourceA(LPCTSTR lpUNCServerName, LPCTSTR lpSourceName);
30     BOOL ReportEventA(HANDLE hEventLog,
31             WORD wType,
32             WORD wCategory,
33             DWORD dwEventID,
34             PSID lpUserSid,
35             WORD wNumStrings,
36             DWORD dwDataSize,
37             LPCTSTR* lpStrings,
38             LPVOID lpRawData);
39     BOOL DeregisterEventSource(HANDLE hEventLog);
40     UINT GetSystemDirectoryA(LPTSTR lpBuffer, UINT uSize);
41 }
42
43 /**
44  * A class to write entries to the Windows system event log.
45  */
46 class EventLogger
47 {
48    enum : ushort
49    {
50       EVENTLOG_SUCCESS                = 0X0000,
51       EVENTLOG_ERROR_TYPE             = 0x0001,
52       EVENTLOG_WARNING_TYPE           = 0x0002,
53       EVENTLOG_INFORMATION_TYPE       = 0x0004,
54       EVENTLOG_AUDIT_SUCCESS          = 0x0008,
55       EVENTLOG_AUDIT_FAILURE          = 0x0010
56    }
57
58    char* _appName;
59    HANDLE _eventSource;
60
61    /*****************************************************************************************
62     * This is an explicit constructor to set a specific event source.
63     *
64     * To log an event, the event source must exist in the registry, and so the constructor
65     * by default will ensure that an appropriate registry entry exists. For this to work,
66     * the message file gpmf.dll must be present in the SYSTEM32 directory.
67     *
68     * Params:
69     *    appName = The event source name
70     *    forceRegEntry = Whether to attempt to create the event source registry entry.
71     *                    If the entry already exists, no action is taken.
72     */
73    this(char[] appName, bool forceRegEntry = true) /// A comment
74    {
75       _appName = toStringz(appName);
76       _eventSource = RegisterEventSourceA(null, _appName);
77       if (forceRegEntry)
78          createRegistryEntry(appName);
79    }
80
81    /*****************************************************************************************
82     * A general-purpose constructor to set the event source from the module file name.
83     *
84     * To log an event, the event source must exist in the registry, and so the constructor
85     * by default will ensure that an appropriate registry entry exists. For this to work,
86     * the message file gpmf.dll must be present in the SYSTEM32 directory.
87     *
88     * Params:
89     *    forceRegEntry = Whether to attempt to create the event source registry entry.
90     *                    If the entry already exists, no action is taken.
91     */
92    this(bool forceRegEntry = true)
93    {
94       char[260] mfn;
95       GetModuleFileNameA(null, mfn.ptr, 260);
96       char[] smfn = std.string.toString(mfn.ptr);
97       int lbs = rfind(smfn, '\\');
98       int period = rfind(smfn, '.');
99       smfn = smfn[lbs+1 .. period];
100       this(smfn, forceRegEntry);
101    }
102
103    ~this()
104    {
105       DeregisterEventSource(_eventSource);
106    }
107
108    /*****************************************************************************************
109     * Creates an event-source entry in HKLM\SOFTWARE\CurrentControlSet\Services\Eventlog.
110     *
111     * To log an event properly, an event source must exist in the registry. This method
112     * creates an entry pointing to %SYSTEM32%\gpmf.dll, which is a very basic message file
113     * with the usual range of severity entries, and with a single %1 replacement token.
114     *
115     * Params:
116     *    appName = The event source name
117     */
118    public static bool createRegistryEntry(char[] appName)
119    {
120       char[260] sd;
121       GetSystemDirectoryA(sd.ptr, 260);
122       char[]sds = std.string.toString(sd.ptr);
123       char[] val = sds ~ "\\gpmf.dll\0";
124       uint ts = 7;
125       char *path = toStringz("SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\" ~ appName);
126       HKEY key = null;
127       uint dispos;
128       bool rv = false;
129       try
130       {
131          do {
132             if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, path, 0, null, 0, KEY_SET_VALUE, null, &key, &dispos)
133                      != ERROR_SUCCESS)
134             {
135                // It's already there - we presume it has suitable values
136                rv = true;
137                break;
138             }
139             if (dispos == REG_CREATED_NEW_KEY)
140             {
141                if (RegSetValueExA(key, toStringz("TypesSupported"), 0u, cast(uint) REG_DWORD, cast(ubyte*) &ts, 4)
142                            != ERROR_SUCCESS)
143                   break;
144                if (RegSetValueExA(key, toStringz("EventMessageFile"), 0u, cast(uint) REG_SZ, cast(ubyte*) &val[0], val.length)
145                            != ERROR_SUCCESS)
146                   break;
147             }
148             rv = true;
149          } while (0);
150       }
151       finally
152       {
153          if (key != null)
154             RegCloseKey(key);
155       }
156       return rv;
157    }
158
159    /*****************************************************************************************
160     * Log a message to the system event log.
161     *
162     * This is a general for where you specify the message and the severity.  For severity,
163     * use the enum values EVENTLOG_SUCCESS (no message prefix), EVENTLOG_INFO_TYPE
164     * (Info: message prefix), EVENTLOG_WARNING_TYPE (Warning: message prefix),
165     * EVENTLOG_ERROR_TYPE (Error: message prefix).
166     *
167     * Params:
168     *    msg = The message to be logged.
169     *    severity = Enum values as above.
170     */
171    public void logEvent(char[] msg, ushort severity)
172    {
173       char*[1] msgList;
174       static int xlate[5] = [ 0,3,2,0,1 ];
175       uint t = xlate[severity];
176       uint code = (t << 30) | t;
177       msgList[0] = std.string.toStringz(msg);
178
179       if (_eventSource != null)
180       {
181           ReportEventA(_eventSource,          // handle of event source
182                       severity,               // event type
183                       0,                      // event category
184                       code,                   // event ID
185                       null,                   // current user's SID
186                       1,                      // number of message strings
187                       0,                      // no bytes of raw data
188                       cast(char**) msgList,   // array of error strings
189                       null);                  // no raw data
190
191       }
192    }
193
194    /*****************************************************************************************
195     * Log a general purpose message to the system event log.
196     *
197     * This is a specific method implemented as
198     * logMessage(char[] msg) { logEvent(msg, EVENTLOG_SUCCESS); }
199     *
200     * Params:
201     *    msg = The message to be logged.
202     */
203    public void logMessage(char[] msg) { logEvent(msg, EVENTLOG_SUCCESS); }
204
205    /*****************************************************************************************
206     * Log an information message to the system event log.
207     *
208     * This is a specific method implemented as
209     * logMessage(char[] msg) { logEvent(msg, EVENTLOG_INFORMATION_TYPE); }
210     *
211     * Params:
212     *    msg = The message to be logged.
213     */
214    public void logInfo(char[] msg) { logEvent(msg, EVENTLOG_INFORMATION_TYPE); }
215
216       /*****************************************************************************************
217     * Log a warning message to the system event log.
218     *
219     * This is a specific method implemented as
220     * logMessage(char[] msg) { logEvent(msg, EVENTLOG_WARNING_TYPE); }
221     *
222     * Params:
223     *    msg = The message to be logged.
224     */
225    public void logWarning(char[] msg) { logEvent(msg, EVENTLOG_WARNING_TYPE); }
226
227    /*****************************************************************************************
228     * Log an error message to the system event log.
229     *
230     * This is a specific method implemented as
231     * logMessage(char[] msg) { logEvent(msg, EVENTLOG_ERROR_TYPE); }
232     *
233     * Params:
234     *    msg = The message to be logged.
235     */
236    public void logError(char[] msg) { logEvent(msg, EVENTLOG_ERROR_TYPE); }
237
238    /*****************************************************************************************
239     * Get an error message from Windows given a Win32 error number.
240     *
241     * Params:
242     *    errnum = The error number for which to recover a message.
243     */
244    public char[] getWindowsErrorText(uint errnum)
245    {
246       DWORD dwRet;
247       char* lpszTemp = null;
248       char[512] buf;
249       char[] rv;
250
251       dwRet = FormatMessageA( FMT_MSG.FORMAT_MESSAGE_ALLOCATE_BUFFER | FMT_MSG.FORMAT_MESSAGE_FROM_SYSTEM |FMT_MSG.FORMAT_MESSAGE_ARGUMENT_ARRAY,
252                        null,
253                        errnum,
254                        0,
255                        cast(char *) &lpszTemp,
256                        0,
257                        null);
258
259       // supplied buffer is not long enough
260       if ( !dwRet || 512 < dwRet+14)
261       {
262          rv = "";
263       }
264       else
265       {
266          rv = std.string.toString(lpszTemp);
267       } 
268       if (lpszTemp)
269          LocalFree(cast(HLOCAL) lpszTemp );
270       return rv;
271    }
272
273    /*****************************************************************************************
274     * Log an error message incorporating Win32 error details to the system event log.
275     *
276     * This method allows you to provide the error number from GetLastError().
277     *
278     * Params:
279     *    msg = The message to be prepended to the error details.
280     *    errnum = The return value from GetLastError().
281     */
282    public void logWindowsError(char[] msg, uint errnum)
283    {
284       DWORD dwRet;
285       char* lpszTemp = null;
286       char[512] buf;
287       char[] rv;
288       char[] delim;
289
290       dwRet = FormatMessageA( FMT_MSG.FORMAT_MESSAGE_ALLOCATE_BUFFER | FMT_MSG.FORMAT_MESSAGE_FROM_SYSTEM |FMT_MSG.FORMAT_MESSAGE_ARGUMENT_ARRAY,
291                        null,
292                        errnum,
293                        0,
294                        cast(char *) &lpszTemp,
295                        0,
296                        null);
297
298       // supplied buffer is not long enough
299       if ( !dwRet || 512 < dwRet+14)
300       {
301          rv = "";
302          delim = "";
303       }
304       else
305       {
306          rv = std.string.toString(lpszTemp);
307          delim = "\n";
308       } 
309       if (lpszTemp)
310          LocalFree(cast(HLOCAL) lpszTemp );
311       char[] serr = (msg == "")?
312                      "(" ~ std.string.toString(errnum) ~ "): " ~ rv:
313                      msg ~ "\n(" ~ std.string.toString(errnum) ~ "): " ~ rv;
314       logEvent(serr, EVENTLOG_ERROR_TYPE);
315    }
316
317    /*****************************************************************************************
318     * Log an error message incorporating Win32 error details to the system event log.
319     *
320     * This method calls GetLastError() to get the error number for you.
321     *
322     * Params:
323     *    msg = The message to be prepended to the error details.
324     *    errnum = The return value from GetLastError().
325     */
326    public void logWindowsError(char[] msg)
327    {
328       uint errnum = GetLastError();
329       logWindowsError(msg, errnum);
330    }
331 }
332 /*
333 void main(char[][] args)
334 {
335    EventLogger el = new EventLogger();
336    el.logMessage("This is steve's event");
337    el.logInfo("This is steve's event");
338    el.logWarning("This is steve's event");
339    el.logError("This is steve's event");
340    el.logWindowsError("Whatever", 10u);
341    writefln(el.getWindowsErrorText(10));
342    delete el;
343 }
344 */
Note: See TracBrowser for help on using the browser.