1 //------------------------------------------------------------------------------
2 // <copyright file="EventLogInternal.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 //#define RETRY_ON_ALL_ERRORS
10 * EventLogInternal contains most of the logic for interacting with the Windows Event Log.
11 * The reason for this class existing (instead of the logic being in EventLog itself) is
12 * that we'd like to be able to have the invariant that the Source, MachineName and Log Name
13 * don't change across the lifetime of an event log object, but we exposed public setters for
14 * these properties. EventLog holds a reference to an EventLogInternal instance, plumbs all
15 * calls to it and replaces it when any of these properites change.
17 * Note that EventLogInternal also holds a reference back to the EventLog instnace that is
18 * exposing it so it is not prematurely collected.
21 namespace System.Diagnostics {
23 using System.Text.RegularExpressions;
24 using System.Threading;
25 using System.Runtime.InteropServices;
26 using System.ComponentModel;
27 using System.Diagnostics;
29 using Microsoft.Win32;
30 using Microsoft.Win32.SafeHandles;
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Globalization;
35 using System.ComponentModel.Design;
36 using System.Security;
37 using System.Security.Permissions;
38 using System.Reflection;
39 using System.Runtime.Versioning;
40 using System.Runtime.CompilerServices;
41 using System.Diagnostics.CodeAnalysis;
45 /// Provides interaction with Windows 2000 event logs.
48 internal class EventLogInternal : IDisposable, ISupportInitialize {
49 // a collection over all our entries. Since the class holds no state, we
50 // can just hand the same instance out every time.
51 private EventLogEntryCollection entriesCollection;
52 // the name of the log we're reading from or writing to
53 internal string logName;
54 // used in monitoring for event postings.
55 private int lastSeenCount;
56 // holds the machine we're on, or null if it's the local machine
57 internal readonly string machineName;
59 // the delegate to call when an event arrives
60 internal EntryWrittenEventHandler onEntryWrittenHandler;
61 // holds onto the handle for reading
62 private SafeEventLogReadHandle readHandle;
63 // the source name - used only when writing
64 internal readonly string sourceName;
65 // holds onto the handle for writing
66 private SafeEventLogWriteHandle writeHandle;
68 private string logDisplayName;
70 // cache system state variables
71 // the initial size of the buffer (it can be made larger if necessary)
72 private const int BUF_SIZE = 40000;
73 // the number of bytes in the cache that belong to entries (not necessarily
74 // the same as BUF_SIZE, because the cache only holds whole entries)
75 private int bytesCached;
76 // the actual cache buffer
78 // the number of the entry at the beginning of the cache
79 private int firstCachedEntry = -1;
80 // the number of the entry that we got out of the cache most recently
81 private int lastSeenEntry;
82 // where that entry was
83 private int lastSeenPos;
84 //support for threadpool based deferred execution
85 private ISynchronizeInvoke synchronizingObject;
86 // the EventLog object that publicly exposes this instance.
87 private readonly EventLog parent;
89 private const string EventLogKey = "SYSTEM\\CurrentControlSet\\Services\\EventLog";
90 internal const string DllName = "EventLogMessages.dll";
91 private const string eventLogMutexName = "netfxeventlog.1.0";
92 private const int SecondsPerDay = 60 * 60 * 24;
93 private const int DefaultMaxSize = 512*1024;
94 private const int DefaultRetention = 7*SecondsPerDay;
96 private const int Flag_notifying = 0x1; // keeps track of whether we're notifying our listeners - to prevent double notifications
97 private const int Flag_forwards = 0x2; // whether the cache contains entries in forwards order (true) or backwards (false)
98 private const int Flag_initializing = 0x4;
99 internal const int Flag_monitoring = 0x8;
100 private const int Flag_registeredAsListener = 0x10;
101 private const int Flag_writeGranted = 0x20;
102 private const int Flag_disposed = 0x100;
103 private const int Flag_sourceVerified= 0x200;
105 private BitVector32 boolFlags = new BitVector32();
107 private Hashtable messageLibraries;
108 private readonly static Hashtable listenerInfos = new Hashtable(StringComparer.OrdinalIgnoreCase);
110 private Object m_InstanceLockObject;
111 private Object InstanceLockObject {
113 if (m_InstanceLockObject == null) {
114 Object o = new Object();
115 Interlocked.CompareExchange(ref m_InstanceLockObject, o, null);
117 return m_InstanceLockObject;
122 private static Object s_InternalSyncObject;
123 private static Object InternalSyncObject {
125 if (s_InternalSyncObject == null) {
126 Object o = new Object();
127 Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
129 return s_InternalSyncObject;
135 /// Initializes a new instance of the <see cref='System.Diagnostics.EventLog'/>
139 public EventLogInternal() : this("", ".", "", null) {
143 /// <para>[To be supplied.]</para>
145 public EventLogInternal(string logName) : this(logName, ".", "", null) {
149 /// <para>[To be supplied.]</para>
151 public EventLogInternal(string logName, string machineName) : this(logName, machineName, "", null) {
155 /// <para>[To be supplied.]</para>
157 public EventLogInternal(string logName, string machineName, string source) : this(logName, machineName, source, null) {
161 /// <para>[To be supplied.]</para>
163 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, oldLog.machineName doesn't change")]
164 public EventLogInternal(string logName, string machineName, string source, EventLog parent) {
165 //look out for invalid log names
167 throw new ArgumentNullException("logName");
168 if (!ValidLogName(logName, true))
169 throw new ArgumentException(SR.GetString(SR.BadLogName));
171 if (!SyntaxCheck.CheckMachineName(machineName))
172 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
174 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
177 this.machineName = machineName;
179 this.logName = logName;
180 this.sourceName = source;
183 boolFlags[Flag_forwards] = true;
184 this.parent = parent;
189 /// Gets the contents of the event log.
192 public EventLogEntryCollection Entries {
194 string currentMachineName = this.machineName;
196 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
199 if (entriesCollection == null)
200 entriesCollection = new EventLogEntryCollection(this);
201 return entriesCollection;
206 /// Gets the number of entries in the log
208 internal int EntryCount {
211 OpenForRead(this.machineName);
213 bool success = UnsafeNativeMethods.GetNumberOfEventLogRecords(readHandle, out count);
215 throw SharedUtils.CreateSafeWin32Exception();
221 /// Determines whether the event log is open in either read or write access
223 private bool IsOpen {
225 return readHandle != null || writeHandle != null;
230 /// Determines whether the event log is open with read access
232 private bool IsOpenForRead {
234 return readHandle != null;
239 /// Determines whether the event log is open with write access.
241 private bool IsOpenForWrite {
243 return writeHandle != null;
251 public string LogDisplayName {
252 [ResourceExposure(ResourceScope.Machine)]
253 [ResourceConsumption(ResourceScope.Machine)]
256 if (logDisplayName != null)
257 return logDisplayName;
259 string currentMachineName = this.machineName;
260 if (GetLogName(currentMachineName) != null) {
262 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
265 //Check environment before looking at the registry
266 SharedUtils.CheckEnvironment();
268 //SECREVIEW: Note that EventLogPermission is just demmanded above
269 PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
270 permissionSet.Assert();
272 RegistryKey logkey = null;
275 // we figure out what logs are on the machine by looking in the registry.
276 logkey = GetLogRegKey(currentMachineName, false);
278 throw new InvalidOperationException(SR.GetString(SR.MissingLog, GetLogName(currentMachineName), currentMachineName));
280 string resourceDll = (string)logkey.GetValue("DisplayNameFile");
281 if (resourceDll == null)
282 logDisplayName = GetLogName(currentMachineName);
284 int resourceId = (int)logkey.GetValue("DisplayNameID");
285 logDisplayName = FormatMessageWrapper(resourceDll, (uint) resourceId, null);
286 if (logDisplayName == null)
287 logDisplayName = GetLogName(currentMachineName);
291 if (logkey != null) logkey.Close();
293 // Revert registry and environment permission asserts
294 CodeAccessPermission.RevertAssert();
298 return logDisplayName;
304 /// Gets the name of the log to read from and write to.
309 string currentMachineName = this.machineName;
310 if (logName == null || logName.Length == 0) {
311 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
315 return GetLogName(currentMachineName);
319 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
320 private string GetLogName(string currentMachineName)
322 if ((logName == null || logName.Length == 0) && sourceName != null && sourceName.Length!=0) {
323 // they've told us a source, but they haven't told us a log name.
324 // try to deduce the log name from the source name.
325 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
328 logName = EventLog._InternalLogNameFromSourceName(sourceName, currentMachineName);
335 /// Gets the name of the computer on which to read or write events.
338 public string MachineName {
340 string currentMachineName = this.machineName;
342 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
345 return currentMachineName;
350 [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "[....]: MaximumKilobytes is the name of this property.")]
351 public long MaximumKilobytes {
353 string currentMachineName = this.machineName;
355 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
358 object val = GetLogRegValue(currentMachineName, "MaxSize");
360 int intval = (int) val; // cast to an int first to unbox
361 return ((uint)intval) / 1024; // then convert to kilobytes
364 // 512k is the default value
368 [ResourceExposure(ResourceScope.None)]
369 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
371 string currentMachineName = this.machineName;
373 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
376 // valid range is 64 KB to 4 GB
377 if (value < 64 || value > 0x3FFFC0 || value % 64 != 0)
378 throw new ArgumentOutOfRangeException("MaximumKilobytes", SR.GetString(SR.MaximumKilobytesOutOfRange));
380 PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
381 permissionSet.Assert();
383 long regvalue = value * 1024; // convert to bytes
384 int i = unchecked((int)regvalue);
386 using (RegistryKey logkey = GetLogRegKey(currentMachineName, true))
387 logkey.SetValue("MaxSize", i, RegistryValueKind.DWord);
391 internal Hashtable MessageLibraries {
393 if (messageLibraries == null)
394 messageLibraries = new Hashtable(StringComparer.OrdinalIgnoreCase);
395 return messageLibraries;
400 public OverflowAction OverflowAction {
402 string currentMachineName = this.machineName;
404 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
407 object retentionobj = GetLogRegValue(currentMachineName, "Retention");
408 if (retentionobj != null) {
409 int retention = (int) retentionobj;
411 return OverflowAction.OverwriteAsNeeded;
412 else if (retention == -1)
413 return OverflowAction.DoNotOverwrite;
415 return OverflowAction.OverwriteOlder;
418 // default value as listed in MSDN
419 return OverflowAction.OverwriteOlder;
424 public int MinimumRetentionDays {
426 string currentMachineName = this.machineName;
428 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
431 object retentionobj = GetLogRegValue(currentMachineName, "Retention");
432 if (retentionobj != null) {
433 int retention = (int) retentionobj;
434 if (retention == 0 || retention == -1)
437 return (int) (((double) retention) / SecondsPerDay);
445 public bool EnableRaisingEvents {
447 string currentMachineName = this.machineName;
449 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
452 return boolFlags[Flag_monitoring];
455 string currentMachineName = this.machineName;
457 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
460 if (parent.ComponentDesignMode)
461 this.boolFlags[Flag_monitoring] = value;
464 StartRaisingEvents(currentMachineName, GetLogName(currentMachineName));
466 StopRaisingEvents(/*currentMachineName,*/ GetLogName(currentMachineName));
471 private int OldestEntryNumber {
474 OpenForRead(this.machineName);
476 bool success = UnsafeNativeMethods.GetOldestEventLogRecord(readHandle, out num);
478 throw SharedUtils.CreateSafeWin32Exception();
480 // When the event log is empty, GetOldestEventLogRecord returns 0.
481 // But then after an entry is written, it returns 1. We need to go from
482 // the last num to the current.
490 internal SafeEventLogReadHandle ReadHandle {
493 OpenForRead(this.machineName);
500 /// Represents the object used to marshal the event handler
501 /// calls issued as a result of an <see cref='System.Diagnostics.EventLog'/>
505 public ISynchronizeInvoke SynchronizingObject {
506 [HostProtection(Synchronization=true)]
508 string currentMachineName = this.machineName;
510 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
512 if (this.synchronizingObject == null && parent.ComponentDesignMode) {
513 IDesignerHost host = (IDesignerHost) parent.ComponentGetService(typeof(IDesignerHost));
515 object baseComponent = host.RootComponent;
516 if (baseComponent != null && baseComponent is ISynchronizeInvoke)
517 this.synchronizingObject = (ISynchronizeInvoke)baseComponent;
521 return this.synchronizingObject;
525 this.synchronizingObject = value;
532 /// sets the application name (source name) to register and use when writing to the event log.
535 public string Source {
537 string currentMachineName = this.machineName;
539 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
547 [HostProtection(Synchronization=true)]
548 [ResourceExposure(ResourceScope.None)]
549 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
550 private static void AddListenerComponent(EventLogInternal component, string compMachineName, string compLogName) {
551 lock (InternalSyncObject) {
552 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::AddListenerComponent(" + compLogName + ")");
554 LogListeningInfo info = (LogListeningInfo) listenerInfos[compLogName];
556 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::AddListenerComponent: listener already active.");
557 info.listeningComponents.Add(component);
561 info = new LogListeningInfo();
562 info.listeningComponents.Add(component);
564 info.handleOwner = new EventLogInternal(compLogName, compMachineName);
566 // tell the event log system about it
567 info.waitHandle = new AutoResetEvent(false);
568 bool success = UnsafeNativeMethods.NotifyChangeEventLog(info.handleOwner.ReadHandle, info.waitHandle.SafeWaitHandle);
570 throw new InvalidOperationException(SR.GetString(SR.CantMonitorEventLog), SharedUtils.CreateSafeWin32Exception());
572 info.registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(info.waitHandle, new WaitOrTimerCallback(StaticCompletionCallback), info, -1, false);
574 listenerInfos[compLogName] = info;
580 /// Occurs when an entry is written to the event log.
583 public event EntryWrittenEventHandler EntryWritten {
585 string currentMachineName = this.machineName;
587 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
590 onEntryWrittenHandler += value;
593 string currentMachineName = this.machineName;
595 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
598 onEntryWrittenHandler -= value;
604 public void BeginInit() {
605 string currentMachineName = this.machineName;
607 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
610 if (boolFlags[Flag_initializing]) throw new InvalidOperationException(SR.GetString(SR.InitTwice));
611 boolFlags[Flag_initializing] = true;
612 if (boolFlags[Flag_monitoring])
613 StopListening(GetLogName(currentMachineName));
619 /// the event log by removing all entries from it.
622 [ResourceExposure(ResourceScope.Machine)] // Should anyone ever call this, other than an event log viewer?
623 [ResourceConsumption(ResourceScope.Machine)]
624 public void Clear() {
625 string currentMachineName = this.machineName;
627 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
631 OpenForRead(currentMachineName);
632 bool success = UnsafeNativeMethods.ClearEventLog(readHandle, NativeMethods.NullHandleRef);
634 // Ignore file not found errors. ClearEventLog seems to try to delete the file where the event log is
635 // stored. If it can't find it, it gives an error.
636 int error = Marshal.GetLastWin32Error();
637 if (error != NativeMethods.ERROR_FILE_NOT_FOUND)
638 throw SharedUtils.CreateSafeWin32Exception();
641 // now that we've cleared the event log, we need to re-open our handles, because
642 // the internal state of the event log has changed.
643 Reset(currentMachineName);
648 /// Closes the event log and releases read and write handles.
651 [ResourceExposure(ResourceScope.None)]
652 public void Close() {
653 Close(this.machineName);
656 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, currentMachineName doesn't change")]
657 private void Close(string currentMachineName) {
658 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
661 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close");
662 //Trace("Close", "Closing the event log");
663 if (readHandle != null) {
667 catch (IOException) {
668 throw SharedUtils.CreateSafeWin32Exception();
671 //Trace("Close", "Closed read handle");
672 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close: closed read handle");
674 if (writeHandle != null) {
678 catch (IOException) {
679 throw SharedUtils.CreateSafeWin32Exception();
682 //Trace("Close", "Closed write handle");
683 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close: closed write handle");
685 if (boolFlags[Flag_monitoring])
686 StopRaisingEvents(/*currentMachineName,*/ GetLogName(currentMachineName));
688 if (messageLibraries != null) {
689 foreach (SafeLibraryHandle handle in messageLibraries.Values)
692 messageLibraries = null;
695 boolFlags[Flag_sourceVerified] = false;
701 /// Called when the threadpool is ready for us to handle a status change.
703 private void CompletionCallback(object context) {
705 if (boolFlags[Flag_disposed]) {
706 // This object has been disposed previously, ignore firing the event.
710 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: starting at " + lastSeenCount.ToString(CultureInfo.InvariantCulture));
711 lock (InstanceLockObject) {
712 if (boolFlags[Flag_notifying]) {
713 // don't do double notifications.
714 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: aborting because we're already notifying.");
717 boolFlags[Flag_notifying] = true;
720 int i = lastSeenCount;
723 int oldest = OldestEntryNumber;
724 int count = EntryCount + oldest;
726 // Ensure lastSeenCount is within bounds. This deals with the case where the event log has been cleared between
728 if (lastSeenCount < oldest || lastSeenCount > count) {
729 lastSeenCount = oldest;
733 // NOTE, [....]: We have a double loop here so that we access the
734 // EntryCount property as infrequently as possible. (It may be expensive
735 // to get the property.) Even though there are two loops, they will together
736 // only execute as many times as (final value of EntryCount) - lastSeenCount.
737 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: OldestEntryNumber is " + OldestEntryNumber + ", EntryCount is " + EntryCount);
740 EventLogEntry entry = GetEntryWithOldest(i);
741 if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
742 this.SynchronizingObject.BeginInvoke(this.onEntryWrittenHandler, new object[]{this, new EntryWrittenEventArgs(entry)});
744 onEntryWrittenHandler(this, new EntryWrittenEventArgs(entry));
748 oldest = OldestEntryNumber;
749 count = EntryCount + oldest;
752 catch (Exception e) {
753 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: Caught exception notifying event handlers: " + e.ToString());
757 // if the user cleared the log while we were receiving events, the call to GetEntryWithOldest above could have
758 // thrown an exception and i could be too large. Make sure we don't set lastSeenCount to something bogus.
759 int newCount = EntryCount + OldestEntryNumber;
761 lastSeenCount = newCount;
764 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: finishing at " + lastSeenCount.ToString(CultureInfo.InvariantCulture));
766 catch (Win32Exception e) {
767 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: Caught exception updating last entry number: " + e.ToString());
770 lock (InstanceLockObject) {
771 boolFlags[Flag_notifying] = false;
775 public void Dispose() {
777 GC.SuppressFinalize(this);
782 internal void Dispose(bool disposing) {
785 //Dispose unmanaged and managed resources
790 // This is probably unnecessary
791 if (readHandle != null) {
796 if (writeHandle != null) {
803 messageLibraries = null;
804 this.boolFlags[Flag_disposed] = true;
810 public void EndInit() {
811 string currentMachineName = this.machineName;
813 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
816 boolFlags[Flag_initializing] = false;
817 if (boolFlags[Flag_monitoring])
818 StartListening(currentMachineName, GetLogName(currentMachineName));
821 [ResourceExposure(ResourceScope.None)]
822 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
823 internal string FormatMessageWrapper(string dllNameList, uint messageNum, string[] insertionStrings) {
824 if (dllNameList == null)
827 if (insertionStrings == null)
828 insertionStrings = new string[0];
830 string[] listDll = dllNameList.Split(';');
832 // Find first mesage in DLL list
833 foreach ( string dllName in listDll) {
834 if (dllName == null || dllName.Length == 0)
837 SafeLibraryHandle hModule = null;
839 // if the EventLog is open, then we want to cache the library in our hashtable. Otherwise
840 // we'll just load it and free it after we're done.
842 hModule = MessageLibraries[dllName] as SafeLibraryHandle;
844 if (hModule == null || hModule.IsInvalid) {
845 hModule = SafeLibraryHandle.LoadLibraryEx(dllName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
846 MessageLibraries[dllName] = hModule;
850 hModule = SafeLibraryHandle.LoadLibraryEx(dllName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
853 if (hModule.IsInvalid)
858 msg = EventLog.TryFormatMessage(hModule, messageNum, insertionStrings);
875 /// Gets an array of EventLogEntry's, one for each entry in the log.
877 [ResourceExposure(ResourceScope.None)]
878 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
879 internal EventLogEntry[] GetAllEntries() {
880 // we could just call getEntryAt() on all the entries, but it'll be faster
881 // if we grab multiple entries at once.
882 string currentMachineName = this.machineName;
885 OpenForRead(currentMachineName);
887 EventLogEntry[] entries = new EventLogEntry[EntryCount];
889 int oldestEntry = OldestEntryNumber;
894 while (idx < entries.Length) {
895 byte[] buf = new byte[BUF_SIZE];
896 bool success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ,
897 oldestEntry+idx, buf, buf.Length, out bytesRead, out minBytesNeeded);
899 error = Marshal.GetLastWin32Error();
900 // NOTE, [....]: ERROR_PROC_NOT_FOUND used to get returned, but I think that
901 // was because I was calling GetLastError directly instead of GetLastWin32Error.
902 // Making the buffer bigger and trying again seemed to work. I've removed the check
903 // for ERROR_PROC_NOT_FOUND because I don't think it's necessary any more, but
904 // I can't prove it...
905 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Error from ReadEventLog is " + error.ToString(CultureInfo.InvariantCulture));
906 #if !RETRY_ON_ALL_ERRORS
907 if (error == NativeMethods.ERROR_INSUFFICIENT_BUFFER || error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
909 if (error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
910 // somewhere along the way the event log file changed - probably it
911 // got cleared while we were looping here. Reset the handle and
913 Reset(currentMachineName);
915 // try again with a bigger buffer if necessary
916 else if (minBytesNeeded > buf.Length) {
917 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Increasing buffer size from " + buf.Length.ToString(CultureInfo.InvariantCulture) + " to " + minBytesNeeded.ToString(CultureInfo.InvariantCulture) + " bytes");
918 buf = new byte[minBytesNeeded];
920 success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ,
921 oldestEntry+idx, buf, buf.Length, out bytesRead, out minBytesNeeded);
923 // we'll just stop right here.
925 #if !RETRY_ON_ALL_ERRORS
933 entries[idx] = new EventLogEntry(buf, 0, this);
934 int sum = IntFrom(buf, 0);
936 while (sum < bytesRead && idx < entries.Length) {
937 entries[idx] = new EventLogEntry(buf, sum, this);
938 sum += IntFrom(buf, sum);
942 if (idx != entries.Length) {
944 throw new InvalidOperationException(SR.GetString(SR.CantRetrieveEntries), SharedUtils.CreateSafeWin32Exception(error));
946 throw new InvalidOperationException(SR.GetString(SR.CantRetrieveEntries));
952 /// Searches the cache for an entry with the given index
954 private int GetCachedEntryPos(int entryIndex) {
955 if (cache == null || (boolFlags[Flag_forwards] && entryIndex < firstCachedEntry) ||
956 (!boolFlags[Flag_forwards] && entryIndex > firstCachedEntry) || firstCachedEntry == -1) {
957 // the index falls before anything we have in the cache, or the cache
961 // we only know where the beginning of the cache is, not the end, so even
962 // if it's past the end of the cache, we'll have to search through the whole
963 // cache to find out.
965 // we're betting heavily that the one they want to see now is close
966 // to the one they asked for last time. We start looking where we
967 // stopped last time.
969 // We have two loops, one to go forwards and one to go backwards. Only one
970 // of them will ever be executed.
971 while (lastSeenEntry < entryIndex) {
973 if (boolFlags[Flag_forwards]) {
974 lastSeenPos = GetNextEntryPos(lastSeenPos);
975 if (lastSeenPos >= bytesCached)
979 lastSeenPos = GetPreviousEntryPos(lastSeenPos);
984 while (lastSeenEntry > entryIndex) {
986 if (boolFlags[Flag_forwards]) {
987 lastSeenPos = GetPreviousEntryPos(lastSeenPos);
992 lastSeenPos = GetNextEntryPos(lastSeenPos);
993 if (lastSeenPos >= bytesCached)
997 if (lastSeenPos >= bytesCached) {
998 // we ran past the end. move back to the last one and return -1
999 lastSeenPos = GetPreviousEntryPos(lastSeenPos);
1000 if (boolFlags[Flag_forwards])
1006 else if (lastSeenPos < 0) {
1007 // we ran past the beginning. move back to the first one and return -1
1009 if (boolFlags[Flag_forwards])
1022 /// Gets the entry at the given index
1024 internal EventLogEntry GetEntryAt(int index) {
1025 EventLogEntry entry = GetEntryAtNoThrow(index);
1027 throw new ArgumentException(SR.GetString(SR.IndexOutOfBounds, index.ToString(CultureInfo.CurrentCulture)));
1031 internal EventLogEntry GetEntryAtNoThrow(int index) {
1033 OpenForRead(this.machineName);
1035 if (index < 0 || index >= EntryCount)
1040 index += OldestEntryNumber;
1041 EventLogEntry entry = null;
1044 entry = GetEntryWithOldest(index);
1046 catch (InvalidOperationException) {
1047 // This would be common in rapidly spinning EventLog (i.e. logs which are rapidly receiving
1048 // new events while discarding old ones in a rolling fashion) or if the EventLog is cleared asynchronously.
1050 // EventLogEntryCollection heuristics is little bit convoluted due to the inherent ----s.
1051 // The enumerator predominantly operates on the index from the last
\93known
\94 oldest entry
1052 // (refreshing on every iteration is probalby not right here) and it has no notion of the
1053 // collection size when it is created or while it is operating. It would keep on enumerating
1054 // until the index become invalid.
1056 // Throwing InvalidOperationException to let you know that your enumerator has been invalidated
1057 // because of changes underneath is probably not the most useful behavior.
1062 [ResourceExposure(ResourceScope.None)]
1063 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1064 private EventLogEntry GetEntryWithOldest(int index) {
1065 EventLogEntry entry = null;
1066 int entryPos = GetCachedEntryPos(index);
1067 if (entryPos >= 0) {
1068 entry = new EventLogEntry(cache, entryPos, this);
1072 string currentMachineName = this.machineName;
1074 // if we haven't seen the one after this, we were probably going
1077 if (GetCachedEntryPos(index+1) < 0) {
1078 flags = NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ;
1079 boolFlags[Flag_forwards] = true;
1082 flags = NativeMethods.BACKWARDS_READ | NativeMethods.SEEK_READ;
1083 boolFlags[Flag_forwards] = false;
1086 cache = new byte[BUF_SIZE];
1089 bool success = UnsafeNativeMethods.ReadEventLog(readHandle, flags, index,
1090 cache, cache.Length, out bytesRead, out minBytesNeeded);
1092 int error = Marshal.GetLastWin32Error();
1093 // NOTE, [....]: ERROR_PROC_NOT_FOUND used to get returned, but I think that
1094 // was because I was calling GetLastError directly instead of GetLastWin32Error.
1095 // Making the buffer bigger and trying again seemed to work. I've removed the check
1096 // for ERROR_PROC_NOT_FOUND because I don't think it's necessary any more, but
1097 // I can't prove it...
1098 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Error from ReadEventLog is " + error.ToString(CultureInfo.InvariantCulture));
1099 if (error == NativeMethods.ERROR_INSUFFICIENT_BUFFER || error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
1100 if (error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
1101 // Reset() sets the cache null. But since we're going to call ReadEventLog right after this,
1102 // we need the cache to be something valid. We'll reuse the old byte array rather
1103 // than creating a new one.
1104 byte[] tempcache = cache;
1105 Reset(currentMachineName);
1108 // try again with a bigger buffer.
1109 if (minBytesNeeded > cache.Length) {
1110 cache = new byte[minBytesNeeded];
1113 success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ, index,
1114 cache, cache.Length, out bytesRead, out minBytesNeeded);
1118 throw new InvalidOperationException(SR.GetString(SR.CantReadLogEntryAt, index.ToString(CultureInfo.CurrentCulture)), SharedUtils.CreateSafeWin32Exception());
1121 bytesCached = bytesRead;
1122 firstCachedEntry = index;
1123 lastSeenEntry = index;
1125 return new EventLogEntry(cache, 0, this);
1128 [ResourceExposure(ResourceScope.Machine)]
1129 [ResourceConsumption(ResourceScope.Machine)]
1130 internal static RegistryKey GetEventLogRegKey(string machine, bool writable) {
1131 RegistryKey lmkey = null;
1134 if (machine.Equals(".")) {
1135 lmkey = Registry.LocalMachine;
1138 lmkey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine);
1142 return lmkey.OpenSubKey(EventLogKey, writable);
1145 if (lmkey != null) lmkey.Close();
1151 [ResourceExposure(ResourceScope.Machine)]
1152 [ResourceConsumption(ResourceScope.Machine)]
1153 private RegistryKey GetLogRegKey(string currentMachineName, bool writable) {
1154 string logname = GetLogName(currentMachineName);
1155 // we need to verify the logname here again because we might have tried to look it up
1156 // based on the source and failed.
1157 if (!ValidLogName(logname, false))
1158 throw new InvalidOperationException(SR.GetString(SR.BadLogName));
1160 RegistryKey eventkey = null;
1161 RegistryKey logkey = null;
1164 eventkey = GetEventLogRegKey(currentMachineName, false);
1165 if (eventkey == null)
1166 throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, EventLogKey, currentMachineName));
1168 logkey = eventkey.OpenSubKey(logname, writable);
1170 throw new InvalidOperationException(SR.GetString(SR.MissingLog, logname, currentMachineName));
1173 if (eventkey != null) eventkey.Close();
1179 [ResourceExposure(ResourceScope.None)]
1180 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1181 private object GetLogRegValue(string currentMachineName, string valuename) {
1182 PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
1183 permissionSet.Assert();
1185 RegistryKey logkey = null;
1188 logkey = GetLogRegKey(currentMachineName, false);
1190 throw new InvalidOperationException(SR.GetString(SR.MissingLog, GetLogName(currentMachineName), currentMachineName));
1192 object val = logkey.GetValue(valuename);
1196 if (logkey != null) logkey.Close();
1198 // Revert registry and environment permission asserts
1199 CodeAccessPermission.RevertAssert();
1204 /// Finds the index into the cache where the next entry starts
1206 private int GetNextEntryPos(int pos) {
1207 return pos + IntFrom(cache, pos);
1211 /// Finds the index into the cache where the previous entry starts
1213 private int GetPreviousEntryPos(int pos) {
1214 // the entries in our buffer come back like this:
1215 // <length 1> ... <data> ... <length 1> <length 2> ... <data> ... <length 2> ...
1216 // In other words, the length for each entry is repeated at the beginning and
1217 // at the end. This makes it easy to navigate forwards and backwards through
1219 return pos - IntFrom(cache, pos - 4);
1222 [ResourceExposure(ResourceScope.Machine)]
1223 [ResourceConsumption(ResourceScope.Machine)]
1224 internal static string GetDllPath(string machineName) {
1225 return Path.Combine(SharedUtils.GetLatestBuildDllDirectory(machineName), DllName);
1229 /// Extracts a 32-bit integer from the ubyte buffer, beginning at the byte offset
1230 /// specified in offset.
1232 private static int IntFrom(byte[] buf, int offset) {
1233 // assumes Little Endian byte order.
1234 return(unchecked((int)0xFF000000) & (buf[offset+3] << 24)) | (0xFF0000 & (buf[offset+2] << 16)) |
1235 (0xFF00 & (buf[offset+1] << 8)) | (0xFF & (buf[offset]));
1239 [ResourceExposure(ResourceScope.None)]
1240 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1241 public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) {
1242 string currentMachineName = this.machineName;
1244 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
1245 permission.Demand();
1247 if (action < OverflowAction.DoNotOverwrite || action > OverflowAction.OverwriteOlder)
1248 throw new InvalidEnumArgumentException("action", (int)action, typeof(OverflowAction));
1250 // this is a long because in the if statement we may need to store values as
1251 // large as UInt32.MaxValue - 1. This would overflow an int.
1252 long retentionvalue = (long) action;
1253 if (action == OverflowAction.OverwriteOlder) {
1254 if (retentionDays < 1 || retentionDays > 365)
1255 throw new ArgumentOutOfRangeException(SR.GetString(SR.RentionDaysOutOfRange));
1257 retentionvalue = (long) retentionDays * SecondsPerDay;
1260 PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
1261 permissionSet.Assert();
1263 using (RegistryKey logkey = GetLogRegKey(currentMachineName, true))
1264 logkey.SetValue("Retention", retentionvalue, RegistryValueKind.DWord);
1269 /// Opens the event log with read access
1271 [ResourceExposure(ResourceScope.None)]
1272 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1273 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
1274 private void OpenForRead(string currentMachineName) {
1275 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::OpenForRead");
1277 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
1278 permission.Demand();
1280 //Cannot allocate the readHandle if the object has been disposed, since finalization has been suppressed.
1281 if (this.boolFlags[Flag_disposed])
1282 throw new ObjectDisposedException(GetType().Name);
1284 string logname = GetLogName(currentMachineName);
1286 if (logname == null || logname.Length==0)
1287 throw new ArgumentException(SR.GetString(SR.MissingLogProperty));
1289 if (!EventLog.Exists(logname, currentMachineName) ) // do not open non-existing Log [[....]]
1290 throw new InvalidOperationException( SR.GetString(SR.LogDoesNotExists, logname, currentMachineName) );
1291 //Check environment before calling api
1292 SharedUtils.CheckEnvironment();
1294 // Clean up cache variables.
1295 // [[....]] The initilizing code is put here to guarantee, that first read of events
1296 // from log file will start by filling up the cache buffer.
1300 firstCachedEntry = -1;
1302 SafeEventLogReadHandle handle = SafeEventLogReadHandle.OpenEventLog(currentMachineName, logname);
1303 if (handle.IsInvalid) {
1304 Win32Exception e = null;
1305 if (Marshal.GetLastWin32Error() != 0) {
1306 e = SharedUtils.CreateSafeWin32Exception();
1309 throw new InvalidOperationException(SR.GetString(SR.CantOpenLog, logname.ToString(), currentMachineName), e);
1312 readHandle = handle;
1316 /// Opens the event log with write access
1318 [ResourceExposure(ResourceScope.None)]
1319 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1320 private void OpenForWrite(string currentMachineName) {
1321 //Cannot allocate the writeHandle if the object has been disposed, since finalization has been suppressed.
1322 if (this.boolFlags[Flag_disposed])
1323 throw new ObjectDisposedException(GetType().Name);
1325 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::OpenForWrite");
1326 if (sourceName == null || sourceName.Length==0)
1327 throw new ArgumentException(SR.GetString(SR.NeedSourceToOpen));
1329 //Check environment before calling api
1330 SharedUtils.CheckEnvironment();
1332 SafeEventLogWriteHandle handle = SafeEventLogWriteHandle.RegisterEventSource(currentMachineName, sourceName);
1333 if (handle.IsInvalid) {
1334 Win32Exception e = null;
1335 if (Marshal.GetLastWin32Error() != 0) {
1336 e = SharedUtils.CreateSafeWin32Exception();
1338 throw new InvalidOperationException(SR.GetString(SR.CantOpenLogAccess, sourceName), e);
1341 writeHandle = handle;
1345 [ResourceExposure(ResourceScope.Machine)]
1346 [ResourceConsumption(ResourceScope.Machine)]
1347 public void RegisterDisplayName(string resourceFile, long resourceId) {
1348 string currentMachineName = this.machineName;
1350 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
1351 permission.Demand();
1353 PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
1354 permissionSet.Assert();
1356 using (RegistryKey logkey = GetLogRegKey(currentMachineName, true)) {
1357 logkey.SetValue("DisplayNameFile", resourceFile, RegistryValueKind.ExpandString);
1358 logkey.SetValue("DisplayNameID", resourceId, RegistryValueKind.DWord);
1362 private void Reset(string currentMachineName) {
1363 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Reset");
1364 // save the state we're in now
1365 bool openRead = IsOpenForRead;
1366 bool openWrite = IsOpenForWrite;
1367 bool isMonitoring = boolFlags[Flag_monitoring];
1368 bool isListening = boolFlags[Flag_registeredAsListener];
1370 // close everything down
1371 Close(currentMachineName);
1374 // and get us back into the same state as before
1376 OpenForRead(currentMachineName);
1378 OpenForWrite(currentMachineName);
1380 StartListening(currentMachineName, GetLogName(currentMachineName));
1382 boolFlags[Flag_monitoring] = isMonitoring;
1385 [HostProtection(Synchronization=true)]
1386 private static void RemoveListenerComponent(EventLogInternal component, string compLogName) {
1387 lock (InternalSyncObject) {
1388 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::RemoveListenerComponent(" + compLogName + ")");
1390 LogListeningInfo info = (LogListeningInfo) listenerInfos[compLogName];
1391 Debug.Assert(info != null);
1393 // remove the requested component from the list.
1394 info.listeningComponents.Remove(component);
1395 if (info.listeningComponents.Count != 0)
1398 // if that was the last interested compononent, destroy the handles and stop listening.
1400 info.handleOwner.Dispose();
1402 //Unregister the thread pool wait handle
1403 info.registeredWaitHandle.Unregister(info.waitHandle);
1405 info.waitHandle.Close();
1407 listenerInfos[compLogName] = null;
1412 /// Sets up the event monitoring mechanism. We don't track event log changes
1413 /// unless someone is interested, so we set this up on demand.
1415 [HostProtection(Synchronization=true, ExternalThreading=true)]
1416 private void StartListening(string currentMachineName, string currentLogName) {
1417 // make sure we don't fire events for entries that are already there
1418 Debug.Assert(!boolFlags[Flag_registeredAsListener], "StartListening called with boolFlags[Flag_registeredAsListener] true.");
1419 lastSeenCount = EntryCount + OldestEntryNumber;
1420 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StartListening: lastSeenCount = " + lastSeenCount);
1421 AddListenerComponent(this, currentMachineName, currentLogName);
1422 boolFlags[Flag_registeredAsListener] = true;
1425 private void StartRaisingEvents(string currentMachineName, string currentLogName) {
1426 if (!boolFlags[Flag_initializing] && !boolFlags[Flag_monitoring] && !parent.ComponentDesignMode) {
1427 StartListening(currentMachineName, currentLogName);
1429 boolFlags[Flag_monitoring] = true;
1432 private static void StaticCompletionCallback(object context, bool wasSignaled) {
1434 LogListeningInfo info = (LogListeningInfo) context;
1438 // get a snapshot of the components to fire the event on
1439 EventLogInternal[] interestedComponents;
1440 lock (InternalSyncObject) {
1441 interestedComponents = (EventLogInternal[])info.listeningComponents.ToArray(typeof(EventLogInternal));
1444 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StaticCompletionCallback: notifying " + interestedComponents.Length + " components.");
1446 for (int i = 0; i < interestedComponents.Length; i++) {
1448 if (interestedComponents[i] != null) {
1449 interestedComponents[i].CompletionCallback(null);
1451 } catch (ObjectDisposedException) {
1452 // The EventLog that was registered to listen has been disposed. Nothing much we can do here
1453 // we don't want to propigate this error up as it will likely be unhandled and will cause the app
1455 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StaticCompletionCallback: ignored an ObjectDisposedException");
1461 /// Tears down the event listening mechanism. This is called when the last
1462 /// interested party removes their event handler.
1464 [HostProtection(Synchronization=true, ExternalThreading=true)]
1465 private void StopListening(/*string currentMachineName,*/ string currentLogName) {
1466 Debug.Assert(boolFlags[Flag_registeredAsListener], "StopListening called without StartListening.");
1467 RemoveListenerComponent(this, currentLogName);
1468 boolFlags[Flag_registeredAsListener] = false;
1473 private void StopRaisingEvents(/*string currentMachineName,*/ string currentLogName) {
1474 if (!boolFlags[Flag_initializing] && boolFlags[Flag_monitoring] && !parent.ComponentDesignMode) {
1475 StopListening(currentLogName);
1477 boolFlags[Flag_monitoring] = false;
1480 // CharIsPrintable used to be Char.IsPrintable, but Jay removed it and
1481 // is forcing people to use the Unicode categories themselves. Copied
1483 private static bool CharIsPrintable(char c) {
1484 UnicodeCategory uc = Char.GetUnicodeCategory(c);
1485 return (!(uc == UnicodeCategory.Control) || (uc == UnicodeCategory.Format) ||
1486 (uc == UnicodeCategory.LineSeparator) || (uc == UnicodeCategory.ParagraphSeparator) ||
1487 (uc == UnicodeCategory.OtherNotAssigned));
1490 // SECREVIEW: Make sure this method catches all the strange cases.
1491 internal static bool ValidLogName(string logName, bool ignoreEmpty) {
1492 // No need to trim here since the next check will verify that there are no spaces.
1493 // We need to ignore the empty string as an invalid log name sometimes because it can
1494 // be passed in from our default constructor.
1495 if (logName.Length == 0 && !ignoreEmpty)
1498 //any space, backslash, asterisk, or question mark is bad
1499 //any non-printable characters are also bad
1500 foreach (char c in logName)
1501 if (!CharIsPrintable(c) || (c == '\\') || (c == '*') || (c == '?'))
1507 [ResourceExposure(ResourceScope.None)]
1508 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1509 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
1510 private void VerifyAndCreateSource(string sourceName, string currentMachineName) {
1511 if (boolFlags[Flag_sourceVerified])
1514 if (!EventLog.SourceExists(sourceName, currentMachineName, true)) {
1516 RuntimeHelpers.PrepareConstrainedRegions();
1518 SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
1519 if (!EventLog.SourceExists(sourceName, currentMachineName, true)) {
1520 if (GetLogName(currentMachineName) == null)
1521 this.logName = "Application";
1522 // we automatically add an entry in the registry if there's not already
1523 // one there for this source
1524 EventLog.CreateEventSource(new EventSourceCreationData(sourceName, GetLogName(currentMachineName), currentMachineName));
1525 // The user may have set a custom log and tried to read it before trying to
1526 // write. Due to a quirk in the event log API, we would have opened the Application
1527 // log to read (because the custom log wasn't there). Now that we've created
1528 // the custom log, we should close so that when we re-open, we get a read
1529 // handle on the _new_ log instead of the Application log.
1530 Reset(currentMachineName);
1533 string rightLogName = EventLog.LogNameFromSourceName(sourceName, currentMachineName);
1534 string currentLogName = GetLogName(currentMachineName);
1535 if (rightLogName != null && currentLogName != null && String.Compare(rightLogName, currentLogName, StringComparison.OrdinalIgnoreCase) != 0)
1536 throw new ArgumentException(SR.GetString(SR.LogSourceMismatch, Source.ToString(), currentLogName, rightLogName));
1541 if (mutex != null) {
1542 mutex.ReleaseMutex();
1548 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
1549 permission.Demand();
1551 string rightLogName = EventLog._InternalLogNameFromSourceName(sourceName, currentMachineName);
1552 string currentLogName = GetLogName(currentMachineName);
1553 if (rightLogName != null && currentLogName != null && String.Compare(rightLogName, currentLogName, StringComparison.OrdinalIgnoreCase) != 0)
1554 throw new ArgumentException(SR.GetString(SR.LogSourceMismatch, Source.ToString(), currentLogName, rightLogName));
1557 boolFlags[Flag_sourceVerified] = true;
1562 /// Writes an information type entry with the given message text to the event log.
1565 public void WriteEntry(string message) {
1566 WriteEntry(message, EventLogEntryType.Information, (short) 0, 0, null);
1571 /// Writes an entry of the specified <see cref='System.Diagnostics.EventLogEntryType'/> to the event log. Valid types are
1572 /// <see langword='Error'/>, <see langword='Warning'/>, <see langword='Information'/>,
1573 /// <see langword='Success Audit'/>, and <see langword='Failure Audit'/>.
1576 public void WriteEntry(string message, EventLogEntryType type) {
1577 WriteEntry(message, type, (short) 0, 0, null);
1582 /// Writes an entry of the specified <see cref='System.Diagnostics.EventLogEntryType'/>
1584 /// user-defined <paramref name="eventID"/>
1589 public void WriteEntry(string message, EventLogEntryType type, int eventID) {
1590 WriteEntry(message, type, eventID, 0, null);
1595 /// Writes an entry of the specified type with the
1596 /// user-defined <paramref name="eventID"/> and <paramref name="category"/>
1597 /// to the event log. The <paramref name="category"/>
1598 /// can be used by the event viewer to filter events in the log.
1601 public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) {
1602 WriteEntry(message, type, eventID, category, null);
1607 /// Writes an entry of the specified type with the
1608 /// user-defined <paramref name="eventID"/> and <paramref name="category"/> to the event log, and appends binary data to
1609 /// the message. The Event Viewer does not interpret this data; it
1610 /// displays raw data only in a combined hexadecimal and text format.
1613 public void WriteEntry(string message, EventLogEntryType type, int eventID, short category,
1616 if (eventID < 0 || eventID > ushort.MaxValue)
1617 throw new ArgumentException(SR.GetString(SR.EventID, eventID, 0, (int)ushort.MaxValue));
1619 if (Source.Length == 0)
1620 throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
1622 if (!Enum.IsDefined(typeof(EventLogEntryType), type))
1623 throw new InvalidEnumArgumentException("type", (int)type, typeof(EventLogEntryType));
1625 string currentMachineName = machineName;
1626 if (!boolFlags[Flag_writeGranted]) {
1627 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
1628 permission.Demand();
1629 boolFlags[Flag_writeGranted] = true;
1632 VerifyAndCreateSource(sourceName, currentMachineName);
1634 // now that the source has been hooked up to our DLL, we can use "normal"
1635 // (message-file driven) logging techniques.
1636 // Our DLL has 64K different entries; all of them just display the first
1637 // insertion string.
1638 InternalWriteEvent((uint)eventID, (ushort)category, type, new string[] { message}, rawData, currentMachineName);
1642 public void WriteEvent(EventInstance instance, params Object[] values) {
1643 WriteEvent(instance, null, values);
1647 public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) {
1648 if (instance == null)
1649 throw new ArgumentNullException("instance");
1650 if (Source.Length == 0)
1651 throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
1653 string currentMachineName = machineName;
1654 if (!boolFlags[Flag_writeGranted]) {
1655 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
1656 permission.Demand();
1657 boolFlags[Flag_writeGranted] = true;
1660 VerifyAndCreateSource(Source, currentMachineName);
1662 string[] strings = null;
1664 if (values != null) {
1665 strings = new string[values.Length];
1666 for (int i=0; i<values.Length; i++) {
1667 if (values[i] != null)
1668 strings[i] = values[i].ToString();
1670 strings[i] = String.Empty;
1674 InternalWriteEvent((uint) instance.InstanceId, (ushort) instance.CategoryId, instance.EntryType, strings, data, currentMachineName);
1677 [ResourceExposure(ResourceScope.None)]
1678 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1679 private void InternalWriteEvent(uint eventID, ushort category, EventLogEntryType type, string[] strings,
1680 byte[] rawData, string currentMachineName) {
1683 if (strings == null)
1684 strings = new string[0];
1685 if (strings.Length >= 256)
1686 throw new ArgumentException(SR.GetString(SR.TooManyReplacementStrings));
1688 for (int i = 0; i < strings.Length; i++) {
1689 if (strings[i] == null)
1690 strings[i] = String.Empty;
1692 // make sure the strings aren't too long. MSDN says each string has a limit of 32k (32768) characters, but
1693 // experimentation shows that it doesn't like anything larger than 32766
1694 if (strings[i].Length > 32766)
1695 throw new ArgumentException(SR.GetString(SR.LogEntryTooLong));
1697 if (rawData == null)
1698 rawData = new byte[0];
1700 if (Source.Length == 0)
1701 throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
1703 if (!IsOpenForWrite)
1704 OpenForWrite(currentMachineName);
1706 // pin each of the strings in memory
1707 IntPtr[] stringRoots = new IntPtr[strings.Length];
1708 GCHandle[] stringHandles = new GCHandle[strings.Length];
1709 GCHandle stringsRootHandle = GCHandle.Alloc(stringRoots, GCHandleType.Pinned);
1711 for (int strIndex = 0; strIndex < strings.Length; strIndex++) {
1712 stringHandles[strIndex] = GCHandle.Alloc(strings[strIndex], GCHandleType.Pinned);
1713 stringRoots[strIndex] = stringHandles[strIndex].AddrOfPinnedObject();
1717 // actually report the event
1718 bool success = UnsafeNativeMethods.ReportEvent(writeHandle, (short) type, category, eventID,
1719 sid, (short) strings.Length, rawData.Length, new HandleRef(this, stringsRootHandle.AddrOfPinnedObject()), rawData);
1721 //Trace("WriteEvent", "Throwing Win32Exception");
1722 throw SharedUtils.CreateSafeWin32Exception();
1726 // now free the pinned strings
1727 for (int i = 0; i < strings.Length; i++) {
1728 if (stringHandles[i].IsAllocated)
1729 stringHandles[i].Free();
1731 stringsRootHandle.Free();
1735 private class LogListeningInfo {
1736 public EventLogInternal handleOwner;
1737 public RegisteredWaitHandle registeredWaitHandle;
1738 public WaitHandle waitHandle;
1739 public ArrayList listeningComponents = new ArrayList();