| File: System\Diagnostics\Reader\EventLogReader.cs | Web Access |
| Project: src\runtime\src\libraries\System.Diagnostics.EventLog\src\System.Diagnostics.EventLog.csproj (System.Diagnostics.EventLog) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.IO; using Microsoft.Win32; namespace System.Diagnostics.Eventing.Reader { /// <summary> /// This public class is used for reading event records from event log. /// </summary> public class EventLogReader : IDisposable { private readonly EventLogQuery _eventQuery; private int _batchSize; // // access to the data member reference is safe, while // invoking methods on it is marked SecurityCritical as appropriate. // private readonly EventLogHandle _handle; /// <summary> /// events buffer holds batched event (handles). /// </summary> private IntPtr[] _eventsBuffer; /// <summary> /// The current index where the function GetNextEvent is (inside the eventsBuffer). /// </summary> private int _currentIndex; /// <summary> /// The number of events read from the batch into the eventsBuffer /// </summary> private int _eventCount; /// <summary> /// When the reader finishes (will always return only ERROR_NO_MORE_ITEMS). /// For subscription, this means we need to wait for next event. /// </summary> private bool _isEof; /// <summary> /// Maintains cached display / metadata information returned from /// EventRecords that were obtained from this reader. /// </summary> private readonly ProviderMetadataCachedInformation _cachedMetadataInformation; public EventLogReader(string path) : this(new EventLogQuery(path, PathType.LogName), null) { } public EventLogReader(string path, PathType pathType) : this(new EventLogQuery(path, pathType), null) { } public EventLogReader(EventLogQuery eventQuery) : this(eventQuery, null) { } public EventLogReader(EventLogQuery eventQuery, EventBookmark? bookmark) { ArgumentNullException.ThrowIfNull(eventQuery); string? logfile = null; if (eventQuery.ThePathType == PathType.FilePath) logfile = eventQuery.Path; _cachedMetadataInformation = new ProviderMetadataCachedInformation(eventQuery.Session, logfile, 50); // Explicit data _eventQuery = eventQuery; // Implicit _batchSize = 64; _eventsBuffer = new IntPtr[_batchSize]; // // compute the flag. // int flag = 0; if (_eventQuery.ThePathType == PathType.LogName) flag |= (int)UnsafeNativeMethods.EvtQueryFlags.EvtQueryChannelPath; else flag |= (int)UnsafeNativeMethods.EvtQueryFlags.EvtQueryFilePath; if (_eventQuery.ReverseDirection) flag |= (int)UnsafeNativeMethods.EvtQueryFlags.EvtQueryReverseDirection; if (_eventQuery.TolerateQueryErrors) flag |= (int)UnsafeNativeMethods.EvtQueryFlags.EvtQueryTolerateQueryErrors; _handle = NativeWrapper.EvtQuery(_eventQuery.Session.Handle, _eventQuery.Path, _eventQuery.Query, flag); EventLogHandle bookmarkHandle = EventLogRecord.GetBookmarkHandleFromBookmark(bookmark); if (!bookmarkHandle.IsInvalid) { using (bookmarkHandle) { NativeWrapper.EvtSeek(_handle, 1, bookmarkHandle, 0, UnsafeNativeMethods.EvtSeekFlags.EvtSeekRelativeToBookmark); } } } public int BatchSize { get { return _batchSize; } set { if (value < 1) throw new ArgumentOutOfRangeException(nameof(value)); _batchSize = value; } } private bool GetNextBatch(TimeSpan ts) { int timeout = -1; if (ts != TimeSpan.MaxValue) timeout = (int)ts.TotalMilliseconds; // batchSize was changed by user, reallocate buffer. if (_batchSize != _eventsBuffer.Length) _eventsBuffer = new IntPtr[_batchSize]; int newEventCount = 0; bool results = NativeWrapper.EvtNext(_handle, _batchSize, _eventsBuffer, timeout, 0, ref newEventCount); if (!results) { _eventCount = 0; _currentIndex = 0; return false; // No more events in the result set } _currentIndex = 0; _eventCount = newEventCount; return true; } public EventRecord? ReadEvent() { return ReadEvent(TimeSpan.MaxValue); } public EventRecord? ReadEvent(TimeSpan timeout) { if (_isEof) throw new InvalidOperationException(); if (_currentIndex >= _eventCount) { // buffer is empty, get next batch. GetNextBatch(timeout); if (_currentIndex >= _eventCount) { _isEof = true; return null; } } EventLogRecord eventInstance = new EventLogRecord(new EventLogHandle(_eventsBuffer[_currentIndex], true), _eventQuery.Session, _cachedMetadataInformation); _currentIndex++; return eventInstance; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { while (_currentIndex < _eventCount) { NativeWrapper.EvtClose(_eventsBuffer[_currentIndex]); _currentIndex++; } if (_handle != null && !_handle.IsInvalid) _handle.Dispose(); } internal void SeekReset() { // // Close all unread event handles in the buffer // while (_currentIndex < _eventCount) { NativeWrapper.EvtClose(_eventsBuffer[_currentIndex]); _currentIndex++; } // Reset the indexes used by Next _currentIndex = 0; _eventCount = 0; _isEof = false; } internal void SeekCommon(long offset) { // // modify offset that we're going to send to service to account for the // fact that we've already read some events in our buffer that the user // hasn't seen yet. // offset -= (_eventCount - _currentIndex); SeekReset(); NativeWrapper.EvtSeek(_handle, offset, EventLogHandle.Zero, 0, UnsafeNativeMethods.EvtSeekFlags.EvtSeekRelativeToCurrent); } public void Seek(EventBookmark bookmark) { Seek(bookmark, 0); } public void Seek(EventBookmark bookmark, long offset) { ArgumentNullException.ThrowIfNull(bookmark); SeekReset(); using (EventLogHandle bookmarkHandle = EventLogRecord.GetBookmarkHandleFromBookmark(bookmark)) { NativeWrapper.EvtSeek(_handle, offset, bookmarkHandle, 0, UnsafeNativeMethods.EvtSeekFlags.EvtSeekRelativeToBookmark); } } public void Seek(SeekOrigin origin, long offset) { switch (origin) { case SeekOrigin.Begin: SeekReset(); NativeWrapper.EvtSeek(_handle, offset, EventLogHandle.Zero, 0, UnsafeNativeMethods.EvtSeekFlags.EvtSeekRelativeToFirst); return; case SeekOrigin.End: SeekReset(); NativeWrapper.EvtSeek(_handle, offset, EventLogHandle.Zero, 0, UnsafeNativeMethods.EvtSeekFlags.EvtSeekRelativeToLast); return; case SeekOrigin.Current: if (offset >= 0) { // We can reuse elements in the batch. if (_currentIndex + offset < _eventCount) { // // We don't call Seek here, we can reposition within the batch. // // Close all event handles between [currentIndex, currentIndex + offset) int index = _currentIndex; while (index < _currentIndex + offset) { NativeWrapper.EvtClose(_eventsBuffer[index]); index++; } _currentIndex = (int)(_currentIndex + offset); // Leave the eventCount unchanged // Leave the same Eof } else { SeekCommon(offset); } } else { SeekCommon(offset); } return; } } public void CancelReading() { NativeWrapper.EvtCancel(_handle); } public IList<EventLogStatus> LogStatus { get { EventLogHandle queryHandle = _handle; if (queryHandle.IsInvalid) throw new InvalidOperationException(); string?[] channelNames = (string?[])NativeWrapper.EvtGetQueryInfo(queryHandle, UnsafeNativeMethods.EvtQueryPropertyId.EvtQueryNames)!; int[] errorStatuses = (int[])NativeWrapper.EvtGetQueryInfo(queryHandle, UnsafeNativeMethods.EvtQueryPropertyId.EvtQueryStatuses)!; if (channelNames.Length != errorStatuses.Length) throw new InvalidOperationException(); var list = new List<EventLogStatus>(channelNames.Length); for (int i = 0; i < channelNames.Length; i++) { EventLogStatus cs = new EventLogStatus(channelNames[i], errorStatuses[i]); list.Add(cs); } return list.AsReadOnly(); } } } }