using System.IO;
using System.Runtime.InteropServices;
using System.Text;
+using System.Threading;
using Microsoft.Win32;
internal class Win32EventLog : EventLogImpl
{
private const int MESSAGE_NOT_FOUND = 317;
+ private ManualResetEvent _notifyResetEvent;
+ private IntPtr _readHandle;
+ private Thread _notifyThread;
+ private int _lastEntryWritten;
+ private bool _notifying;
public Win32EventLog (EventLog coreEventLog)
: base (coreEventLog)
public override void Clear ()
{
- IntPtr hEventLog = OpenEventLog ();
- try {
- int ret = PInvoke.ClearEventLog (hEventLog, null);
- if (ret != 1) {
- throw new Win32Exception (Marshal.GetLastWin32Error ());
- }
- } finally {
- CloseEventLog (hEventLog);
- }
+ int ret = PInvoke.ClearEventLog (ReadHandle, null);
+ if (ret != 1)
+ throw new Win32Exception (Marshal.GetLastWin32Error ());
}
public override void Close ()
{
- // we don't hold any unmanaged resources
+ if (_readHandle != IntPtr.Zero) {
+ CloseEventLog (_readHandle);
+ _readHandle = IntPtr.Zero;
+ }
}
public override void CreateEventSource (EventSourceCreationData sourceData)
protected override int GetEntryCount ()
{
- IntPtr hEventLog = OpenEventLog ();
- try {
- int entryCount = 0;
- int retVal = PInvoke.GetNumberOfEventLogRecords (hEventLog, ref entryCount);
- if (retVal != 1) {
- throw new Win32Exception (Marshal.GetLastWin32Error ());
- }
- return entryCount;
- } finally {
- CloseEventLog (hEventLog);
- }
+ int entryCount = 0;
+ int retVal = PInvoke.GetNumberOfEventLogRecords (ReadHandle, ref entryCount);
+ if (retVal != 1)
+ throw new Win32Exception (Marshal.GetLastWin32Error ());
+ return entryCount;
}
protected override EventLogEntry GetEntry (int index)
index += OldestEventLogEntry;
- IntPtr hEventLog = OpenEventLog ();
- try {
- int bytesRead = 0;
- int minBufferNeeded = 0;
-
- byte [] buffer = new byte [0x7ffff]; // according to MSDN this is the max size of the buffer
- int length = buffer.Length;
-
- int ret = PInvoke.ReadEventLog (hEventLog, ReadFlags.Seek |
- ReadFlags.ForwardsRead, index, buffer, length,
- ref bytesRead, ref minBufferNeeded);
- if (ret != 1) {
- throw new Win32Exception (Marshal.GetLastWin32Error ());
- }
-
- MemoryStream ms = new MemoryStream (buffer);
- BinaryReader br = new BinaryReader (ms);
-
- // skip first 8 bytes
- br.ReadBytes (8);
-
- int recordNumber = br.ReadInt32 (); // 8
-
- int timeGeneratedSeconds = br.ReadInt32 (); // 12
- int timeWrittenSeconds = br.ReadInt32 (); // 16
- uint instanceID = br.ReadUInt32 ();
- int eventID = EventLog.GetEventID (instanceID);
- short eventType = br.ReadInt16 (); // 24
- short numStrings = br.ReadInt16 (); ; // 26
- short categoryNumber = br.ReadInt16 (); ; // 28
- // skip reservedFlags
- br.ReadInt16 (); // 30
- // skip closingRecordNumber
- br.ReadInt32 (); // 32
- int stringOffset = br.ReadInt32 (); // 36
- int userSidLength = br.ReadInt32 (); // 40
- int userSidOffset = br.ReadInt32 (); // 44
- int dataLength = br.ReadInt32 (); // 48
- int dataOffset = br.ReadInt32 (); // 52
-
- DateTime timeGenerated = new DateTime (1970, 1, 1).AddSeconds (
- timeGeneratedSeconds);
-
- DateTime timeWritten = new DateTime (1970, 1, 1).AddSeconds (
- timeWrittenSeconds);
-
- StringBuilder sb = new StringBuilder ();
- while (br.PeekChar () != '\0')
- sb.Append (br.ReadChar ());
- br.ReadChar (); // skip the null-char
-
- string sourceName = sb.ToString ();
-
- sb.Length = 0;
- while (br.PeekChar () != '\0')
- sb.Append (br.ReadChar ());
- br.ReadChar (); // skip the null-char
- string machineName = sb.ToString ();
+ int bytesRead = 0;
+ int minBufferNeeded = 0;
+ byte [] buffer = new byte [0x7ffff]; // according to MSDN this is the max size of the buffer
+
+ ReadEventLog (index, buffer, ref bytesRead, ref minBufferNeeded);
+
+ MemoryStream ms = new MemoryStream (buffer);
+ BinaryReader br = new BinaryReader (ms);
+
+ // skip first 8 bytes
+ br.ReadBytes (8);
+
+ int recordNumber = br.ReadInt32 (); // 8
+
+ int timeGeneratedSeconds = br.ReadInt32 (); // 12
+ int timeWrittenSeconds = br.ReadInt32 (); // 16
+ uint instanceID = br.ReadUInt32 ();
+ int eventID = EventLog.GetEventID (instanceID);
+ short eventType = br.ReadInt16 (); // 24
+ short numStrings = br.ReadInt16 (); ; // 26
+ short categoryNumber = br.ReadInt16 (); ; // 28
+ // skip reservedFlags
+ br.ReadInt16 (); // 30
+ // skip closingRecordNumber
+ br.ReadInt32 (); // 32
+ int stringOffset = br.ReadInt32 (); // 36
+ int userSidLength = br.ReadInt32 (); // 40
+ int userSidOffset = br.ReadInt32 (); // 44
+ int dataLength = br.ReadInt32 (); // 48
+ int dataOffset = br.ReadInt32 (); // 52
+
+ DateTime timeGenerated = new DateTime (1970, 1, 1).AddSeconds (
+ timeGeneratedSeconds);
+
+ DateTime timeWritten = new DateTime (1970, 1, 1).AddSeconds (
+ timeWrittenSeconds);
+
+ StringBuilder sb = new StringBuilder ();
+ while (br.PeekChar () != '\0')
+ sb.Append (br.ReadChar ());
+ br.ReadChar (); // skip the null-char
+
+ string sourceName = sb.ToString ();
+
+ sb.Length = 0;
+ while (br.PeekChar () != '\0')
+ sb.Append (br.ReadChar ());
+ br.ReadChar (); // skip the null-char
+ string machineName = sb.ToString ();
+
+ sb.Length = 0;
+ while (br.PeekChar () != '\0')
+ sb.Append (br.ReadChar ());
+ br.ReadChar (); // skip the null-char
+
+ string userName = null;
+ if (userSidLength != 0) {
+ // TODO: lazy init ?
+ ms.Position = userSidOffset;
+ byte [] sid = br.ReadBytes (userSidLength);
+ userName = LookupAccountSid (machineName, sid);
+ }
+ ms.Position = stringOffset;
+ string [] replacementStrings = new string [numStrings];
+ for (int i = 0; i < numStrings; i++) {
sb.Length = 0;
while (br.PeekChar () != '\0')
sb.Append (br.ReadChar ());
br.ReadChar (); // skip the null-char
+ replacementStrings [i] = sb.ToString ();
+ }
- string userName = null;
- if (userSidLength != 0) {
- // TODO: lazy init ?
- ms.Position = userSidOffset;
- byte [] sid = br.ReadBytes (userSidLength);
- userName = LookupAccountSid (machineName, sid);
- }
-
- ms.Position = stringOffset;
- string [] replacementStrings = new string [numStrings];
- for (int i = 0; i < numStrings; i++) {
- sb.Length = 0;
- while (br.PeekChar () != '\0')
- sb.Append (br.ReadChar ());
- br.ReadChar (); // skip the null-char
- replacementStrings [i] = sb.ToString ();
- }
-
- byte [] data = new byte [dataLength];
- ms.Position = dataOffset;
- br.Read (data, 0, dataLength);
+ byte [] data = new byte [dataLength];
+ ms.Position = dataOffset;
+ br.Read (data, 0, dataLength);
- // TODO: lazy fetch ??
- string message = this.FormatMessage (sourceName, instanceID, replacementStrings);
- string category = FormatCategory (sourceName, categoryNumber);
+ // TODO: lazy fetch ??
+ string message = this.FormatMessage (sourceName, instanceID, replacementStrings);
+ string category = FormatCategory (sourceName, categoryNumber);
- return new EventLogEntry (category, (short) categoryNumber, recordNumber,
- eventID, sourceName, message, userName, machineName,
- (EventLogEntryType) eventType, timeGenerated, timeWritten,
- data, replacementStrings, instanceID);
- } finally {
- CloseEventLog (hEventLog);
- }
+ return new EventLogEntry (category, (short) categoryNumber, recordNumber,
+ eventID, sourceName, message, userName, machineName,
+ (EventLogEntryType) eventType, timeGenerated, timeWritten,
+ data, replacementStrings, instanceID);
}
[MonoTODO]
Environment.SpecialFolder.System), "config");
logKey.SetValue ("File", Path.Combine (configPath, file));
}
-
}
private static void UpdateSourceRegistry (RegistryKey sourceKey, EventSourceCreationData data)
return logName.Substring (logName.LastIndexOf ("\\") + 1);
}
+ private void ReadEventLog (int index, byte [] buffer, ref int bytesRead, ref int minBufferNeeded)
+ {
+ const int max_retries = 3;
+
+ // if the eventlog file changed since the handle was
+ // obtained, then we need to re-try multiple times
+ for (int i = 0; i < max_retries; i++) {
+ int ret = PInvoke.ReadEventLog (ReadHandle,
+ ReadFlags.Seek | ReadFlags.ForwardsRead,
+ index, buffer, buffer.Length, ref bytesRead,
+ ref minBufferNeeded);
+ if (ret != 1) {
+ int error = Marshal.GetLastWin32Error ();
+ if (i < (max_retries - 1)) {
+ CoreEventLog.Reset ();
+ } else {
+ throw new Win32Exception (error);
+ }
+ }
+ }
+ }
+
+
[MonoTODO ("Support remote machines")]
private static RegistryKey GetEventLogKey (string machineName, bool writable)
{
private int OldestEventLogEntry {
get {
- IntPtr hEventLog = OpenEventLog ();
- try {
- int oldestEventLogEntry = 0;
- int ret = PInvoke.GetOldestEventLogRecord (hEventLog, ref oldestEventLogEntry);
- if (ret != 1) {
- throw new Win32Exception (Marshal.GetLastWin32Error ());
- }
- return oldestEventLogEntry;
- } finally {
- CloseEventLog (hEventLog);
+ int oldestEventLogEntry = 0;
+ int ret = PInvoke.GetOldestEventLogRecord (ReadHandle, ref oldestEventLogEntry);
+ if (ret != 1) {
+ throw new Win32Exception (Marshal.GetLastWin32Error ());
}
+ return oldestEventLogEntry;
}
}
return new string [0];
}
- private IntPtr OpenEventLog ()
- {
- string logName = CoreEventLog.GetLogName ();
- IntPtr hEventLog = PInvoke.OpenEventLog (CoreEventLog.MachineName,
- logName);
- if (hEventLog == IntPtr.Zero) {
- throw new InvalidOperationException (string.Format (
- CultureInfo.InvariantCulture, "Event Log '{0}' on computer"
- + " '{1}' cannot be opened.", logName, CoreEventLog.MachineName),
- new Win32Exception ());
+ private IntPtr ReadHandle {
+ get {
+ if (_readHandle != IntPtr.Zero)
+ return _readHandle;
+
+ string logName = CoreEventLog.GetLogName ();
+ _readHandle = PInvoke.OpenEventLog (CoreEventLog.MachineName,
+ logName);
+ if (_readHandle == IntPtr.Zero)
+ throw new InvalidOperationException (string.Format (
+ CultureInfo.InvariantCulture, "Event Log '{0}' on computer"
+ + " '{1}' cannot be opened.", logName, CoreEventLog.MachineName),
+ new Win32Exception ());
+ return _readHandle;
}
- return hEventLog;
}
private IntPtr RegisterEventSource ()
public override void DisableNotification ()
{
- // FIXME: implement
+ if (_notifyResetEvent != null) {
+ _notifyResetEvent.Close ();
+ _notifyResetEvent = null;
+ }
+
+ if (_notifyThread != null) {
+ if (_notifyThread.ThreadState == System.Threading.ThreadState.Running)
+ _notifyThread.Abort ();
+ _notifyThread = null;
+ }
}
public override void EnableNotification ()
{
- // FIXME: implement
+ _notifyResetEvent = new ManualResetEvent (false);
+ _lastEntryWritten = OldestEventLogEntry + EntryCount;
+ if (PInvoke.NotifyChangeEventLog (ReadHandle, _notifyResetEvent.Handle) == 0)
+ throw new InvalidOperationException (string.Format (
+ CultureInfo.InvariantCulture, "Unable to receive notifications"
+ + " for log '{0}' on computer '{1}'.", CoreEventLog.GetLogName (),
+ CoreEventLog.MachineName), new Win32Exception ());
+ _notifyThread = new Thread (new ThreadStart (NotifyEventThread));
+ _notifyThread.IsBackground = true;
+ _notifyThread.Start ();
+ }
+
+ private void NotifyEventThread ()
+ {
+ while (true) {
+ _notifyResetEvent.WaitOne ();
+ lock (this) {
+ // after a clear, we something get notified
+ // twice for the same entry
+ if (_notifying)
+ return;
+ _notifying = true;
+ }
+
+ try {
+ int oldest_entry = OldestEventLogEntry;
+ if (_lastEntryWritten < oldest_entry)
+ _lastEntryWritten = oldest_entry;
+ int current_entry = _lastEntryWritten - oldest_entry;
+ int last_entry = EntryCount + oldest_entry;
+ for (int i = current_entry; i < (last_entry - 1); i++) {
+ EventLogEntry entry = GetEntry (i);
+ CoreEventLog.OnEntryWritten (entry);
+ }
+ _lastEntryWritten = last_entry;
+ } finally {
+ lock (this)
+ _notifying = false;
+ }
+ }
+ }
+
+ public override OverflowAction OverflowAction {
+ get { throw new NotImplementedException (); }
+ }
+
+ public override int MinimumRetentionDays {
+ get { throw new NotImplementedException (); }
+ }
+
+ public override long MaximumKilobytes {
+ get { throw new NotImplementedException (); }
+ set { throw new NotImplementedException (); }
+ }
+
+ public override void ModifyOverflowPolicy (OverflowAction action, int retentionDays)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override void RegisterDisplayName (string resourceFile, long resourceId)
+ {
+ throw new NotImplementedException ();
}
private class PInvoke
[DllImport ("advapi32.dll", SetLastError=true)]
public static extern int DeregisterEventSource (IntPtr hEventLog);
- [DllImport ("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
+ [DllImport ("kernel32", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int FormatMessage (FormatMessageFlags dwFlags, IntPtr lpSource, uint dwMessageId, int dwLanguageId, ref IntPtr lpBuffer, int nSize, IntPtr [] arguments);
- [DllImport ("kernel32.dll", SetLastError=true)]
+ [DllImport ("kernel32", SetLastError=true)]
public static extern bool FreeLibrary (IntPtr hModule);
[DllImport ("advapi32.dll", SetLastError=true)]
[DllImport ("advapi32.dll", SetLastError=true)]
public static extern int GetOldestEventLogRecord (IntPtr hEventLog, ref int OldestRecord);
- [DllImport ("kernel32.dll", SetLastError=true)]
+ [DllImport ("kernel32", SetLastError=true)]
public static extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hFile, LoadFlags dwFlags);
- [DllImport ("kernel32.dll", SetLastError=true)]
+ [DllImport ("kernel32", SetLastError=true)]
public static extern IntPtr LocalFree (IntPtr hMem);
[DllImport ("advapi32.dll", SetLastError=true)]
ref uint cchReferencedDomainName,
out SidNameUse peUse);
+ [DllImport ("advapi32.dll", SetLastError = true)]
+ public static extern int NotifyChangeEventLog (IntPtr hEventLog, IntPtr hEvent);
+
[DllImport ("advapi32.dll", SetLastError=true)]
public static extern IntPtr OpenEventLog (string machineName, string logName);
[DllImport ("advapi32.dll", SetLastError=true)]
public static extern IntPtr RegisterEventSource (string machineName, string sourceName);
- [DllImport ("Advapi32.dll", SetLastError=true)]
+ [DllImport ("advapi32.dll", SetLastError=true)]
public static extern int ReportEvent (IntPtr hHandle, ushort wType,
ushort wCategory, uint dwEventID, IntPtr sid, ushort wNumStrings,
uint dwDataSize, string [] lpStrings, byte [] lpRawData);
- [DllImport ("advapi32.dll", SetLastError = true)]
+ [DllImport ("advapi32.dll", SetLastError=true)]
public static extern int ReadEventLog (IntPtr hEventLog, ReadFlags dwReadFlags, int dwRecordOffset, byte [] buffer, int nNumberOfBytesToRead, ref int pnBytesRead, ref int pnMinNumberOfBytesNeeded);
public const int ERROR_INSUFFICIENT_BUFFER = 122;
+ public const int ERROR_EVENTLOG_FILE_CHANGED = 1503;
}
private enum ReadFlags