// // System.Diagnostics.EventLog.cs // // Authors: // Jonathan Pryor (jonpryor@vt.edu) // Andreas Nahr (ClassDevelopment@A-SoftTech.com) // Gert Driesen (drieseng@users.sourceforge.net) // // Copyright (C) 2002 // Copyright (C) 2003 Andreas Nahr // Copyright (C) 2006 Novell, Inc (http://www.novell.com) // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Diagnostics; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Globalization; using System.IO; using System.Runtime.InteropServices; namespace System.Diagnostics { [DefaultEvent ("EntryWritten")] [InstallerType (typeof (EventLogInstaller))] #if NET_2_0 [MonitoringDescription ("Represents an event log")] #else [Designer ("Microsoft.VisualStudio.Install.EventLogInstallableComponentDesigner, " + Consts.AssemblyMicrosoft_VisualStudio)] #endif public class EventLog : Component, ISupportInitialize { private string source; private string logName; private string machineName; private bool doRaiseEvents = false; private ISynchronizeInvoke synchronizingObject = null; // IMPORTANT: also update constants in EventLogTest internal const string LOCAL_FILE_IMPL = "local"; private const string WIN32_IMPL = "win32"; private const string NULL_IMPL = "null"; internal const string EVENTLOG_TYPE_VAR = "MONO_EVENTLOG_TYPE"; private EventLogImpl Impl; public EventLog() : this (string.Empty) { } public EventLog(string logName) : this (logName, ".") { } public EventLog(string logName, string machineName) : this (logName, machineName, string.Empty) { } public EventLog(string logName, string machineName, string source) { if (logName == null) { throw new ArgumentNullException ("logName"); } if (machineName == null || machineName.Trim ().Length == 0) #if NET_2_0 throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value '{0}' for" + " parameter 'machineName'.", machineName)); #else throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value {0} for" + " parameter MachineName.", machineName)); #endif this.source = source; this.machineName = machineName; this.logName = logName; Impl = CreateEventLogImpl (this); } [Browsable (false), DefaultValue (false)] [MonitoringDescription ("If enabled raises event when a log is written.")] public bool EnableRaisingEvents { get {return doRaiseEvents;} set { if (value == doRaiseEvents) return; if (value) Impl.EnableNotification (); else Impl.DisableNotification (); doRaiseEvents = value; } } [Browsable (false), DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [MonitoringDescription ("The entries in the log.")] public EventLogEntryCollection Entries { get {return new EventLogEntryCollection(Impl);} } [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)] [TypeConverter ("System.Diagnostics.Design.LogConverter, " + Consts.AssemblySystem_Design)] [MonitoringDescription ("Name of the log that is read and written.")] public string Log { get { if (source != null && source.Length > 0) return GetLogName (); return logName; } set { if (value == null) throw new ArgumentNullException ("value"); // log name is treated case-insensitively on all platforms if (string.Compare (logName, value, true) != 0) { logName = value; Reset (); } } } [Browsable (false)] public string LogDisplayName { get { return Impl.LogDisplayName; } } [ReadOnly (true), DefaultValue ("."), RecommendedAsConfigurable (true)] [MonitoringDescription ("Name of the machine that this log get written to.")] public string MachineName { get { return machineName; } set { if (value == null || value.Trim ().Length == 0) throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value {0} for" + " property MachineName.", value)); if (string.Compare (machineName, value, true) != 0) { Close (); machineName = value; } } } [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)] [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)] [MonitoringDescription ("The application name that writes the log.")] public string Source { get { return source; } set { if (value == null) value = string.Empty; // Source only affects eventlog implementation if Source was set // and no Log was set if (source == null || source.Length == 0 && (logName == null ||logName.Length == 0)) { source = value; } else if (string.Compare (source, value, true) != 0) { source = value; Reset (); } } } [Browsable (false), DefaultValue (null)] [MonitoringDescription ("An object that synchronizes event handler calls.")] public ISynchronizeInvoke SynchronizingObject { get {return synchronizingObject;} set {synchronizingObject = value;} } #if NET_2_0 [MonoTODO] [ComVisibleAttribute (false)] [Browsable (false)] public OverflowAction OverflowAction { get { return Impl.OverflowAction; } } [MonoTODO] [ComVisibleAttribute (false)] [Browsable (false)] public int MinimumRetentionDays { get { return Impl.MinimumRetentionDays; } } [MonoTODO] [ComVisibleAttribute (false)] [Browsable (false)] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] public long MaximumKilobytes { get { return Impl.MaximumKilobytes; } set { Impl.MaximumKilobytes = value; } } [MonoTODO] [ComVisible (false)] public void ModifyOverflowPolicy (OverflowAction action, int retentionDays) { Impl.ModifyOverflowPolicy (action, retentionDays); } [MonoTODO] [ComVisibleAttribute (false)] public void RegisterDisplayName (string resourceFile, long resourceId) { Impl.RegisterDisplayName (resourceFile, resourceId); } #endif public void BeginInit () { Impl.BeginInit(); } public void Clear () { string logName = Log; if (logName == null || logName.Length == 0) throw new ArgumentException ("Log property value has not been specified."); if (!EventLog.Exists (logName, MachineName)) throw new InvalidOperationException (string.Format ( CultureInfo.InvariantCulture, "Event Log '{0}'" + " does not exist on computer '{1}'.", logName, machineName)); Impl.Clear (); Reset (); } public void Close () { Impl.Close(); EnableRaisingEvents = false; } internal void Reset () { bool enableRaisingEvents = EnableRaisingEvents; Close (); EnableRaisingEvents = enableRaisingEvents; } public static void CreateEventSource (string source, string logName) { CreateEventSource (source, logName, "."); } #if NET_2_0 [Obsolete ("use CreateEventSource(EventSourceCreationData) instead")] #endif public static void CreateEventSource (string source, string logName, string machineName) { CreateEventSource (new EventSourceCreationData (source, logName, machineName)); } #if NET_2_0 [MonoNotSupported ("remote machine is not supported")] public #else private #endif static void CreateEventSource (EventSourceCreationData sourceData) { if (sourceData.Source == null || sourceData.Source.Length == 0) throw new ArgumentException ("Source property value has not been specified."); if (sourceData.LogName == null || sourceData.LogName.Length == 0) throw new ArgumentException ("Log property value has not been specified."); if (SourceExists (sourceData.Source, sourceData.MachineName)) throw new ArgumentException (string.Format (CultureInfo.InvariantCulture, "Source '{0}' already exists on '{1}'.", sourceData.Source, sourceData.MachineName)); EventLogImpl impl = CreateEventLogImpl (sourceData.LogName, sourceData.MachineName, sourceData.Source); impl.CreateEventSource (sourceData); } public static void Delete (string logName) { Delete (logName, "."); } [MonoNotSupported ("remote machine is not supported")] public static void Delete (string logName, string machineName) { if (machineName == null || machineName.Trim ().Length == 0) throw new ArgumentException ("Invalid format for argument" + " machineName."); if (logName == null || logName.Length == 0) throw new ArgumentException ("Log to delete was not specified."); EventLogImpl impl = CreateEventLogImpl (logName, machineName, string.Empty); impl.Delete (logName, machineName); } public static void DeleteEventSource (string source) { DeleteEventSource (source, "."); } [MonoNotSupported ("remote machine is not supported")] public static void DeleteEventSource (string source, string machineName) { if (machineName == null || machineName.Trim ().Length == 0) #if NET_2_0 throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value '{0}' for" + " parameter 'machineName'.", machineName)); #else throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value {0} for" + " parameter machineName.", machineName)); #endif EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName, source); impl.DeleteEventSource (source, machineName); } protected override void Dispose (bool disposing) { if (Impl != null) Impl.Dispose (disposing); } public void EndInit() { Impl.EndInit(); } public static bool Exists (string logName) { return Exists (logName, "."); } [MonoNotSupported ("remote machine is not supported")] public static bool Exists (string logName, string machineName) { if (machineName == null || machineName.Trim ().Length == 0) throw new ArgumentException ("Invalid format for argument machineName."); if (logName == null || logName.Length == 0) return false; EventLogImpl impl = CreateEventLogImpl (logName, machineName, string.Empty); return impl.Exists (logName, machineName); } public static EventLog[] GetEventLogs () { return GetEventLogs ("."); } [MonoNotSupported ("remote machine is not supported")] public static EventLog[] GetEventLogs (string machineName) { EventLogImpl impl = CreateEventLogImpl (new EventLog ()); return impl.GetEventLogs (machineName); } [MonoNotSupported ("remote machine is not supported")] public static string LogNameFromSourceName (string source, string machineName) { if (machineName == null || machineName.Trim ().Length == 0) #if NET_2_0 throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value '{0}' for" + " parameter 'MachineName'.", machineName)); #else throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value {0} for" + " parameter MachineName.", machineName)); #endif EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName, source); return impl.LogNameFromSourceName (source, machineName); } public static bool SourceExists (string source) { return SourceExists (source, "."); } [MonoNotSupported ("remote machine is not supported")] public static bool SourceExists (string source, string machineName) { if (machineName == null || machineName.Trim ().Length == 0) #if NET_2_0 throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value '{0}' for" + " parameter 'machineName'.", machineName)); #else throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "Invalid value {0} for" + " parameter machineName.", machineName)); #endif EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName, source); return impl.SourceExists (source, machineName); } public void WriteEntry (string message) { WriteEntry (message, EventLogEntryType.Information); } public void WriteEntry (string message, EventLogEntryType type) { WriteEntry (message, type, 0); } public void WriteEntry (string message, EventLogEntryType type, int eventID) { WriteEntry (message, type, eventID, 0); } public void WriteEntry (string message, EventLogEntryType type, int eventID, short category) { WriteEntry (message, type, eventID, category, null); } public void WriteEntry (string message, EventLogEntryType type, int eventID, short category, byte[] rawData) { WriteEntry (new string [] { message }, type, eventID, category, rawData); } public static void WriteEntry (string source, string message) { WriteEntry (source, message, EventLogEntryType.Information); } public static void WriteEntry (string source, string message, EventLogEntryType type) { WriteEntry (source, message, type, 0); } public static void WriteEntry (string source, string message, EventLogEntryType type, int eventID) { WriteEntry (source, message, type, eventID, 0); } public static void WriteEntry (string source, string message, EventLogEntryType type, int eventID, short category) { WriteEntry (source, message, type, eventID, category, null); } public static void WriteEntry (string source, string message, EventLogEntryType type, int eventID, short category, byte[] rawData) { using (EventLog eventLog = new EventLog ()) { eventLog.Source = source; eventLog.WriteEntry (message, type, eventID, category, rawData); } } #if NET_2_0 [ComVisible (false)] public void WriteEvent (EventInstance instance, params object [] values) { WriteEvent (instance, null, values); } [ComVisible (false)] public void WriteEvent (EventInstance instance, byte [] data, params object [] values) { if (instance == null) throw new ArgumentNullException ("instance"); string [] replacementStrings = null; if (values != null) { replacementStrings = new string [values.Length]; for (int i = 0; i < values.Length; i++) { object value = values [i]; if (value == null) replacementStrings [i] = string.Empty; else replacementStrings [i] = values [i].ToString (); } } else { replacementStrings = new string [0]; } WriteEntry (replacementStrings, instance.EntryType, instance .InstanceId, (short) instance.CategoryId, data); } public static void WriteEvent (string source, EventInstance instance, params object [] values) { WriteEvent (source, instance, null, values); } public static void WriteEvent (string source, EventInstance instance, byte [] data, params object [] values) { using (EventLog eventLog = new EventLog ()) { eventLog.Source = source; eventLog.WriteEvent (instance, data, values); } } #endif internal void OnEntryWritten (EventLogEntry newEntry) { if (doRaiseEvents && EntryWritten != null) EntryWritten (this, new EntryWrittenEventArgs (newEntry)); } [MonitoringDescription ("Raised for each EventLog entry written.")] public event EntryWrittenEventHandler EntryWritten; internal string GetLogName () { if (logName != null && logName.Length > 0) return logName; // if no log name has been set, then use source to determine name of log logName = LogNameFromSourceName (source, machineName); return logName; } private static EventLogImpl CreateEventLogImpl (string logName, string machineName, string source) { EventLog eventLog = new EventLog (logName, machineName, source); return CreateEventLogImpl (eventLog); } private static EventLogImpl CreateEventLogImpl (EventLog eventLog) { switch (EventLogImplType) { case LOCAL_FILE_IMPL: return new LocalFileEventLog (eventLog); case WIN32_IMPL: return new Win32EventLog (eventLog); case NULL_IMPL: return new NullEventLog (eventLog); default: // we should never get here throw new NotSupportedException (string.Format ( CultureInfo.InvariantCulture, "Eventlog implementation" + " '{0}' is not supported.", EventLogImplType)); } } private static bool Win32EventLogEnabled { get { return (Environment.OSVersion.Platform == PlatformID.Win32NT); } } // IMPORTANT: also modify corresponding property in EventLogTest private static string EventLogImplType { get { string implType = Environment.GetEnvironmentVariable (EVENTLOG_TYPE_VAR); if (implType == null) { if (Win32EventLogEnabled) return WIN32_IMPL; implType = NULL_IMPL; } else { if (Win32EventLogEnabled && string.Compare (implType, WIN32_IMPL, true) == 0) implType = WIN32_IMPL; else if (string.Compare (implType, NULL_IMPL, true) == 0) implType = NULL_IMPL; else if (string.Compare (implType, 0, LOCAL_FILE_IMPL, 0, LOCAL_FILE_IMPL.Length, true) == 0) implType = LOCAL_FILE_IMPL; else throw new NotSupportedException (string.Format ( CultureInfo.InvariantCulture, "Eventlog implementation" + " '{0}' is not supported.", implType)); } return implType; } } private void WriteEntry (string [] replacementStrings, EventLogEntryType type, long instanceID, short category, byte [] rawData) { if (Source.Length == 0) throw new ArgumentException ("Source property was not set" + "before writing to the event log."); if (!Enum.IsDefined (typeof (EventLogEntryType), type)) throw new InvalidEnumArgumentException ("type", (int) type, typeof (EventLogEntryType)); #if NET_2_0 ValidateEventID (instanceID); #endif if (!SourceExists (Source, MachineName)) { if (Log == null || Log.Length == 0) { Log = "Application"; } CreateEventSource (Source, Log, MachineName); #if ONLY_1_1 ValidateEventID (instanceID); #endif } else if (logName != null && logName.Length != 0) { #if ONLY_1_1 ValidateEventID (instanceID); #endif string actualLog = LogNameFromSourceName (Source, MachineName); if (string.Compare (logName, actualLog, true, CultureInfo.InvariantCulture) != 0) throw new ArgumentException (string.Format ( CultureInfo.InvariantCulture, "The source '{0}' is not" + " registered in log '{1}' (it is registered in log" + " '{2}'). The Source and Log properties must be" + " matched, or you may set Log to the empty string," + " and it will automatically be matched to the Source" + " property.", Source, logName, actualLog)); } #if ONLY_1_1 ValidateEventID (instanceID); #endif if (rawData == null) rawData = new byte [0]; Impl.WriteEntry (replacementStrings, type, (uint) instanceID, category, rawData); } private void ValidateEventID (long instanceID) { int eventID = GetEventID (instanceID); if (eventID < ushort.MinValue || eventID > ushort.MaxValue) throw new ArgumentException (string.Format (CultureInfo.InvariantCulture, "Invalid eventID value '{0}'. It must be in the range between" + " '{1}' and '{2}'.", instanceID, ushort.MinValue, ushort.MaxValue)); } internal static int GetEventID (long instanceID) { long inst = (instanceID < 0) ? -instanceID : instanceID; // MSDN: eventID equals the InstanceId with the top two bits masked int eventID = (int) (inst & 0x3fffffff); return (instanceID < 0) ? -eventID : eventID; } } }