WindowsNT Event Log Viewer






3.38/5 (12 votes)
Dec 1, 1999
2 min read

268617

5595
The Event Viewer is intended to be run under WindowsNT System Manager (check the submission with the same name).
This sample application uses (more or less) the same policy regarding MDI, child dialog encapsulation, controls etc.,
and these things are treated there. In this article you can see two thread routines which you may find interesting.
1. The first is the routine which enumerates event entries, FillEventLogList
.
The structure passed as parameter to this thread routine is
typedef struct _tagEVENTLOGFILTER { BOOL fApplication; /* 'Application' entries */ BOOL fSecurity; /* 'Security' entries */ BOOL fSystem; /* 'System' entries */ BOOL fCustom; /* 'Custom' entries: unused */ TCHAR lpszComputerName[_MAX_PATH + 1]; /* computer name */ HWND hwndLV; /* list view handle */ HWND hwndDlg; /* encapsulated dialog handle */ HWND hwndProgr; /* progress bar handle */ HANDLE hCancelEvent; /* 'Cancel' event handle */ HANDLE hCloseEvent; /* 'Close' event handle */ unsigned uThreadId; /* thread ID */ TCHAR lpszCustomEventFileName[_MAX_PATH + 1]; /* Custom file name: unused */ } EVENTLOGFILTER, *LPEVENTLOGFILTER;The routine looks like this:
unsigned int __stdcall FillEventLogList(LPVOID lpParam) { EVENTLOGFILTER *pelf = 0; int nRetVal = 0; HWND hParentWnd = 0, hwndDlg = 0, hwndLV = 0, hwndProgr = 0; HANDLE hEventLog = 0; DWORD dwEventLogRecords = 0, dwOldestEventLogRecord = 0, dwEvLogCounter = 0, dwNumberOfBytesToRead = 0, dwBytesRead = 0, dwMinNumberOfBytesNeeded = 0, dwCancel = 0, dwClose = 0; LPVOID lpEventLogRecordBuffer = 0; TCHAR chFakeBuffer; BOOL bRetVal = FALSE; BOOL fExit = FALSE; UINT uStep = 0, uStepAt = 0, uPos = 0, uOffset = 0; TCHAR lpUNCServerName[_MAX_PATH + 1], lpszEventLogSourceName[_MAX_PATH + 1], lpszErrMsg[1024]; // get thread parameter structure address... pelf = (EVENTLOGFILTER *)lpParam; // ...and retrieve the appropriate handles hwndDlg = pelf->hwndDlg; hwndLV = pelf->hwndLV; hwndProgr = pelf->hwndProgr; // get parent window... hParentWnd = GetParent(hwndDlg); // ...and set user data to 1 (the window has thread running) - will be reset to 0 when thread will terminate SetWindowLong(hParentWnd, GWL_USERDATA, (LONG)pelf); // resize dialog MDIChild_ResizeDlg(hwndDlg, TRUE); // format UNC machine name to work with wsprintf(lpUNCServerName, _T("\\\\%s"), pelf->lpszComputerName); // establish what kind of event log section will show the list if(g_fApplication) _tcscpy(lpszEventLogSourceName, _T("Application")); // APPLICATION else if(g_fSystem) _tcscpy(lpszEventLogSourceName, _T("System")); // SYSTEM else if(g_fSecurity) _tcscpy(lpszEventLogSourceName, _T("Security")); // SECURITY else if(g_fCustom) _tcscpy(lpszEventLogSourceName, pelf->lpszCustomEventFileName); // CUSTOM else { nRetVal = -1; goto _cleanup_; } // lookup for close or cancel (ESC pressed) attempts to intrrerupt thread dwCancel = WaitForSingleObject(pelf->hCancelEvent, 0); dwClose = WaitForSingleObject(pelf->hCloseEvent, 0); while(!fExit) { if(g_fCustom) // never reached hEventLog = OpenBackupEventLog((LPCTSTR)lpUNCServerName, (LPCTSTR)lpszEventLogSourceName); else hEventLog = OpenEventLog((LPCTSTR)lpUNCServerName, (LPCTSTR)lpszEventLogSourceName); if(hEventLog) { if(GetNumberOfEventLogRecords(hEventLog, &dwEventLogRecords) && GetOldestEventLogRecord(hEventLog, &dwOldestEventLogRecord)) { // establish step fro progress bar SendMessage(hwndProgr, PBM_SETRANGE, (WPARAM)0, (LPARAM)MAKELPARAM(0, 100)); uStepAt = (dwEventLogRecords / 100) + 1; for(dwEvLogCounter = dwOldestEventLogRecord; dwEvLogCounter < (dwOldestEventLogRecord + dwEventLogRecords); dwEvLogCounter++) { // advance progress bar with step uStep++; if(uStep % uStepAt == 0) hwndProgr && SendMessage(hwndProgr, PBM_SETPOS, (WPARAM)++uPos, 0); dwCancel = WaitForSingleObject(pelf->hCancelEvent, 0); if(dwCancel == WAIT_OBJECT_0) goto _canceled_; dwClose = WaitForSingleObject(pelf->hCloseEvent, 0); if(dwClose == WAIT_OBJECT_0) goto _close_; // this is a false call (intended to get the real structure size) lpEventLogRecordBuffer = (LPVOID)&chFakeBuffer; dwNumberOfBytesToRead = 1; dwMinNumberOfBytesNeeded = 1; _retry_: bRetVal = ReadEventLog(hEventLog, EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ, dwEvLogCounter, lpEventLogRecordBuffer, dwNumberOfBytesToRead, &dwBytesRead, &dwMinNumberOfBytesNeeded); if(!bRetVal) { g_dwLastError = GetLastError(); if(g_dwLastError == ERROR_INSUFFICIENT_BUFFER) { lpEventLogRecordBuffer = (LPVOID)GlobalAlloc(GPTR, dwMinNumberOfBytesNeeded); if(lpEventLogRecordBuffer == (void *)0) goto _allocationfailure_; dwNumberOfBytesToRead = dwMinNumberOfBytesNeeded; goto _retry_; } else goto _unknownerror_; } else // here we are with complete structure filled; proceed { PEVENTLOGRECORD pELR = 0; TCHAR *lpszSourceName = 0, lpszUserName[_MAX_PATH + 1], *lpszComputerName = 0, lpszRefDomainName[_MAX_PATH + 1], *szSIDType = 0, *szSIDName = 0, sz2[32], *szExpandedString = 0, szSubmitTime[32], szWriteTime[32]; DWORD dwSourceNameLen = 0, dwComputerNameLen = 0, cbName = _MAX_PATH + 1, cbRefDomainName = _MAX_PATH + 1, dwSIDTypeLen = 0, dwSidSize = 0, dwEventTypeLen = 0; PSID pUserSID = 0; SID_NAME_USE _SidNameUse = (SID_NAME_USE)(SidTypeUser - 1); BOOL bRetVal = FALSE; LPBYTE pStrings = 0, pData = 0; UINT x = 0, uSize, uStringOffset, uStepOfString = 0, uImage = 0; // the buffer pELR = (PEVENTLOGRECORD)lpEventLogRecordBuffer; // source name and advance offset uOffset = sizeof(EVENTLOGRECORD); lpszSourceName = (TCHAR *)GlobalAlloc(GPTR, (_MAX_PATH + 1) * sizeof(TCHAR)); strcpy(lpszSourceName, (LPTSTR)((LPBYTE)pELR + uOffset)); dwSourceNameLen = strlen(lpszSourceName); // computer name and advance offset uOffset += strlen(lpszSourceName) + sizeof(TCHAR); lpszComputerName = (TCHAR *)GlobalAlloc(GPTR, (_MAX_PATH + 1) * sizeof(TCHAR)); strcpy(lpszComputerName, (LPTSTR)((LPBYTE)pELR + uOffset)); dwComputerNameLen = strlen(lpszComputerName); uOffset += strlen(lpszComputerName) + sizeof(TCHAR); // SID dwSIDTypeLen = 32; szSIDType = (TCHAR *)GlobalAlloc(GPTR, (dwSIDTypeLen + 1) * sizeof(TCHAR)); // retrieve SID if(pELR->UserSidLength > 0) { pUserSID = (SID *)GlobalAlloc(GPTR, pELR->UserSidLength); memcpy(pUserSID, (PSID)((LPBYTE)pELR + pELR->UserSidOffset), pELR->UserSidLength); cbName = cbRefDomainName = _MAX_PATH + 1; *lpszRefDomainName = *lpszUserName = '\0'; bRetVal = LookupAccountSid(0, pUserSID, lpszUserName, &cbName, lpszRefDomainName, &cbRefDomainName, &_SidNameUse); if(bRetVal) { if(bRetVal) { dwSIDTypeLen = 32; GetNameUse(_SidNameUse, szSIDType, &dwSIDTypeLen); dwSidSize = (15 + 12 + (12 * (*GetSidSubAuthorityCount(pUserSID))) + 1) * sizeof(TCHAR); szSIDName = (TCHAR *)GlobalAlloc(GPTR, (dwSidSize + 1) * sizeof(TCHAR)); ConvertSid(pUserSID, szSIDName, &dwSidSize); } else { strcpy(lpszRefDomainName, "N/A"); strcpy(lpszUserName, "N/A"); strcpy(szSIDType, "N/A"); } } else { } } else { strcpy(lpszRefDomainName, "N/A"); strcpy(lpszUserName, "N/A"); strcpy(szSIDType, "N/A"); } // Now we have to get the description strings. uSize = 0, uStringOffset = pELR->StringOffset; uSize = pELR->DataOffset - pELR->StringOffset; // Strings if(uSize > 0) { pStrings = (LPBYTE)GlobalAlloc(GPTR, uSize * sizeof(BYTE)); memcpy(pStrings, (LPBYTE)pELR + uStringOffset, uSize); // Strings uStepOfString = 0; szExpandedString = (TCHAR *)GlobalAlloc(GPTR, (uSize + MAX_MSG_LENGTH) * sizeof(TCHAR)); for(x = 0; x < pELR->NumStrings; x++) { if(x == 0) { strcpy(szExpandedString, (TCHAR *)pStrings + uStepOfString); if(x < (UINT)pELR->NumStrings - 1) strcat(szExpandedString, ","); } else strcat(szExpandedString, (TCHAR *)pStrings + uStepOfString); uStepOfString = strlen((TCHAR *)pStrings + uStepOfString) + 1; } } // Data pData = (LPBYTE)GlobalAlloc(GPTR, pELR->DataLength * sizeof(BYTE)); memcpy(pData, (LPBYTE)((LPBYTE)pELR + pELR->DataOffset), pELR->DataLength); dwEventTypeLen = 32; GetEventLogType(sz2, pELR->EventType, &dwEventTypeLen); GetEventLogImage(&uImage, pELR->EventType); lstrcpyn(szSubmitTime, asctime(localtime((time_t *)&(pELR->TimeGenerated))), 25); lstrcpyn(szWriteTime, asctime(localtime((time_t *)&(pELR->TimeWritten))), 25); InsertRowInList(hwndLV, 9, &dwEvLogCounter, lpszSourceName, lpszUserName, szSIDName, lpszRefDomainName, sz2, uImage, szSubmitTime, szWriteTime); SafeDeletePointer(pData, pELR->DataLength); SafeDeletePointer(szExpandedString, uSize); SafeDeletePointer(pStrings, pELR->DataOffset - pELR->StringOffset); SafeDeletePointer(szSIDName, dwSidSize + 1); SafeDeletePointer(szSIDType, dwSIDTypeLen + 1); SafeDeletePointer(lpszSourceName, dwSourceNameLen); SafeDeletePointer(lpszComputerName, dwComputerNameLen); SafeDeletePointer(pUserSID, pELR->UserSidLength); SafeDeletePointer(lpEventLogRecordBuffer, dwNumberOfBytesToRead); } } goto _cleanup_; } else ReportLastError(0, 0, TRUE); _unknownerror_: ReportLastError(lpszErrMsg, 0, TRUE); goto _cleanup_; _allocationfailure_: LoadString(g_hInstance, IDS_ERR_ALLOCATIONFAILURE, lpszErrMsg, 1024); MessageBox(0, lpszErrMsg, 0, MB_OK | MB_ICONSTOP); goto _cleanup_; _canceled_: nRetVal = 1; goto _cleanup_; _close_: nRetVal = 2; goto _cleanup_; _cleanup_: fExit = TRUE; CloseEventLog(hEventLog); hEventLog = 0; } else { fExit = TRUE; ReportLastError(0, 0, TRUE); } } // final cleanup on dialog if(nRetVal != 2) { if(IsWindow(hwndDlg)) MDIChild_ResizeDlg(hwndDlg, FALSE); if(IsWindow(hParentWnd)) SetWindowLong(hParentWnd, GWL_USERDATA, (LONG)0); } CloseHandle(pelf->hCancelEvent); CloseHandle(pelf->hCloseEvent); GlobalFree(pelf); pelf = 0; return nRetVal; }
The key to understanding this king of enumeration and especially the using of structure PEVENTLOGRECORD
resides in its definition:
typedef struct _EVENTLOGRECORD { DWORD Length; DWORD Reserved; DWORD RecordNumber; DWORD TimeGenerated; DWORD TimeWritten; DWORD EventID; WORD EventType; WORD NumStrings; WORD EventCategory; WORD ReservedFlags; DWORD ClosingRecordNumber; DWORD StringOffset; DWORD UserSidLength; DWORD UserSidOffset; DWORD DataLength; DWORD DataOffset; // // Then follow: // // TCHAR SourceName[] // TCHAR Computername[] // SID UserSid // TCHAR Strings[] // BYTE Data[] // CHAR Pad[] // DWORD Length; // } EVENTLOGRECORD;
Because this is a variable-length structure, the using of offsets, conversion and allocations seems to me the best approach. (By the way, spending more time
you can easily rewrite a better routine).
Also, you could spend some time reading the ConvertSid
function, which retrieves the name of SID looking in
some weird registry key. Again you have to deal with a nonstandard structure, SID
, which cannot be manipulated manually, but only using the
API specific routines.
BOOL ConvertSid(PSID pSid, LPTSTR pszSidText, LPDWORD dwBufferLen) { DWORD dwSubAuthorities; DWORD dwSidRev = SID_REVISION; DWORD dwCounter; DWORD dwSidSize; PSID_IDENTIFIER_AUTHORITY psia; if(!IsValidSid(pSid)) return FALSE; psia = GetSidIdentifierAuthority(pSid); dwSubAuthorities =* GetSidSubAuthorityCount(pSid); dwSidSize = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR); if (*dwBufferLen < dwSidSize) { *dwBufferLen = dwSidSize; SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; } dwSidSize=wsprintf(pszSidText, TEXT("S-%lu-"), dwSidRev ); if((psia->Value[0] != 0) || (psia->Value[1] != 0)) dwSidSize += wsprintf(pszSidText + lstrlen(pszSidText), TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"), (USHORT)psia->Value[0], (USHORT)psia->Value[1], (USHORT)psia->Value[2], (USHORT)psia->Value[3], (USHORT)psia->Value[4], (USHORT)psia->Value[5]); else dwSidSize += wsprintf(pszSidText + lstrlen(pszSidText), TEXT("%lu"), (ULONG)(psia->Value[5] ) + (ULONG)(psia->Value[4] << 8) + (ULONG)(psia->Value[3] << 16) + (ULONG)(psia->Value[2] << 24) ); for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++) dwSidSize+=wsprintf(pszSidText + dwSidSize, TEXT("-%lu"), *GetSidSubAuthority(pSid, dwCounter)); return TRUE; }
Double-clicking on an event log entry represented by a row in list, the data window appears, showing more detailed data about the entry you choose. You have to
excuse me for the resize problems (sometimes - or I must say often? - resizing parent data window does not resize encapsulated dialog). Again, the job is done via
another thread, which use the EVENTID
structure
typedef struct _tagEVENTID { TCHAR lpszMachineName[_MAX_PATH + 1]; TCHAR lpszEventName[_MAX_PATH + 1]; DWORD dwEventId; HWND hwndDlg; } EVENTID, *LPEVENTID;which simply identifies the record entry, and the routine:
unsigned int __stdcall ShowEventData(LPVOID lpParam) { LPEVENTID peid = (LPEVENTID)lpParam; int nRetVal = 0; HWND hwndDlg = peid->hwndDlg; HWND hwndEditStrings = GetDlgItem(hwndDlg, IDE_STRINGS); HWND hwndEditData = GetDlgItem(hwndDlg, IDE_DATA); DWORD dwRecId = peid->dwEventId; TCHAR lpUNCServerName[_MAX_PATH + 1]; TCHAR lpSourceName[_MAX_PATH + 1]; HANDLE hEventLog = 0; DWORD dwEventLogRecords = 0; DWORD dwOldestEventLogRecord = 0; DWORD dwEvLogCounter = 0; LPVOID lpEventLogRecordBuffer = 0; char chFakeBuffer = ' '; DWORD dwNumberOfBytesToRead = 0; DWORD dwBytesRead = 0; DWORD dwMinNumberOfBytesNeeded = 0; BOOL bRetVal = FALSE; TCHAR lpszEventLogSourceName[_MAX_PATH + 1]; wsprintf(lpUNCServerName, _T("\\\\%s"), peid->lpszMachineName); wsprintf(lpSourceName, _T("%s"), peid->lpszEventName); if(g_fApplication) _tcscpy(lpszEventLogSourceName, _T("Application")); else if(g_fSystem) _tcscpy(lpszEventLogSourceName, _T("System")); else if(g_fSecurity) _tcscpy(lpszEventLogSourceName, _T("Security")); // else if(g_fCustom) // _tcscpy(lpszEventLogSourceName, _T("Application")); else { nRetVal = -1; goto _cleanup_; } hEventLog = OpenEventLog((LPCTSTR)lpUNCServerName, (LPCTSTR)lpszEventLogSourceName); if(hEventLog) { if(GetNumberOfEventLogRecords(hEventLog, &dwEventLogRecords) && GetOldestEventLogRecord(hEventLog, &dwOldestEventLogRecord)) { for(dwEvLogCounter = dwOldestEventLogRecord; dwEvLogCounter <= (dwOldestEventLogRecord + dwEventLogRecords); dwEvLogCounter++) { if(dwEvLogCounter != dwRecId) continue; lpEventLogRecordBuffer = (LPVOID)&chFakeBuffer; dwNumberOfBytesToRead = 1; dwMinNumberOfBytesNeeded = 0; _retry_: bRetVal = ReadEventLog(hEventLog, EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ, dwEvLogCounter, lpEventLogRecordBuffer, dwNumberOfBytesToRead, &dwBytesRead, &dwMinNumberOfBytesNeeded); if(!bRetVal) { g_dwLastError = GetLastError(); if(g_dwLastError == ERROR_INSUFFICIENT_BUFFER) { lpEventLogRecordBuffer = (LPVOID)GlobalAlloc(GPTR, dwMinNumberOfBytesNeeded); if(lpEventLogRecordBuffer == (void *)0) goto _allocationfailure_; dwNumberOfBytesToRead = dwMinNumberOfBytesNeeded; goto _retry_; } else goto _unknownerror_; } else { PEVENTLOGRECORD pELR = 0; LPBYTE pData = 0; HMODULE hModule = 0; TCHAR szExeFile[_MAX_PATH + 1], szExeFilePath[_MAX_PATH + 1]; HKEY hk = (HKEY)0; TCHAR szKeyName[_MAX_PATH + 1]; DWORD dwMaxPath; DWORD dwType; LPBYTE pStrings = 0; UINT uStringOffset; TCHAR *szExpandedString; LPVOID lpszBuffer = 0; pELR = (PEVENTLOGRECORD)lpEventLogRecordBuffer; pData = (LPBYTE)GlobalAlloc(GPTR, pELR->DataLength * sizeof(BYTE)); memcpy(pData, (LPBYTE)((LPBYTE)pELR + pELR->DataOffset), pELR->DataLength); { UINT x, uStepOfString = 0; pStrings = (LPBYTE)GlobalAlloc(GPTR, pELR->DataOffset - pELR->StringOffse * sizeof(BYTE)); memcpy(pStrings, (LPBYTE)pELR + pELR->StringOffset, pELR->DataOffset - pELR->StringOffset); szExpandedString = (TCHAR *)GlobalAlloc(GPTR, (pELR->DataOffset - pELR->StringOffset + 1024) * sizeof(TCHAR)); for(x = 0; x < pELR->NumStrings; x++) { if(x == 0) { strcpy(szExpandedString, (TCHAR *)pStrings + uStepOfString); if(x < (UINT)pELR->NumStrings - 1) strcat(szExpandedString, ","); } else strcat(szExpandedString, (TCHAR *)pStrings + uStepOfString); uStepOfString = strlen((TCHAR *)pStrings + uStepOfString) + 1; } wsprintf(szKeyName, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\%s\\%s"), lpszEventLogSourceName, peid->lpszEventName); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0L, KEY_READ, &hk) == NOERROR) { dwMaxPath = _MAX_PATH + 1; if(RegQueryValueEx(hk, _T("EventMessageFile"), 0, &dwType, (LPBYTE)szExeFile, &dwMaxPath) == NOERROR) { if(ExpandEnvironmentStrings(szExeFile, szExeFilePath, _MAX_PATH + 1) == 0) strcpy(szExeFilePath, szExeFile); hModule = LoadLibraryEx(szExeFilePath, 0, DONT_RESOLVE_DLL_REFERENCES); if(hModule) { TCHAR **_sz = (TCHAR**)GlobalAlloc(GPTR, (pELR->NumStrings) * sizeof(TCHAR *)); register UINT z; uStringOffset = 0; for(z = 0; z < pELR->NumStrings; z++) { _sz[z] = (TCHAR *)GlobalAlloc(GPTR, (strlen((TCHAR *)pStrings + uStringOffset) + 1) * sizeof(TCHAR)); strcpy(_sz[z], (TCHAR *)pStrings + uStringOffset); uStringOffset += strlen((TCHAR *)pStrings + uStringOffset) + 1; } FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, hModule, pELR->EventID, 0, (LPTSTR)&lpszBuffer, 1024, _sz ); for(z = 0; z < pELR->NumStrings; z++) { SafeDeletePointer(_sz[z], strlen(_sz[z])); _sz[z] = 0; } SafeDeletePointer(_sz, (pELR->NumStrings) * sizeof(TCHAR *)); _sz = 0; if(lpszBuffer) { strcpy(szExpandedString, (TCHAR *)lpszBuffer); uStringOffset = strlen(szExpandedString); } if(lpszBuffer) LocalFree(lpszBuffer); FreeLibrary(hModule); } } RegCloseKey(hk); } SendMessage(hwndEditStrings, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)szExpandedString); SafeDeletePointer(szExpandedString, strlen(szExpandedString)); } { TCHAR _str[1024]; _tcscpy(_str, _T("")); if(pELR->DataLength > 0) { register UINT x; for(x = 0; x < pELR->DataLength; x += 8) { TCHAR _strAux[1024]; register UINT y; wsprintf(_strAux, "%.4x: ", x); _tcscat(_str, _strAux); for(y = x; y < x + 8; y++) { wsprintf(_strAux, "%.2x ", pData[y]); _tcscat(_str, _strAux); } _tcscat(_str, _T(" ")); for(y = x; y < x + 8; y++) { if(!isprint((int)pData[y])) _tcscat(_str, _T(".")); else { TCHAR s[2]; s[0] = (TCHAR)pData[y]; s[1] = '\0'; _tcscat(_str, s); } } _tcscat(_str, _T("\r\n")); } } else _tcscat(_str, _T("No data available.")); SendMessage(hwndEditData, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)_str); } } } goto _cleanup_; } else ReportLastError(0, 0, TRUE); _unknownerror_: MessageBox(0, TEXT("Unknown error."), 0, MB_OK | MB_ICONSTOP); goto _cleanup_; _allocationfailure_: MessageBox(0, TEXT("Allocation failure."), 0, MB_OK | MB_ICONSTOP); goto _cleanup_; _cleanup_: CloseEventLog(hEventLog); hEventLog = 0; } else ReportLastError(0, 0, TRUE); #pragma warning(disable:4127) SafeDeletePointer(peid, sizeof(EVENTID)); return 0L; }Although the string
szExpandedString
is allocated with only 1024 length (so can truncate sometimes thre REAL value), again you can easily
replace with the correct size doing some checkings before. The construction key is now a registry key specific to the record entry, located under
SYSTEM\CurrentControlSet\Sevices\EventLog
key. If opening of this key succeeds and the call of ExpandEnvironmentStrings
leads to the
binary file where .mc
message entry resides, then a LoadLibraryEx
call with the
DONT_RESOLVE_DLL_REFERENCES
flag set (to do not load other DLLs, but rather resource section) will lead us to the necessary .mc resource entry,
which is a entry in the message table (usually created with message compiler tool). Now, the call
FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, hModule, pELR->EventID, 0, (LPTSTR)&lpszBuffer, 1024, _sz );will offers us the message bound with this particular event log entry. The rest is history (hexa formatting, dialog stuff).