void EventLogMonitor(void) { DWORD status = ERROR_SUCCESS; DWORD dwWaitReason = 0; DWORD dwLastRecordNumber = 0; int nMonSize = sizeof(event_monitor)/sizeof(struct _EVENT_MONITOR); HANDLE *arrhWaitHandles = (HANDLE*)malloc(nMonSize*sizeof(HANDLE)); g_arrhEventLog = (HANDLE*)malloc(nMonSize*sizeof(HANDLE)); for (int i=0; i<nMonSize; i++) { // Get the DLL that contains the message table string resources for the provider. event_monitor[i].hResource = GetMessageResources(event_monitor[i].resource_dll); if (NULL == event_monitor[i].hResource) { wprintf(L"Fail to load resource file.\n"); } // Get a handle to a manual reset event object that will be signal // when events are written to the log. arrhWaitHandles[i] = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == arrhWaitHandles[i]) { wprintf(L"CreateEvent failed with %lu.\n", GetLastError()); } // Open the log file. The source name (provider) must exist as // a subkey of Application. g_arrhEventLog[i] = OpenEventLog(NULL, event_monitor[i].provider_name); //g_arrhEventLog[i] = OpenEventLog(NULL, L"Application"); if (NULL == g_arrhEventLog[i]) { wprintf(L"OpenEventLog failed with 0x%x.\n", GetLastError()); } // Seek to the last record in the event log and read it in order // to position the cursor for reading any new records when the // service notifies you that new records have been written to the // log file. status = SeekToLastRecord(g_arrhEventLog[i]); if (ERROR_SUCCESS != status) { wprintf(L"SeekToLastRecord failed with %lu.\n", status); } // Request notification when events are written to the log. if (!NotifyChangeEventLog(g_arrhEventLog[i], arrhWaitHandles[i])) { wprintf(L"NotifyChangeEventLog failed with %lu.\n", GetLastError()); goto cleanup; } } wprintf(L"Waiting for notification of new events...\n\n"); // Loop until the user presses a key or there is an error. while (true) { if (0 == g_enable_monitor) { break; } dwWaitReason = WaitForMultipleObjects(nMonSize, arrhWaitHandles, FALSE, /*INFINITE*/10*1000); dwWaitReason -= WAIT_OBJECT_0; if (dwWaitReason >= 0 && dwWaitReason < nMonSize) { if (ERROR_SUCCESS != (status = DumpNewRecords(dwWaitReason))) { wprintf(L"DumpNewRecords failed.\n"); break; } wprintf(L"\nWaiting for notification of new events...\n"); ResetEvent(arrhWaitHandles[dwWaitReason]); } else if (dwWaitReason == WAIT_TIMEOUT) { printf("WaitForMultiple Timeout.\n"); } else { if (WAIT_FAILED == dwWaitReason) { wprintf(L"WaitForSingleObject failed with %lu\n", GetLastError()); } break; } } cleanup: for (int i=0; i<nMonSize; i++) { if (g_arrhEventLog[i]) CloseEventLog(g_arrhEventLog[i]); if (arrhWaitHandles[i]) CloseHandle(arrhWaitHandles[i]); if (event_monitor[i].hResource) FreeLibrary(event_monitor[i].hResource); } if (arrhWaitHandles) free(arrhWaitHandles); if (g_arrhEventLog) free(g_arrhEventLog); }
DWORD WINAPI eventLogMonitorThreadProc(LPVOID elm_info_param) { EVENTLOGRECORD *pevlr; BYTE bBuffer[BUFFER_SIZE] = { 0 }; DWORD dwRead, dwNeeded, res; DWORD reported_next_record, num_records; BOOL skip_first = FALSE; HANDLE log = NULL, event = NULL; WCHAR msgbuf[BUFFER_SIZE]; eventlogmon_info *elm_info = (eventlogmon_info *) elm_info_param; control = 0; log = OpenEventLog(NULL, L_COMPANY_NAME); if (log == NULL) { (*elm_info->cb_err)(ELM_ERR_FATAL, L"Could not open the " L_COMPANY_NAME L" event log."); goto exit_elm_thread_error; } event = CreateEvent(NULL, FALSE, FALSE, NULL); NotifyChangeEventLog(log, event); pevlr = (EVENTLOGRECORD *) &bBuffer; if (!GetNumberOfEventLogRecords(log, &num_records) || !GetOldestEventLogRecord(log, &reported_next_record)) { _snwprintf(msgbuf, BUFFER_SIZE_ELEMENTS(msgbuf), L"error %d getting eventlog info", GetLastError()); (*elm_info->cb_err)(ELM_ERR_FATAL, msgbuf); goto exit_elm_thread_error; } /* FIXME: we don't handle the situation when the eventlog was cleared, * but our pointer is less than the number of new records. for this * we'll probably have to store a timestamp, and compare against the * event record at next_record. */ if (((int)elm_info->next_record) < 0) { elm_info->next_record = reported_next_record; } else if (elm_info->next_record > (reported_next_record + num_records + 1)) { /* looks like the eventlog was cleared since we last checked * it. issue a warning and reset */ elm_info->next_record = reported_next_record; (*elm_info->cb_err)(ELM_ERR_CLEARED, L"Eventlog was cleared!\n"); } else { /* we need to ensure we SEEK to a valid record; but since * it's already been reported, don't report it again. */ elm_info->next_record--; skip_first = TRUE; } /* first seek to the last record * EVENTLOG_FORWARDS_READ indicates we will get messages in * chronological order. * FIXME: test to make sure that this works properly on * overwrite-wrapped logs */ if (!ReadEventLog(log, EVENTLOG_FORWARDS_READ | EVENTLOG_SEEK_READ, elm_info->next_record, pevlr, BUFFER_SIZE, &dwRead, &dwNeeded)) { dwRead = 0; dwNeeded = 0; } for(;;) { do { /* case 5813: if pevlr->Length is 0, we'll have an infinite * loop. this could possibly happen if drive is full? * just abort if we detect it. */ while (dwRead > 0 && pevlr->Length > 0) { if (format_messages && !skip_first) { res = get_formatted_message(pevlr, msgbuf, BUFFER_SIZE); if (res != ERROR_SUCCESS) { _snwprintf(msgbuf, BUFFER_SIZE_ELEMENTS(msgbuf), L"FormatMessage error %d\n", res); (*elm_info->cb_err)(ELM_ERR_WARN, msgbuf); } else { /* invoke the callback */ (*elm_info->cb_format)(pevlr->RecordNumber, pevlr->EventType, msgbuf, pevlr->TimeGenerated); } } else if(!skip_first) { /* xref case 3065: insurance */ if (pevlr->RecordNumber != 0 || pevlr->TimeGenerated != 0) { /* raw callback */ (*elm_info->cb_raw)(pevlr); } } else { skip_first = FALSE; } dwRead -= pevlr->Length; pevlr = (EVENTLOGRECORD *) ((LPBYTE) pevlr + pevlr->Length); } pevlr = (EVENTLOGRECORD *) &bBuffer; } while (ReadEventLog(log, EVENTLOG_FORWARDS_READ | EVENTLOG_SEQUENTIAL_READ, 0, pevlr, BUFFER_SIZE, &dwRead, &dwNeeded)); if((res = GetLastError()) != ERROR_HANDLE_EOF) { //FIXME: assert GetLastError() is appropriate _snwprintf(msgbuf, BUFFER_SIZE_ELEMENTS(msgbuf), L"Unexpected error %d reading event log\n", res); (*elm_info->cb_err)(ELM_ERR_WARN, msgbuf); } if (do_once) break; /* the event is auto-reset. timeout because NotifyChangeEventLog is not reliable. */ WaitForSingleObject(event, MINIPULSE); if(control) break; } exit_elm_thread_error: if (log != NULL) CloseEventLog(log); if (event != NULL) CloseHandle(event); free(elm_info); /* FIXME: need ExitThread()? */ return 0; }