GENERIC_SERVER  0.0.0.9
A light-weight, cross-platform, pluggable, extensible and secure framework for deploying C++ plug-ins.
 All Classes Files Functions Variables Typedefs Pages
XEventLog.cpp
1 // XEventLog.cpp Version 1.0
2 //
3 // Author: Hans Dietrich
4 // hdietrich2@hotmail.com
5 //
6 // This software is released into the public domain.
7 // You are free to use it in any way you like.
8 //
9 // This software is provided "as is" with no expressed
10 // or implied warranty. I accept no liability for any
11 // damage or loss of business that this software may cause.
12 //
14 
15 
18 //
19 // NOTE
20 // Change C/C++ Precompiled Headers settings to
21 // "Not using precompiled headers" for this file.
22 //
25 #ifdef WINDOWS
26 // this file does not need any MFC
27 //#include "stdafx.h"
28 
29 // if you are not using MFC, you must have the following three includes:
30 #include <windows.h>
31 #include <TCHAR.h>
32 #include <crtdbg.h>
33 
34 
35 #include "XEventLog.h"
36 
37 
38 #ifdef __AFXWIN_H__
39 #ifdef _DEBUG
40 #define new DEBUG_NEW
41 #undef THIS_FILE
42 static char THIS_FILE[] = __FILE__;
43 #endif
44 #else
45 #define TRACE
46 #pragma warning(disable : 4127)
47 #endif // __AFXWIN_H__
48 
50 //
51 // ctor
52 //
53 // Purpose: Construct CXEventLog object.
54 //
55 // Parameters: lpszApp - name of app (event log source). This
56 // parameter is optional. If it is not
57 // specified, Init() must be called separately.
58 // lpszEventMessageDll - fully-qualified name of event message
59 // dll. Includes complete path and file
60 // name. Example: "C:\\bin\\MyMessage.dll".
61 // This parameter is optional.
62 //
63 // Returns: None
64 //
65 CXEventLog::CXEventLog(LPCTSTR lpszApp /*= NULL*/,
66  LPCTSTR lpszEventMessageDll /*= NULL*/)
67 {
68 #ifdef _DEBUG
69  if ((lpszApp == NULL) || (lpszApp[0] == _T('\0')))
70  {
71  TRACE(_T("=== No app specified in CXEventLog ctor. ")
72  _T("Be sure to call Init() before calling Write(). ===\n"));
73  }
74 #endif
75 
76  m_hEventLog = NULL;
77  m_pszAppName = NULL;
78 
79  BOOL bRet = FALSE;
80 
81  // open event log
82  if (lpszApp && (lpszApp[0] != _T('\0')))
83  bRet = Init(lpszApp, lpszEventMessageDll);
84 }
85 
87 //
88 // dtor
89 //
90 // Purpose: Destroy CXEventLog object.
91 //
92 // Parameters: none
93 //
94 // Returns: none
95 //
96 CXEventLog::~CXEventLog()
97 {
98  Close();
99  if (m_pszAppName)
100  delete [] m_pszAppName;
101  m_pszAppName = NULL;
102 }
103 
105 //
106 // Close()
107 //
108 // Purpose: Close event log handle. Called automatically by dtor.
109 //
110 // Parameters: none
111 //
112 // Returns: none
113 //
114 void CXEventLog::Close()
115 {
116  if (m_hEventLog)
117  ::DeregisterEventSource(m_hEventLog);
118  m_hEventLog = NULL;
119 }
120 
122 //
123 // GetAppName()
124 //
125 // Purpose: Get name of currently registered app.
126 //
127 // Parameters: none
128 //
129 // Returns: LPTSTR - app name
130 //
131 LPTSTR CXEventLog::GetAppName()
132 {
133  return m_pszAppName;
134 }
135 
137 //
138 // Init()
139 //
140 // Purpose: Initialize the registry for event logging from this app.
141 // Normally Init() is called from ctor. If default ctor (no args)
142 // is used, then Init() must be called before Write() is called.
143 // Sets class variable m_hEventLog.
144 //
145 // Parameters: lpszApp - name of app (event log source).
146 // MUST BE SPECIFIED.
147 // lpszEventMessageDll - fully-qualified name of event message
148 // dll. Includes complete path and file
149 // name. Example: "C:\\bin\\MyMessage.dll".
150 // This parameter is optional.
151 //
152 // Returns: BOOL - TRUE = success
153 //
154 BOOL CXEventLog::Init(LPCTSTR lpszApp, LPCTSTR lpszEventMessageDll /*= NULL*/)
155 {
156  _ASSERTE((lpszApp != NULL) && (lpszApp[0] != _T('\0')));
157  if (!lpszApp || lpszApp[0] == _T('\0'))
158  return FALSE;
159 
160  Close(); // close event log if already open
161 
162  SetAppName(lpszApp);
163 
164  BOOL bRet = RegisterSource(lpszApp, lpszEventMessageDll);
165  _ASSERTE(bRet);
166 
167  if (bRet)
168  {
169  m_hEventLog = ::RegisterEventSource(NULL, lpszApp);
170  }
171  else
172  {
173  TRACE(_T("RegisterSource failed\n"));
174  }
175 
176  _ASSERTE(m_hEventLog != NULL);
177 
178  return (m_hEventLog != NULL);
179 }
180 
182 //
183 // Write()
184 //
185 // Purpose: Write string to event log
186 //
187 // Parameters: wType - event type. See ReportEvent() in MSDN for complete list.
188 // lpszMessage - string to log
189 //
190 // Returns: BOOL - TRUE = success
191 //
192 BOOL CXEventLog::Write(WORD wType, LPCTSTR lpszMessage)
193 {
194  BOOL bRet = TRUE;
195 
196  _ASSERTE(m_hEventLog != NULL);
197  if (!m_hEventLog)
198  {
199  // Init() not called
200  return FALSE;
201  }
202 
203  _ASSERTE(lpszMessage != NULL);
204  if (!lpszMessage)
205  return FALSE;
206 
207  _ASSERTE((wType == EVENTLOG_ERROR_TYPE) ||
208  (wType == EVENTLOG_WARNING_TYPE) ||
209  (wType == EVENTLOG_INFORMATION_TYPE) ||
210  (wType == EVENTLOG_AUDIT_SUCCESS) ||
211  (wType == EVENTLOG_AUDIT_FAILURE));
212 
213  // get our user name information
214  PSID pSid = GetUserSid();
215 
216  LPCTSTR* lpStrings = &lpszMessage;
217 
218  bRet = ::ReportEvent(m_hEventLog, // event log source handle
219  wType, // event type to log
220  0, // event category
221  0x20000001L, // event identifier (GENERIC_MESSAGE)
222  pSid, // user security identifier (optional)
223  1, // number of strings to merge with message
224  0, // size of binary data, in bytes
225  lpStrings, // array of strings to merge with message
226  NULL); // address of binary data
227 
228  if (pSid)
229  HeapFree(GetProcessHeap(), 0, pSid);
230 
231  return bRet;
232 }
233 
234 
237 //
238 // INTERNAL METHODS
239 //
242 
243 
245 //
246 // RegisterSource() - INTERNAL METHOD
247 //
248 // Purpose: Create entry in registry for message DLL. This method will
249 // create the following registry keys:
250 // HKLM
251 // SYSTEM
252 // CurrentControlSet
253 // Services
254 // Eventlog
255 // Application
256 // <app name>
257 // EventMessageFile = <complete path to message DLL>
258 // TypesSupported = 0x0000001f (31)
259 //
260 // Note that <app name> is the lpszApp parameter, and
261 // <complete path to message DLL> is the lpszEventMessageDll
262 // parameter.
263 //
264 // Parameters: lpszApp - name of app (event log source).
265 // lpszEventMessageDll - fully-qualified name of event message
266 // DLL. Includes complete path and file
267 // name. Example: "C:\\bin\\MyMessage.dll".
268 // If this parameter is NULL, default is to
269 // use exe's path + "XEventMessage.dll".
270 //
271 // Returns: BOOL - TRUE = success
272 //
273 BOOL CXEventLog::RegisterSource(LPCTSTR lpszApp,
274  LPCTSTR lpszEventMessageDll)
275 {
276  _ASSERTE((lpszApp != NULL) && (lpszApp[0] != _T('\0')));
277  if (!lpszApp || lpszApp[0] == _T('\0'))
278  return FALSE;
279 
280  TCHAR * szRegPath =
281  _T("SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\");
282 
283  TCHAR szKey[_MAX_PATH*2];
284  memset(szKey, 0, _MAX_PATH*2*sizeof(TCHAR));
285  _tcscpy(szKey, szRegPath);
286  _tcscat(szKey, lpszApp);
287  TRACE(_T("szKey=<%s>\n"), szKey);
288 
289  // open the registry event source key
290  DWORD dwResult = 0;
291  HKEY hKey = NULL;
292  LONG lRet = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, NULL,
293  REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwResult);
294 
295  if (lRet == ERROR_SUCCESS)
296  {
297  // registry key was opened or created -
298 
299  // === write EventMessageFile key ===
300 
301  TCHAR szPathName[_MAX_PATH*2];
302  memset(szPathName, 0, _MAX_PATH*2*sizeof(TCHAR));
303 
304  if (lpszEventMessageDll)
305  {
306  // if dll path was specified use that - note that this
307  // must be complete path + dll filename
308  _tcsncpy(szPathName, lpszEventMessageDll, _MAX_PATH*2-2);
309  }
310  else
311  {
312  // use app's directory + "XEventMessage.dll"
313  ::GetModuleFileName(NULL, szPathName, MAX_PATH*2-2);
314 
315  TCHAR *cp = _tcsrchr(szPathName, _T('\\'));
316  if (cp != NULL)
317  *cp = _T('\0');
318 
319  _tcscat(szPathName, _T("\\XEventMessage.dll"));
320  }
321 
322  TRACE(_T("szPathName=<%s>\n"), szPathName);
323 
324  ::RegSetValueEx(hKey, _T("EventMessageFile"), 0, REG_SZ,
325  (const BYTE *) szPathName, (_tcslen(szPathName) + 1)*sizeof(TCHAR));
326 
327  // === write TypesSupported key ===
328 
329  // message DLL supports all types
330  DWORD dwSupportedTypes = EVENTLOG_ERROR_TYPE |
331  EVENTLOG_WARNING_TYPE |
332  EVENTLOG_INFORMATION_TYPE |
333  EVENTLOG_AUDIT_SUCCESS |
334  EVENTLOG_AUDIT_FAILURE;
335 
336  ::RegSetValueEx(hKey, _T("TypesSupported"), 0, REG_DWORD,
337  (const BYTE *) &dwSupportedTypes, sizeof(DWORD));
338 
339  ::RegCloseKey(hKey);
340 
341  return TRUE;
342  }
343 
344  return FALSE;
345 }
346 
348 //
349 // GetUserSid() - INTERNAL METHOD
350 //
351 // Purpose: Get SID of current user
352 //
353 // Parameters: none
354 //
355 // Returns: PSID - pointer to alloc'd SID; must be freed by caller.
356 // Example: HeapFree(GetProcessHeap(), 0, pSid);
357 //
358 PSID CXEventLog::GetUserSid()
359 {
360  HANDLE hToken = NULL;
361  PTOKEN_USER ptiUser = NULL;
362  DWORD cbti = 0;
363 
364  // get calling thread's access token
365  if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
366  {
367  if (GetLastError() != ERROR_NO_TOKEN)
368  return NULL;
369 
370  // retry for process token if no thread token exists
371  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
372  return NULL;
373  }
374 
375  // get size of the user information in the token
376  if (GetTokenInformation(hToken, TokenUser, NULL, 0, &cbti))
377  {
378  // call should have failed due to zero-length buffer.
379  return NULL;
380  }
381  else
382  {
383  // call should have failed due to zero-length buffer
384  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
385  return NULL;
386  }
387 
388  // allocate buffer for user information in the token
389  ptiUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, cbti);
390  if (!ptiUser)
391  return NULL;
392 
393  // retrieve the user information from the token
394  if (!GetTokenInformation(hToken, TokenUser, ptiUser, cbti, &cbti))
395  return NULL;
396 
397  DWORD dwLen = ::GetLengthSid(ptiUser->User.Sid);
398 
399  // allocate buffer for SID
400  PSID psid = (PSID) HeapAlloc(GetProcessHeap(), 0, dwLen);
401  if (!psid)
402  return NULL;
403 
404  BOOL bRet = ::CopySid(dwLen, psid, ptiUser->User.Sid);
405  if (!bRet)
406  return NULL;
407 
408  // close access token
409  if (hToken)
410  CloseHandle(hToken);
411 
412  if (ptiUser)
413  HeapFree(GetProcessHeap(), 0, ptiUser);
414 
415  return psid;
416 }
417 
419 //
420 // SetAppName() - INTERNAL METHOD
421 //
422 // Purpose: Stores name of current app
423 //
424 // Parameters: lpszApp - app name
425 //
426 // Returns: none
427 //
428 void CXEventLog::SetAppName(LPCTSTR lpszApp)
429 {
430  if (!lpszApp)
431  return;
432  if (!m_pszAppName)
433  m_pszAppName = new TCHAR [_MAX_PATH*2];
434  if (m_pszAppName)
435  {
436  memset(m_pszAppName, 0, _MAX_PATH*2*sizeof(TCHAR));
437  _tcsncpy(m_pszAppName, lpszApp, _MAX_PATH*2-2);
438  }
439 }
440 #endif