1 //------------------------------------------------------------------------------
2 // <copyright file="EventLog.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 //#define RETRY_ON_ALL_ERRORS
9 namespace System.Diagnostics {
11 using System.Text.RegularExpressions;
12 using System.Threading;
13 using System.Runtime.InteropServices;
14 using System.ComponentModel;
15 using System.Diagnostics;
17 using Microsoft.Win32;
18 using Microsoft.Win32.SafeHandles;
20 using System.Collections;
21 using System.Collections.Specialized;
22 using System.Globalization;
23 using System.ComponentModel.Design;
24 using System.Security;
25 using System.Security.Permissions;
26 using System.Reflection;
27 using System.Runtime.Versioning;
28 using System.Runtime.CompilerServices;
29 using System.Diagnostics.CodeAnalysis;
33 /// Provides interaction with Windows 2000 event logs.
37 DefaultEvent("EntryWritten"),
38 InstallerType("System.Diagnostics.EventLogInstaller, " + AssemblyRef.SystemConfigurationInstall),
39 MonitoringDescription(SR.EventLogDesc)
41 public class EventLog : Component, ISupportInitialize {
43 private const string EventLogKey = "SYSTEM\\CurrentControlSet\\Services\\EventLog";
44 internal const string DllName = "EventLogMessages.dll";
45 private const string eventLogMutexName = "netfxeventlog.1.0";
47 private const int DefaultMaxSize = 512 * 1024;
48 private const int DefaultRetention = 7 * SecondsPerDay;
49 private const int SecondsPerDay = 60 * 60 * 24;
51 private EventLogInternal m_underlyingEventLog;
53 // Whether we need backward compatible OS patch work or not
54 private static volatile bool s_CheckedOsVersion;
55 private static volatile bool s_SkipRegPatch;
57 private static bool SkipRegPatch {
59 if (!s_CheckedOsVersion) {
60 OperatingSystem os = Environment.OSVersion;
61 s_SkipRegPatch = (os.Platform == PlatformID.Win32NT) && (os.Version.Major > 5);
62 s_CheckedOsVersion = true;
64 return s_SkipRegPatch;
68 internal static PermissionSet _UnsafeGetAssertPermSet() {
69 // SEC_NOTE: All callers should already be guarded by EventLogPermission demand.
70 PermissionSet permissionSet = new PermissionSet(PermissionState.None);
72 // We need RegistryPermission
73 RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
74 permissionSet.AddPermission(registryPermission);
76 // It is not enough to just assert RegistryPermission, for some regkeys
77 // we need to assert EnvironmentPermission too
78 EnvironmentPermission environmentPermission = new EnvironmentPermission(PermissionState.Unrestricted);
79 permissionSet.AddPermission(environmentPermission);
81 // For remote machine registry access UnmanagdCodePermission is required.
82 SecurityPermission securityPermission = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
83 permissionSet.AddPermission(securityPermission);
90 /// Initializes a new instance of the <see cref='System.Diagnostics.EventLog'/>
94 public EventLog() : this("", ".", "") {
98 /// <para>[To be supplied.]</para>
100 public EventLog(string logName) : this(logName, ".", "") {
104 /// <para>[To be supplied.]</para>
106 public EventLog(string logName, string machineName) : this(logName, machineName, "") {
110 /// <para>[To be supplied.]</para>
112 public EventLog(string logName, string machineName, string source) {
113 m_underlyingEventLog = new EventLogInternal(logName, machineName, source, this);
118 /// Gets the contents of the event log.
122 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
123 [MonitoringDescription(SR.LogEntries)]
124 public EventLogEntryCollection Entries {
126 return m_underlyingEventLog.Entries;
135 public string LogDisplayName {
136 [ResourceExposure(ResourceScope.Machine)]
137 [ResourceConsumption(ResourceScope.Machine)]
139 return m_underlyingEventLog.LogDisplayName;
145 /// Gets or sets the name of the log to read from and write to.
148 [TypeConverter("System.Diagnostics.Design.LogConverter, " + AssemblyRef.SystemDesign)]
150 [MonitoringDescription(SR.LogLog)]
152 [SettingsBindable(true)]
153 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, oldLog.machineName doesn't change")]
154 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "[....]: By design, see justification above assert")]
157 return m_underlyingEventLog.Log;
160 EventLogInternal newLog = new EventLogInternal(value, m_underlyingEventLog.MachineName, m_underlyingEventLog.Source, this);
161 EventLogInternal oldLog = m_underlyingEventLog;
163 // EnableRaisingEvents and Close demand Write permission but that permission might be removed upstack
164 // previously we didn't call Close() since we were reusing the same object. We assert the permission here.
165 new EventLogPermission(EventLogPermissionAccess.Write, oldLog.machineName).Assert();
166 if (oldLog.EnableRaisingEvents) {
167 newLog.onEntryWrittenHandler = oldLog.onEntryWrittenHandler;
168 newLog.EnableRaisingEvents = true;
170 m_underlyingEventLog = newLog;
177 /// Gets or sets the name of the computer on which to read or write events.
181 [MonitoringDescription(SR.LogMachineName)]
183 [SettingsBindable(true)]
184 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "[....]: By design, see justification above assert")]
185 public string MachineName {
187 return m_underlyingEventLog.MachineName;
190 EventLogInternal newLog = new EventLogInternal(m_underlyingEventLog.logName, value, m_underlyingEventLog.sourceName, this);
191 EventLogInternal oldLog = m_underlyingEventLog;
193 // EnableRaisingEvents and Close demand Write permission but that permission might be removed upstack
194 // previously we didn't call Close() since we were reusing the same object. We assert the permission here.
195 new EventLogPermission(EventLogPermissionAccess.Write, oldLog.machineName).Assert();
196 if (oldLog.EnableRaisingEvents) {
197 newLog.onEntryWrittenHandler = oldLog.onEntryWrittenHandler;
198 newLog.EnableRaisingEvents = true;
200 m_underlyingEventLog = newLog;
205 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
208 public long MaximumKilobytes {
210 return m_underlyingEventLog.MaximumKilobytes;
213 [ResourceExposure(ResourceScope.None)]
214 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
216 m_underlyingEventLog.MaximumKilobytes = value;
222 public OverflowAction OverflowAction {
224 return m_underlyingEventLog.OverflowAction;
230 public int MinimumRetentionDays {
232 return m_underlyingEventLog.MinimumRetentionDays;
236 // EventLogInternal needs to know if the component is in design mode but
237 // the DesignMode property is protected.
238 internal bool ComponentDesignMode {
240 return this.DesignMode;
244 // Expose for EventLogInternal
245 internal object ComponentGetService(Type service) {
246 return GetService(service);
252 [MonitoringDescription(SR.LogMonitoring)]
253 [DefaultValue(false)]
254 public bool EnableRaisingEvents {
256 return m_underlyingEventLog.EnableRaisingEvents;
259 m_underlyingEventLog.EnableRaisingEvents = value;
265 /// Represents the object used to marshal the event handler
266 /// calls issued as a result of an <see cref='System.Diagnostics.EventLog'/>
272 [MonitoringDescription(SR.LogSynchronizingObject)]
273 public ISynchronizeInvoke SynchronizingObject {
274 [HostProtection(Synchronization=true)]
276 return m_underlyingEventLog.SynchronizingObject;
280 m_underlyingEventLog.SynchronizingObject = value;
287 /// sets the application name (source name) to register and use when writing to the event log.
291 [TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign)]
292 [MonitoringDescription(SR.LogSource)]
294 [SettingsBindable(true)]
295 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, oldLog.machineName doesn't change")]
296 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "[....]: By design, see justification above assert")]
297 public string Source {
299 return m_underlyingEventLog.Source;
302 EventLogInternal newLog = new EventLogInternal(m_underlyingEventLog.Log, m_underlyingEventLog.MachineName, CheckAndNormalizeSourceName(value), this);
303 EventLogInternal oldLog = m_underlyingEventLog;
305 // EnableRaisingEvents and Close demand Write permission but that permission might be removed upstack
306 // previously we didn't call Close() since we were reusing the same object. We assert the permission here.
307 new EventLogPermission(EventLogPermissionAccess.Write, oldLog.machineName).Assert();
308 if (oldLog.EnableRaisingEvents) {
309 newLog.onEntryWrittenHandler = oldLog.onEntryWrittenHandler;
310 newLog.EnableRaisingEvents = true;
312 m_underlyingEventLog = newLog;
320 /// Occurs when an entry is written to the event log.
323 [MonitoringDescription(SR.LogEntryWritten)]
324 public event EntryWrittenEventHandler EntryWritten {
326 m_underlyingEventLog.EntryWritten += value;
329 m_underlyingEventLog.EntryWritten -= value;
335 public void BeginInit() {
336 m_underlyingEventLog.BeginInit();
342 /// the event log by removing all entries from it.
345 [ResourceExposure(ResourceScope.Machine)] // Should anyone ever call this, other than an event log viewer?
346 [ResourceConsumption(ResourceScope.Machine)]
347 public void Clear() {
348 m_underlyingEventLog.Clear();
353 /// Closes the event log and releases read and write handles.
356 [ResourceExposure(ResourceScope.None)]
357 public void Close() {
358 m_underlyingEventLog.Close();
362 /// <para> Establishes an application, using the
363 /// specified <see cref='System.Diagnostics.EventLog.Source'/> , as a valid event source for
365 /// to a log on the local computer. This method
366 /// can also be used to create
367 /// a new custom log on the local computer.</para>
369 public static void CreateEventSource(string source, string logName) {
370 CreateEventSource(new EventSourceCreationData(source, logName, "."));
374 /// <para>Establishes an application, using the specified
375 /// <see cref='System.Diagnostics.EventLog.Source'/> as a valid event source for writing
376 /// entries to a log on the computer
377 /// specified by <paramref name="machineName"/>. This method can also be used to create a new
378 /// custom log on the given computer.</para>
380 [Obsolete("This method has been deprecated. Please use System.Diagnostics.EventLog.CreateEventSource(EventSourceCreationData sourceData) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
381 public static void CreateEventSource(string source, string logName, string machineName) {
382 CreateEventSource(new EventSourceCreationData(source, logName, machineName));
385 [ResourceExposure(ResourceScope.None)]
386 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
387 public static void CreateEventSource(EventSourceCreationData sourceData) {
388 if (sourceData == null)
389 throw new ArgumentNullException("sourceData");
391 string logName = sourceData.LogName;
392 string source = sourceData.Source;
393 string machineName = sourceData.MachineName;
396 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Checking arguments");
397 if (!SyntaxCheck.CheckMachineName(machineName)) {
398 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
400 if (logName == null || logName.Length==0)
401 logName = "Application";
402 if (!ValidLogName(logName, false))
403 throw new ArgumentException(SR.GetString(SR.BadLogName));
404 if (source == null || source.Length==0)
405 throw new ArgumentException(SR.GetString(SR.MissingParameter, "source"));
406 if (source.Length + EventLogKey.Length > 254)
407 throw new ArgumentException(SR.GetString(SR.ParameterTooLong, "source", 254 - EventLogKey.Length));
409 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
413 RuntimeHelpers.PrepareConstrainedRegions();
415 SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
416 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Calling SourceExists");
417 if (SourceExists(source, machineName, true)) {
418 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: SourceExists returned true");
419 // don't let them register a source if it already exists
420 // this makes more sense than just doing it anyway, because the source might
421 // be registered under a different log name, and we don't want to create
423 if (".".Equals(machineName))
424 throw new ArgumentException(SR.GetString(SR.LocalSourceAlreadyExists, source));
426 throw new ArgumentException(SR.GetString(SR.SourceAlreadyExists, source, machineName));
429 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting DllPath");
431 //SECREVIEW: Note that EventLog permission is demanded above.
432 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
433 permissionSet.Assert();
435 RegistryKey baseKey = null;
436 RegistryKey eventKey = null;
437 RegistryKey logKey = null;
438 RegistryKey sourceLogKey = null;
439 RegistryKey sourceKey = null;
441 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting local machine regkey");
442 if (machineName == ".")
443 baseKey = Registry.LocalMachine;
445 baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName);
447 eventKey = baseKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\EventLog", true);
448 if (eventKey == null) {
449 if (!".".Equals(machineName))
450 throw new InvalidOperationException(SR.GetString(SR.RegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source, machineName));
452 throw new InvalidOperationException(SR.GetString(SR.LocalRegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source));
455 // The event log system only treats the first 8 characters of the log name as
456 // significant. If they're creating a new log, but that new log has the same
457 // first 8 characters as another log, the system will think they're the same.
458 // Throw an exception to let them know.
459 logKey = eventKey.OpenSubKey(logName, true);
460 if (logKey == null && logName.Length >= 8) {
462 // check for Windows embedded logs file names
463 string logNameFirst8 = logName.Substring(0,8);
464 if ( string.Compare(logNameFirst8,"AppEvent",StringComparison.OrdinalIgnoreCase) ==0 ||
465 string.Compare(logNameFirst8,"SecEvent",StringComparison.OrdinalIgnoreCase) ==0 ||
466 string.Compare(logNameFirst8,"SysEvent",StringComparison.OrdinalIgnoreCase) ==0 )
467 throw new ArgumentException(SR.GetString(SR.InvalidCustomerLogName, logName));
469 string sameLogName = FindSame8FirstCharsLog(eventKey, logName);
470 if ( sameLogName != null )
471 throw new ArgumentException(SR.GetString(SR.DuplicateLogName, logName, sameLogName));
474 bool createLogKey = (logKey == null);
476 if (SourceExists(logName, machineName, true)) {
477 // don't let them register a log name that already
478 // exists as source name, a source with the same
479 // name as the log will have to be created by default
480 if (".".Equals(machineName))
481 throw new ArgumentException(SR.GetString(SR.LocalLogAlreadyExistsAsSource, logName));
483 throw new ArgumentException(SR.GetString(SR.LogAlreadyExistsAsSource, logName, machineName));
486 logKey = eventKey.CreateSubKey(logName);
488 // NOTE: We shouldn't set "Sources" explicitly, the OS will automatically set it.
489 // The EventLog service doesn't use it for anything it is just an helping hand for event viewer filters.
490 // Writing this value explicitly might confuse the service as it might perceive it as a change and
491 // start initializing again
494 logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
496 SetSpecialLogRegValues(logKey, logName);
498 // A source with the same name as the log has to be created
499 // by default. It is the behavior expected by EventLog API.
500 sourceLogKey = logKey.CreateSubKey(logName);
501 SetSpecialSourceRegValues(sourceLogKey, sourceData);
504 if (logName != source) {
506 SetSpecialLogRegValues(logKey, logName);
509 string[] sources = logKey.GetValue("Sources") as string[];
511 logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
513 // We have a ---- with OS EventLog here.
514 // OS might update Sources as well. We should avoid writing the
515 // source name if OS beats us.
516 if( Array.IndexOf(sources, source) == -1) {
517 string[] newsources = new string[sources.Length + 1];
518 Array.Copy(sources, newsources, sources.Length);
519 newsources[sources.Length] = source;
520 logKey.SetValue("Sources", newsources, RegistryValueKind.MultiString);
526 sourceKey = logKey.CreateSubKey(source);
527 SetSpecialSourceRegValues(sourceKey, sourceData);
534 if (eventKey != null)
537 if (logKey != null) {
542 if (sourceLogKey != null) {
543 sourceLogKey.Flush();
544 sourceLogKey.Close();
547 if (sourceKey != null) {
552 // Revert registry and environment permission asserts
553 CodeAccessPermission.RevertAssert();
558 mutex.ReleaseMutex();
568 /// log from the local computer.
571 [ResourceExposure(ResourceScope.Machine)] // See why someone would delete an event log
572 [ResourceConsumption(ResourceScope.Machine)]
573 public static void Delete(string logName) {
574 Delete(logName, ".");
582 /// log from the specified computer.
585 [ResourceExposure(ResourceScope.Machine)] // See why someone would delete an event log
586 [ResourceConsumption(ResourceScope.Machine)]
587 public static void Delete(string logName, string machineName) {
589 if (!SyntaxCheck.CheckMachineName(machineName))
590 throw new ArgumentException(SR.GetString(SR.InvalidParameterFormat, "machineName"));
591 if (logName == null || logName.Length==0)
592 throw new ArgumentException(SR.GetString(SR.NoLogName));
593 if (!ValidLogName(logName, false))
594 throw new InvalidOperationException(SR.GetString(SR.BadLogName));
596 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
599 //Check environment before even trying to play with the registry
600 SharedUtils.CheckEnvironment();
602 //SECREVIEW: Note that EventLog permission is demanded above.
603 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
604 permissionSet.Assert();
606 RegistryKey eventlogkey = null;
609 RuntimeHelpers.PrepareConstrainedRegions();
611 SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
614 eventlogkey = GetEventLogRegKey(machineName, true);
615 if (eventlogkey == null) {
616 // there's not even an event log service on the machine.
617 // or, more likely, we don't have the access to read the registry.
618 throw new InvalidOperationException(SR.GetString(SR.RegKeyNoAccess, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog", machineName));
621 using (RegistryKey logKey = eventlogkey.OpenSubKey(logName)) {
623 throw new InvalidOperationException(SR.GetString(SR.MissingLog, logName, machineName));
625 //clear out log before trying to delete it
626 //that way, if we can't delete the log file, no entries will persist because it has been cleared
627 EventLog logToClear = new EventLog(logName, machineName);
638 string filename = null;
640 //most of the time, the "File" key does not exist, but we'll still give it a whirl
641 filename = (string) logKey.GetValue("File");
644 if (filename != null) {
646 File.Delete(filename);
652 // now delete the registry entry
653 eventlogkey.DeleteSubKeyTree(logName);
656 if (eventlogkey != null) eventlogkey.Close();
658 // Revert registry and environment permission asserts
659 CodeAccessPermission.RevertAssert();
663 if (mutex != null) mutex.ReleaseMutex();
669 /// Removes the event source
670 /// registration from the event log of the local computer.
673 [ResourceExposure(ResourceScope.None)]
674 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
675 public static void DeleteEventSource(string source) {
676 DeleteEventSource(source, ".");
682 /// the application's event source registration from the specified computer.
685 [ResourceExposure(ResourceScope.Machine)]
686 [ResourceConsumption(ResourceScope.Machine)]
687 public static void DeleteEventSource(string source, string machineName) {
688 if (!SyntaxCheck.CheckMachineName(machineName)) {
689 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
692 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
695 //Check environment before looking at the registry
696 SharedUtils.CheckEnvironment();
698 //SECREVIEW: Note that EventLog permission is demanded above.
699 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
700 permissionSet.Assert();
703 RuntimeHelpers.PrepareConstrainedRegions();
705 SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
706 RegistryKey key = null;
708 // First open the key read only so we can do some checks. This is important so we get the same
709 // exceptions even if we don't have write access to the reg key.
710 using (key = FindSourceRegistration(source, machineName, true)) {
712 if (machineName == null)
713 throw new ArgumentException(SR.GetString(SR.LocalSourceNotRegistered, source));
715 throw new ArgumentException(SR.GetString(SR.SourceNotRegistered, source, machineName, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog"));
718 // Check parent registry key (Event Log Name) and if it's equal to source, then throw an exception.
719 // The reason: each log registry key must always contain subkey (i.e. source) with the same name.
720 string keyname = key.Name;
721 int index = keyname.LastIndexOf('\\');
722 if ( string.Compare(keyname, index+1, source, 0, keyname.Length - index, StringComparison.Ordinal) == 0 )
723 throw new InvalidOperationException(SR.GetString(SR.CannotDeleteEqualSource, source));
727 // now open it read/write to try to do the actual delete
728 key = FindSourceRegistration(source, machineName, false);
729 key.DeleteSubKeyTree(source);
732 string[] sources = (string[]) key.GetValue("Sources");
733 ArrayList newsources = new ArrayList(sources.Length - 1);
735 for (int i=0; i<sources.Length; i++) {
736 if (sources[i] != source) {
737 newsources.Add(sources[i]);
740 string[] newsourcesArray = new string[newsources.Count];
741 newsources.CopyTo(newsourcesArray);
743 key.SetValue("Sources", newsourcesArray, RegistryValueKind.MultiString);
752 // Revert registry and environment permission asserts
753 CodeAccessPermission.RevertAssert();
758 mutex.ReleaseMutex();
764 protected override void Dispose(bool disposing) {
765 if(m_underlyingEventLog != null) {
766 m_underlyingEventLog.Dispose(disposing);
769 base.Dispose(disposing);
774 public void EndInit() {
775 m_underlyingEventLog.EndInit();
780 /// Determines whether the log
781 /// exists on the local computer.
784 public static bool Exists(string logName) {
785 return Exists(logName, ".");
790 /// Determines whether the
791 /// log exists on the specified computer.
794 [ResourceExposure(ResourceScope.None)]
795 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
796 public static bool Exists(string logName, string machineName) {
797 if (!SyntaxCheck.CheckMachineName(machineName))
798 throw new ArgumentException(SR.GetString(SR.InvalidParameterFormat, "machineName"));
800 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
803 if (logName == null || logName.Length==0)
806 //Check environment before looking at the registry
807 SharedUtils.CheckEnvironment();
809 //SECREVIEW: Note that EventLog permission is demanded above.
810 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
811 permissionSet.Assert();
813 RegistryKey eventkey = null;
814 RegistryKey logKey = null;
817 eventkey = GetEventLogRegKey(machineName, false);
818 if (eventkey == null)
821 logKey = eventkey.OpenSubKey(logName, false); // try to find log file key immediately.
822 return (logKey != null );
825 if (eventkey != null) eventkey.Close();
826 if (logKey != null) logKey.Close();
828 // Revert registry and environment permission asserts
829 CodeAccessPermission.RevertAssert();
834 // Try to find log file name with the same 8 first characters.
835 // Returns 'null' if no "same first 8 chars" log is found. logName.Length must be > 7
836 private static string FindSame8FirstCharsLog(RegistryKey keyParent, string logName) {
838 string logNameFirst8 = logName.Substring(0, 8);
839 string[] logNames = keyParent.GetSubKeyNames();
841 for (int i = 0; i < logNames.Length; i++) {
842 string currentLogName = logNames[i];
843 if ( currentLogName.Length >= 8 &&
844 string.Compare(currentLogName.Substring(0, 8), logNameFirst8, StringComparison.OrdinalIgnoreCase) == 0)
845 return currentLogName;
848 return null; // not found
852 /// Gets a RegistryKey that points to the LogName entry in the registry that is
853 /// the parent of the given source on the given machine, or null if none is found.
855 [ResourceExposure(ResourceScope.Machine)]
856 [ResourceConsumption(ResourceScope.Machine)]
857 private static RegistryKey FindSourceRegistration(string source, string machineName, bool readOnly) {
858 return FindSourceRegistration(source, machineName, readOnly, false);
862 /// Gets a RegistryKey that points to the LogName entry in the registry that is
863 /// the parent of the given source on the given machine, or null if none is found.
865 [ResourceExposure(ResourceScope.Machine)]
866 [ResourceConsumption(ResourceScope.Machine)]
867 private static RegistryKey FindSourceRegistration(string source, string machineName, bool readOnly, bool wantToCreate) {
868 if (source != null && source.Length != 0) {
870 //Check environment before looking at the registry
871 SharedUtils.CheckEnvironment();
873 //SECREVIEW: Any call to this function must have demmanded
874 // EventLogPermission before.
875 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
876 permissionSet.Assert();
878 RegistryKey eventkey = null;
880 eventkey = GetEventLogRegKey(machineName, !readOnly);
881 if (eventkey == null) {
882 // there's not even an event log service on the machine.
883 // or, more likely, we don't have the access to read the registry.
887 StringBuilder inaccessibleLogs = null;
889 // Most machines will return only { "Application", "System", "Security" },
890 // but you can create your own if you want.
891 string[] logNames = eventkey.GetSubKeyNames();
892 for (int i = 0; i < logNames.Length; i++) {
893 // see if the source is registered in this log.
894 // NOTE: A source name must be unique across ALL LOGS!
895 RegistryKey sourceKey = null;
897 RegistryKey logKey = eventkey.OpenSubKey(logNames[i], /*writable*/!readOnly);
898 if (logKey != null) {
899 sourceKey = logKey.OpenSubKey(source, /*writable*/!readOnly);
900 if (sourceKey != null) {
907 // else logKey is null, so we don't need to Close it
909 catch (UnauthorizedAccessException) {
910 if (inaccessibleLogs == null) {
911 inaccessibleLogs = new StringBuilder(logNames[i]);
914 inaccessibleLogs.Append(", ");
915 inaccessibleLogs.Append(logNames[i]);
918 catch (SecurityException) {
919 if (inaccessibleLogs == null) {
920 inaccessibleLogs = new StringBuilder(logNames[i]);
923 inaccessibleLogs.Append(", ");
924 inaccessibleLogs.Append(logNames[i]);
928 if (sourceKey != null) sourceKey.Close();
932 if (inaccessibleLogs != null)
933 throw new SecurityException(SR.GetString(wantToCreate ? SR.SomeLogsInaccessibleToCreate : SR.SomeLogsInaccessible, inaccessibleLogs.ToString()));
937 if (eventkey != null) eventkey.Close();
939 // Revert registry and environment permission asserts
940 CodeAccessPermission.RevertAssert();
942 // didn't see it anywhere
950 /// Searches for all event logs on the local computer and
951 /// creates an array of <see cref='System.Diagnostics.EventLog'/>
952 /// objects to contain the
956 public static EventLog[] GetEventLogs() {
957 return GetEventLogs(".");
962 /// Searches for all event logs on the given computer and
963 /// creates an array of <see cref='System.Diagnostics.EventLog'/>
964 /// objects to contain the
968 [ResourceExposure(ResourceScope.None)]
969 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
970 public static EventLog[] GetEventLogs(string machineName) {
971 if (!SyntaxCheck.CheckMachineName(machineName)) {
972 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
975 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
978 //Check environment before looking at the registry
979 SharedUtils.CheckEnvironment();
981 string[] logNames = new string[0];
982 //SECREVIEW: Note that EventLogPermission is just demmanded above
983 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
984 permissionSet.Assert();
986 RegistryKey eventkey = null;
988 // we figure out what logs are on the machine by looking in the registry.
989 eventkey = GetEventLogRegKey(machineName, false);
990 if (eventkey == null)
991 // there's not even an event log service on the machine.
992 // or, more likely, we don't have the access to read the registry.
993 throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, EventLogKey, machineName));
994 // Most machines will return only { "Application", "System", "Security" },
995 // but you can create your own if you want.
996 logNames = eventkey.GetSubKeyNames();
999 if (eventkey != null) eventkey.Close();
1000 // Revert registry and environment permission asserts
1001 CodeAccessPermission.RevertAssert();
1004 // now create EventLog objects that point to those logs
1005 EventLog[] logs = new EventLog[logNames.Length];
1006 for (int i = 0; i < logNames.Length; i++) {
1007 EventLog log = new EventLog(logNames[i], machineName);
1014 [ResourceExposure(ResourceScope.Machine)]
1015 [ResourceConsumption(ResourceScope.Machine)]
1016 internal static RegistryKey GetEventLogRegKey(string machine, bool writable) {
1017 RegistryKey lmkey = null;
1020 if (machine.Equals(".")) {
1021 lmkey = Registry.LocalMachine;
1024 lmkey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine);
1028 return lmkey.OpenSubKey(EventLogKey, writable);
1031 if (lmkey != null) lmkey.Close();
1037 [ResourceExposure(ResourceScope.Machine)]
1038 [ResourceConsumption(ResourceScope.Machine)]
1039 internal static string GetDllPath(string machineName) {
1040 return Path.Combine(SharedUtils.GetLatestBuildDllDirectory(machineName), DllName);
1045 /// Determines whether an event source is registered on the local computer.
1048 public static bool SourceExists(string source) {
1049 return SourceExists(source, ".");
1054 /// Determines whether an event
1055 /// source is registered on a specified computer.
1058 [ResourceExposure(ResourceScope.None)]
1059 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1060 public static bool SourceExists(string source, string machineName) {
1061 return SourceExists(source, machineName, false);
1066 /// Determines whether an event
1067 /// source is registered on a specified computer.
1070 [ResourceExposure(ResourceScope.None)]
1071 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1072 [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
1073 internal static bool SourceExists(string source, string machineName, bool wantToCreate) {
1074 if (!SyntaxCheck.CheckMachineName(machineName)) {
1075 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
1078 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
1079 permission.Demand();
1081 using (RegistryKey keyFound = FindSourceRegistration(source, machineName, true, wantToCreate)) {
1082 return (keyFound != null);
1087 /// Gets the name of the log that the given source name is registered in.
1089 public static string LogNameFromSourceName(string source, string machineName) {
1090 EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
1091 permission.Demand();
1093 return _InternalLogNameFromSourceName(source, machineName);
1096 // No permission check, use with care!
1097 [ResourceExposure(ResourceScope.None)]
1098 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1099 internal static string _InternalLogNameFromSourceName(string source, string machineName) {
1100 using (RegistryKey key = FindSourceRegistration(source, machineName, true)) {
1104 string name = key.Name;
1105 int whackPos = name.LastIndexOf('\\');
1106 // this will work even if whackPos is -1
1107 return name.Substring(whackPos+1);
1114 [ResourceExposure(ResourceScope.None)]
1115 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1116 public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) {
1117 m_underlyingEventLog.ModifyOverflowPolicy(action, retentionDays);
1121 [ResourceExposure(ResourceScope.Machine)]
1122 [ResourceConsumption(ResourceScope.Machine)]
1123 public void RegisterDisplayName(string resourceFile, long resourceId) {
1124 m_underlyingEventLog.RegisterDisplayName(resourceFile, resourceId);
1127 // The reasoning behind filling these values is historical. WS03 RTM had a ----
1128 // between registry changes and EventLog service, which made the service wait 2 secs
1129 // before retrying to see whether all regkey values are present. To avoid this
1130 // potential lag (worst case up to n*2 secs where n is the number of required regkeys)
1131 // between creation and being able to write events, we started filling some of these
1132 // values explicitly but for XP and latter OS releases like WS03 SP1 and Vista this
1133 // is not necessary and in some cases like the "File" key it's plain wrong to write.
1134 private static void SetSpecialLogRegValues(RegistryKey logKey, string logName) {
1135 // Set all the default values for this log. AutoBackupLogfiles only makes sense in
1136 // Win2000 SP4, WinXP SP1, and Win2003, but it should alright elsewhere.
1138 // Since we use this method on the existing system logs as well as our own,
1139 // we need to make sure we don't overwrite any existing values.
1140 if (logKey.GetValue("MaxSize") == null)
1141 logKey.SetValue("MaxSize", DefaultMaxSize, RegistryValueKind.DWord);
1142 if (logKey.GetValue("AutoBackupLogFiles") == null)
1143 logKey.SetValue("AutoBackupLogFiles", 0, RegistryValueKind.DWord);
1145 if (!SkipRegPatch) {
1146 // In Vista, "retention of events for 'n' days" concept is removed
1147 if (logKey.GetValue("Retention") == null)
1148 logKey.SetValue("Retention", DefaultRetention, RegistryValueKind.DWord);
1150 if (logKey.GetValue("File") == null) {
1152 if (logName.Length > 8)
1153 filename = @"%SystemRoot%\System32\config\" + logName.Substring(0,8) + ".evt";
1155 filename = @"%SystemRoot%\System32\config\" + logName + ".evt";
1157 logKey.SetValue("File", filename, RegistryValueKind.ExpandString);
1162 [ResourceExposure(ResourceScope.None)]
1163 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1164 private static void SetSpecialSourceRegValues(RegistryKey sourceLogKey, EventSourceCreationData sourceData) {
1165 if (String.IsNullOrEmpty(sourceData.MessageResourceFile))
1166 sourceLogKey.SetValue("EventMessageFile", GetDllPath(sourceData.MachineName), RegistryValueKind.ExpandString);
1168 sourceLogKey.SetValue("EventMessageFile", FixupPath(sourceData.MessageResourceFile), RegistryValueKind.ExpandString);
1170 if (!String.IsNullOrEmpty(sourceData.ParameterResourceFile))
1171 sourceLogKey.SetValue("ParameterMessageFile", FixupPath(sourceData.ParameterResourceFile), RegistryValueKind.ExpandString);
1173 if (!String.IsNullOrEmpty(sourceData.CategoryResourceFile)) {
1174 sourceLogKey.SetValue("CategoryMessageFile", FixupPath(sourceData.CategoryResourceFile), RegistryValueKind.ExpandString);
1175 sourceLogKey.SetValue("CategoryCount", sourceData.CategoryCount, RegistryValueKind.DWord);
1179 [ResourceExposure(ResourceScope.Machine)]
1180 [ResourceConsumption(ResourceScope.Machine)]
1181 private static string FixupPath(string path) {
1185 return Path.GetFullPath(path);
1188 // Format message in specific DLL. Return <null> on failure.
1189 internal static string TryFormatMessage(SafeLibraryHandle hModule, uint messageNum, string[] insertionStrings) {
1191 if (insertionStrings.Length == 0) {
1192 // UnsafeTryFromatMessage will set FORMAT_MESSAGE_IGNORE_INSERTS when calling into the OS
1193 // when there are no insertion strings, in this case we don't have to guard against insertionStrings
1194 // not having enough data since it is unused when FORMAT_MESSAGE_IGNORE_INSERTS is specified
1195 return UnsafeTryFormatMessage(hModule, messageNum, insertionStrings);
1198 // If you pass in an empty array UnsafeTryFormatMessage will just pull out the message.
1199 string formatString = UnsafeTryFormatMessage(hModule, messageNum, new string[0]);
1201 if (formatString == null) {
1205 int largestNumber = 0;
1207 for (int i = 0; i < formatString.Length; i++) {
1208 if (formatString[i] == '%') {
1209 // See if a number follows this, if so, grab the number.
1210 if(formatString.Length > i + 1) {
1211 StringBuilder sb = new StringBuilder();
1212 while (i + 1 < formatString.Length && Char.IsDigit(formatString[i + 1])) {
1213 sb.Append(formatString[i + 1]);
1217 // move over the non number character that broke us out of the loop
1220 if (sb.Length > 0) {
1222 if (Int32.TryParse(sb.ToString(), NumberStyles.None, CultureInfo.InvariantCulture, out num)) {
1223 largestNumber = Math.Max(largestNumber, num);
1230 // Replacement strings are 1 indexed.
1231 if (largestNumber > insertionStrings.Length) {
1232 string[] newStrings = new string[largestNumber];
1233 Array.Copy(insertionStrings, newStrings, insertionStrings.Length);
1234 for (int i = insertionStrings.Length; i < newStrings.Length; i++) {
1235 newStrings[i] = "%" + (i + 1);
1238 insertionStrings = newStrings;
1241 return UnsafeTryFormatMessage(hModule, messageNum, insertionStrings);
1244 // FormatMessageW will AV if you don't pass in enough format strings. If you call TryFormatMessage we ensure insertionStrings
1245 // is long enough. You don't want to call this directly unless you're sure insertionStrings is long enough!
1246 internal static string UnsafeTryFormatMessage(SafeLibraryHandle hModule, uint messageNum, string[] insertionStrings) {
1250 StringBuilder buf = new StringBuilder(1024);
1251 int flags = NativeMethods.FORMAT_MESSAGE_FROM_HMODULE | NativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY;
1253 IntPtr[] addresses = new IntPtr[insertionStrings.Length];
1254 GCHandle[] handles = new GCHandle[insertionStrings.Length];
1255 GCHandle stringsRoot = GCHandle.Alloc(addresses, GCHandleType.Pinned);
1257 // Make sure that we don't try to pass in a zero length array of addresses. If there are no insertion strings,
1258 // we'll use the FORMAT_MESSAGE_IGNORE_INSERTS flag .
1259 // If you change this behavior, make sure you look at TryFormatMessage which depends on this behavior!
1260 if (insertionStrings.Length == 0) {
1261 flags |= NativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS;
1265 for (int i=0; i<handles.Length; i++) {
1266 handles[i] = GCHandle.Alloc(insertionStrings[i], GCHandleType.Pinned);
1267 addresses[i] = handles[i].AddrOfPinnedObject();
1269 int lastError = NativeMethods.ERROR_INSUFFICIENT_BUFFER;
1270 while (msgLen == 0 && lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER) {
1271 msgLen = SafeNativeMethods.FormatMessage(
1281 lastError = Marshal.GetLastWin32Error();
1282 if (lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER)
1283 buf.Capacity = buf.Capacity * 2;
1288 msgLen = 0; // return empty on failure
1291 for (int i=0; i<handles.Length; i++) {
1292 if (handles[i].IsAllocated) handles[i].Free();
1298 msg = buf.ToString();
1299 // chop off a single CR/LF pair from the end if there is one. FormatMessage always appends one extra.
1300 if (msg.Length > 1 && msg[msg.Length-1] == '\n')
1301 msg = msg.Substring(0, msg.Length-2);
1308 // CharIsPrintable used to be Char.IsPrintable, but Jay removed it and
1309 // is forcing people to use the Unicode categories themselves. Copied
1311 private static bool CharIsPrintable(char c) {
1312 UnicodeCategory uc = Char.GetUnicodeCategory(c);
1313 return (!(uc == UnicodeCategory.Control) || (uc == UnicodeCategory.Format) ||
1314 (uc == UnicodeCategory.LineSeparator) || (uc == UnicodeCategory.ParagraphSeparator) ||
1315 (uc == UnicodeCategory.OtherNotAssigned));
1318 // SECREVIEW: Make sure this method catches all the strange cases.
1319 internal static bool ValidLogName(string logName, bool ignoreEmpty) {
1320 // No need to trim here since the next check will verify that there are no spaces.
1321 // We need to ignore the empty string as an invalid log name sometimes because it can
1322 // be passed in from our default constructor.
1323 if (logName.Length == 0 && !ignoreEmpty)
1326 //any space, backslash, asterisk, or question mark is bad
1327 //any non-printable characters are also bad
1328 foreach (char c in logName)
1329 if (!CharIsPrintable(c) || (c == '\\') || (c == '*') || (c == '?'))
1337 /// Writes an information type entry with the given message text to the event log.
1340 public void WriteEntry(string message) {
1341 WriteEntry(message, EventLogEntryType.Information, (short) 0, 0, null);
1346 public static void WriteEntry(string source, string message) {
1347 WriteEntry(source, message, EventLogEntryType.Information, (short) 0, 0, null);
1352 /// Writes an entry of the specified <see cref='System.Diagnostics.EventLogEntryType'/> to the event log. Valid types are
1353 /// <see langword='Error'/>, <see langword='Warning'/>, <see langword='Information'/>,
1354 /// <see langword='Success Audit'/>, and <see langword='Failure Audit'/>.
1357 public void WriteEntry(string message, EventLogEntryType type) {
1358 WriteEntry(message, type, (short) 0, 0, null);
1362 /// <para>[To be supplied.]</para>
1364 public static void WriteEntry(string source, string message, EventLogEntryType type) {
1365 WriteEntry(source, message, type, (short) 0, 0, null);
1370 /// Writes an entry of the specified <see cref='System.Diagnostics.EventLogEntryType'/>
1372 /// user-defined <paramref name="eventID"/>
1377 public void WriteEntry(string message, EventLogEntryType type, int eventID) {
1378 WriteEntry(message, type, eventID, 0, null);
1382 /// <para>[To be supplied.]</para>
1384 public static void WriteEntry(string source, string message, EventLogEntryType type, int eventID) {
1385 WriteEntry(source, message, type, eventID, 0, null);
1390 /// Writes an entry of the specified type with the
1391 /// user-defined <paramref name="eventID"/> and <paramref name="category"/>
1392 /// to the event log. The <paramref name="category"/>
1393 /// can be used by the event viewer to filter events in the log.
1396 public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) {
1397 WriteEntry(message, type, eventID, category, null);
1401 /// <para>[To be supplied.]</para>
1403 public static void WriteEntry(string source, string message, EventLogEntryType type, int eventID, short category) {
1404 WriteEntry(source, message, type, eventID, category, null);
1408 /// <para>[To be supplied.]</para>
1410 public static void WriteEntry(string source, string message, EventLogEntryType type, int eventID, short category,
1412 using (EventLogInternal log = new EventLogInternal("", ".", CheckAndNormalizeSourceName(source))) {
1413 log.WriteEntry(message, type, eventID, category, rawData);
1419 /// Writes an entry of the specified type with the
1420 /// user-defined <paramref name="eventID"/> and <paramref name="category"/> to the event log, and appends binary data to
1421 /// the message. The Event Viewer does not interpret this data; it
1422 /// displays raw data only in a combined hexadecimal and text format.
1425 public void WriteEntry(string message, EventLogEntryType type, int eventID, short category,
1428 m_underlyingEventLog.WriteEntry(message, type, eventID, category, rawData);
1432 public void WriteEvent(EventInstance instance, params Object[] values) {
1433 WriteEvent(instance, null, values);
1437 public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) {
1438 m_underlyingEventLog.WriteEvent(instance, data, values);
1441 public static void WriteEvent(string source, EventInstance instance, params Object[] values) {
1442 using (EventLogInternal log = new EventLogInternal("", ".", CheckAndNormalizeSourceName(source))) {
1443 log.WriteEvent(instance, null, values);
1447 public static void WriteEvent(string source, EventInstance instance, byte[] data, params Object[] values) {
1448 using (EventLogInternal log = new EventLogInternal("", ".", CheckAndNormalizeSourceName(source))) {
1449 log.WriteEvent(instance, data, values);
1453 // The EventLog.set_Source used to do some normalization and throw some exceptions. We mimic that behavior here.
1454 private static string CheckAndNormalizeSourceName(string source) {
1456 source = string.Empty;
1458 // this 254 limit is the max length of a registry key.
1459 if (source.Length + EventLogKey.Length > 254)
1460 throw new ArgumentException(SR.GetString(SR.ParameterTooLong, "source", 254 - EventLogKey.Length));