BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System / System.Diagnostics / EventLog.cs
1 //
2 // System.Diagnostics.EventLog.cs
3 //
4 // Authors:
5 //      Jonathan Pryor (jonpryor@vt.edu)
6 //      Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //      Gert Driesen (drieseng@users.sourceforge.net)
8 //
9 // Copyright (C) 2002
10 // Copyright (C) 2003 Andreas Nahr
11 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
12 //
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Diagnostics;
36 using System.Collections;
37 using System.ComponentModel;
38 using System.ComponentModel.Design;
39 using System.Globalization;
40 using System.IO;
41 using System.Runtime.InteropServices;
42
43 namespace System.Diagnostics 
44 {
45         [DefaultEvent ("EntryWritten")]
46         [InstallerType (typeof (EventLogInstaller))]
47 #if NET_2_0
48         [MonitoringDescription ("Represents an event log")]
49 #else
50         [Designer ("Microsoft.VisualStudio.Install.EventLogInstallableComponentDesigner, " + Consts.AssemblyMicrosoft_VisualStudio)]
51 #endif
52         public class EventLog : Component, ISupportInitialize 
53         {
54                 private string source;
55                 private string logName;
56                 private string machineName;
57                 private bool doRaiseEvents = false;
58                 private ISynchronizeInvoke synchronizingObject = null;
59
60                 // IMPORTANT: also update constants in EventLogTest
61                 internal const string LOCAL_FILE_IMPL = "local";
62                 private const string WIN32_IMPL = "win32";
63                 private const string NULL_IMPL = "null";
64
65                 internal const string EVENTLOG_TYPE_VAR = "MONO_EVENTLOG_TYPE";
66
67                 private EventLogImpl Impl;
68
69                 public EventLog() : this (string.Empty)
70                 {
71                 }
72
73                 public EventLog(string logName) : this (logName, ".")
74                 {
75                 }
76
77                 public EventLog(string logName, string machineName)
78                         : this (logName, machineName, string.Empty)
79                 {
80                 }
81
82                 public EventLog(string logName, string machineName, string source)
83                 {
84                         if (logName == null) {
85                                 throw new ArgumentNullException ("logName");
86                         }
87                         if (machineName == null || machineName.Trim ().Length == 0)
88 #if NET_2_0
89                                 throw new ArgumentException (string.Format (
90                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
91                                         + " parameter 'machineName'.", machineName));
92 #else
93                                 throw new ArgumentException (string.Format (
94                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
95                                         + " parameter MachineName.", machineName));
96 #endif
97
98                         this.source = source;
99                         this.machineName = machineName;
100                         this.logName = logName;
101
102                         Impl = CreateEventLogImpl (this);
103                 }
104
105                 [Browsable (false), DefaultValue (false)]
106                 [MonitoringDescription ("If enabled raises event when a log is written.")]
107                 public bool EnableRaisingEvents {
108                         get {return doRaiseEvents;}
109                         set {
110                                 if (value == doRaiseEvents)
111                                         return;
112
113                                 if (value)
114                                         Impl.EnableNotification ();
115                                 else
116                                         Impl.DisableNotification ();
117                                 doRaiseEvents = value;
118                         }
119                 }
120
121                 [Browsable (false), DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
122                 [MonitoringDescription ("The entries in the log.")]
123                 public EventLogEntryCollection Entries {
124                         get {return new EventLogEntryCollection(Impl);}
125                 }
126
127                 [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)]
128                 [TypeConverter ("System.Diagnostics.Design.LogConverter, " + Consts.AssemblySystem_Design)]
129                 [MonitoringDescription ("Name of the log that is read and written.")]
130                 public string Log {
131                         get {
132                                 if (source != null && source.Length > 0)
133                                         return GetLogName ();
134                                 return logName;
135                         }
136                         set {
137                                 if (value == null)
138                                         throw new ArgumentNullException ("value");
139
140                                 // log name is treated case-insensitively on all platforms
141                                 if (string.Compare (logName, value, true) != 0) {
142                                         logName = value;
143                                         Reset ();
144                                 }
145                         }
146                 }
147
148                 [Browsable (false)]
149                 public string LogDisplayName {
150                         get { return Impl.LogDisplayName; }
151                 }
152
153                 [ReadOnly (true), DefaultValue ("."), RecommendedAsConfigurable (true)]
154                 [MonitoringDescription ("Name of the machine that this log get written to.")]
155                 public string MachineName {
156                         get { return machineName; }
157                         set {
158                                 if (value == null || value.Trim ().Length == 0)
159                                         throw new ArgumentException (string.Format (
160                                                 CultureInfo.InvariantCulture, "Invalid value {0} for"
161                                                 + " property MachineName.", value));
162
163                                 if (string.Compare (machineName, value, true) != 0) {
164                                         Close ();
165                                         machineName = value;
166                                 }
167                         }
168                 }
169
170                 [ReadOnly (true), DefaultValue (""), RecommendedAsConfigurable (true)]
171                 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
172                 [MonitoringDescription ("The application name that writes the log.")]
173                 public string Source {
174                         get { return source; }
175                         set {
176                                 if (value == null)
177                                         value = string.Empty;
178
179                                 // Source only affects eventlog implementation if Source was set
180                                 // and no Log was set
181                                 if (source == null || source.Length == 0 && (logName == null ||logName.Length == 0)) {
182                                         source = value;
183                                 } else if (string.Compare (source, value, true) != 0) {
184                                         source = value;
185                                         Reset ();
186                                 }
187                         }
188                 }
189
190                 [Browsable (false), DefaultValue (null)]
191                 [MonitoringDescription ("An object that synchronizes event handler calls.")]
192                 public ISynchronizeInvoke SynchronizingObject {
193                         get {return synchronizingObject;}
194                         set {synchronizingObject = value;}
195                 }
196
197 #if NET_2_0
198                 [MonoTODO]
199                 [ComVisibleAttribute (false)]
200                 [Browsable (false)]
201                 public OverflowAction OverflowAction {
202                         get { return Impl.OverflowAction; }
203                 }
204
205                 [MonoTODO]
206                 [ComVisibleAttribute (false)]
207                 [Browsable (false)]
208                 public int MinimumRetentionDays {
209                         get { return Impl.MinimumRetentionDays; }
210                 }
211
212                 [MonoTODO]
213                 [ComVisibleAttribute (false)]
214                 [Browsable (false)]
215                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
216                 public long MaximumKilobytes {
217                         get { return Impl.MaximumKilobytes; }
218                         set { Impl.MaximumKilobytes = value; }
219                 }
220
221                 [MonoTODO]
222                 [ComVisible (false)]
223                 public void ModifyOverflowPolicy (OverflowAction action, int retentionDays)
224                 {
225                         Impl.ModifyOverflowPolicy (action, retentionDays);
226                 }
227
228                 [MonoTODO]
229                 [ComVisibleAttribute (false)]
230                 public void RegisterDisplayName (string resourceFile, long resourceId)
231                 {
232                         Impl.RegisterDisplayName (resourceFile, resourceId);
233                 }
234 #endif
235
236                 public void BeginInit ()
237                 {
238                         Impl.BeginInit();
239                 }
240
241                 public void Clear ()
242                 {
243                         string logName = Log;
244                         if (logName == null || logName.Length == 0)
245                                 throw new ArgumentException ("Log property value has not been specified.");
246
247                         if (!EventLog.Exists (logName, MachineName))
248                                 throw new InvalidOperationException (string.Format (
249                                         CultureInfo.InvariantCulture, "Event Log '{0}'"
250                                         + " does not exist on computer '{1}'.", logName,
251                                         machineName));
252
253                         Impl.Clear ();
254                         Reset ();
255                 }
256
257                 public void Close ()
258                 {
259                         Impl.Close();
260                         EnableRaisingEvents = false;
261                 }
262
263                 internal void Reset ()
264                 {
265                         bool enableRaisingEvents = EnableRaisingEvents;
266                         Close ();
267                         EnableRaisingEvents = enableRaisingEvents;
268                 }
269
270                 public static void CreateEventSource (string source, string logName)
271                 {
272                         CreateEventSource (source, logName, ".");
273                 }
274
275 #if NET_2_0
276                 [Obsolete ("use CreateEventSource(EventSourceCreationData) instead")]
277 #endif
278                 public static void CreateEventSource (string source, 
279                         string logName, 
280                         string machineName)
281                 {
282                         CreateEventSource (new EventSourceCreationData (source, logName,
283                                 machineName));
284                 }
285
286 #if NET_2_0
287                 [MonoNotSupported ("remote machine is not supported")]
288                 public
289 #else
290                 private
291 #endif
292                 static void CreateEventSource (EventSourceCreationData sourceData)
293                 {
294                         if (sourceData.Source == null || sourceData.Source.Length == 0)
295                                 throw new ArgumentException ("Source property value has not been specified.");
296
297                         if (sourceData.LogName == null || sourceData.LogName.Length == 0)
298                                 throw new ArgumentException ("Log property value has not been specified.");
299
300                         if (SourceExists (sourceData.Source, sourceData.MachineName))
301                                 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
302                                         "Source '{0}' already exists on '{1}'.", sourceData.Source,
303                                         sourceData.MachineName));
304
305                         EventLogImpl impl = CreateEventLogImpl (sourceData.LogName,
306                                 sourceData.MachineName, sourceData.Source);
307                         impl.CreateEventSource (sourceData);
308                 }
309
310                 public static void Delete (string logName)
311                 {
312                         Delete (logName, ".");
313                 }
314
315                 [MonoNotSupported ("remote machine is not supported")]
316                 public static void Delete (string logName, string machineName)
317                 {
318                         if (machineName == null || machineName.Trim ().Length == 0)
319                                 throw new ArgumentException ("Invalid format for argument"
320                                         + " machineName.");
321
322                         if (logName == null || logName.Length == 0)
323                                 throw new ArgumentException ("Log to delete was not specified.");
324
325                         EventLogImpl impl = CreateEventLogImpl (logName, machineName, 
326                                 string.Empty);
327                         impl.Delete (logName, machineName);
328                 }
329
330                 public static void DeleteEventSource (string source)
331                 {
332                         DeleteEventSource (source, ".");
333                 }
334
335                 [MonoNotSupported ("remote machine is not supported")]
336                 public static void DeleteEventSource (string source, string machineName)
337                 {
338                         if (machineName == null || machineName.Trim ().Length == 0)
339 #if NET_2_0
340                                 throw new ArgumentException (string.Format (
341                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
342                                         + " parameter 'machineName'.", machineName));
343 #else
344                                 throw new ArgumentException (string.Format (
345                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
346                                         + " parameter machineName.", machineName));
347 #endif
348
349                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
350                                 source);
351                         impl.DeleteEventSource (source, machineName);
352                 }
353
354                 protected override void Dispose (bool disposing)
355                 {
356                         if (Impl != null)
357                                 Impl.Dispose (disposing);
358                 }
359
360                 public void EndInit()
361                 {
362                         Impl.EndInit();
363                 }
364
365                 public static bool Exists (string logName)
366                 {
367                         return Exists (logName, ".");
368                 }
369
370                 [MonoNotSupported ("remote machine is not supported")]
371                 public static bool Exists (string logName, string machineName)
372                 {
373                         if (machineName == null || machineName.Trim ().Length == 0)
374                                 throw new ArgumentException ("Invalid format for argument machineName.");
375
376                         if (logName == null || logName.Length == 0)
377                                 return false; 
378
379                         EventLogImpl impl = CreateEventLogImpl (logName, machineName,
380                                 string.Empty);
381                         return impl.Exists (logName, machineName);
382                 }
383
384                 public static EventLog[] GetEventLogs ()
385                 {
386                         return GetEventLogs (".");
387                 }
388
389                 [MonoNotSupported ("remote machine is not supported")]
390                 public static EventLog[] GetEventLogs (string machineName)
391                 {
392                         EventLogImpl impl = CreateEventLogImpl (new EventLog ());
393                         return impl.GetEventLogs (machineName);
394                 }
395
396                 [MonoNotSupported ("remote machine is not supported")]
397                 public static string LogNameFromSourceName (string source, string machineName)
398                 {
399                         if (machineName == null || machineName.Trim ().Length == 0)
400 #if NET_2_0
401                                 throw new ArgumentException (string.Format (
402                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
403                                         + " parameter 'MachineName'.", machineName));
404 #else
405                                 throw new ArgumentException (string.Format (
406                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
407                                         + " parameter MachineName.", machineName));
408 #endif
409
410                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
411                                 source);
412                         return impl.LogNameFromSourceName (source, machineName);
413                 }
414
415                 public static bool SourceExists (string source)
416                 {
417                         return SourceExists (source, ".");
418                 }
419
420                 [MonoNotSupported ("remote machine is not supported")]
421                 public static bool SourceExists (string source, string machineName)
422                 {
423                         if (machineName == null || machineName.Trim ().Length == 0)
424 #if NET_2_0
425                                 throw new ArgumentException (string.Format (
426                                         CultureInfo.InvariantCulture, "Invalid value '{0}' for"
427                                         + " parameter 'machineName'.", machineName));
428 #else
429                                 throw new ArgumentException (string.Format (
430                                         CultureInfo.InvariantCulture, "Invalid value {0} for"
431                                         + " parameter machineName.", machineName));
432 #endif
433
434                         EventLogImpl impl = CreateEventLogImpl (string.Empty, machineName,
435                                 source);
436                         return impl.SourceExists (source, machineName);
437                 }
438
439                 public void WriteEntry (string message)
440                 {
441                         WriteEntry (message, EventLogEntryType.Information);
442                 }
443
444                 public void WriteEntry (string message, EventLogEntryType type)
445                 {
446                         WriteEntry (message, type, 0);
447                 }
448
449                 public void WriteEntry (string message, EventLogEntryType type, 
450                         int eventID)
451                 {
452                         WriteEntry (message, type, eventID, 0);
453                 }
454
455                 public void WriteEntry (string message, EventLogEntryType type, 
456                         int eventID,
457                         short category)
458                 {
459                         WriteEntry (message, type, eventID, category, null);
460                 }
461
462                 public void WriteEntry (string message, EventLogEntryType type, 
463                         int eventID,
464                         short category, byte[] rawData)
465                 {
466                         WriteEntry (new string [] { message }, type, eventID,
467                                 category, rawData);
468                 }
469
470                 public static void WriteEntry (string source, string message)
471                 {
472                         WriteEntry (source, message, EventLogEntryType.Information);
473                 }
474
475                 public static void WriteEntry (string source, string message, 
476                         EventLogEntryType type)
477                 {
478                         WriteEntry (source, message, type, 0);
479                 }
480
481                 public static void WriteEntry (string source, string message, 
482                         EventLogEntryType type, int eventID)
483                 {
484                         WriteEntry (source, message, type, eventID, 0);
485                 }
486
487                 public static void WriteEntry (string source, string message, 
488                         EventLogEntryType type, int eventID, short category)
489                 {
490                         WriteEntry (source, message, type, eventID, category, null);
491                 }
492
493                 public static void WriteEntry (string source, string message, 
494                         EventLogEntryType type, int eventID, short category, 
495                         byte[] rawData)
496                 {
497                         using (EventLog eventLog = new EventLog ()) {
498                                 eventLog.Source = source;
499                                 eventLog.WriteEntry (message, type, eventID, category, rawData);
500                         }
501                 }
502
503 #if NET_2_0
504                 [ComVisible (false)]
505                 public void WriteEvent (EventInstance instance, params object [] values)
506                 {
507                         WriteEvent (instance, null, values);
508                 }
509
510                 [ComVisible (false)]
511                 public void WriteEvent (EventInstance instance, byte [] data, params object [] values)
512                 {
513                         if (instance == null)
514                                 throw new ArgumentNullException ("instance");
515
516                         string [] replacementStrings = null;
517                         if (values != null) {
518                                 replacementStrings = new string [values.Length];
519                                 for (int i = 0; i < values.Length; i++) {
520                                         object value = values [i];
521                                         if (value == null)
522                                                 replacementStrings [i] = string.Empty;
523                                         else
524                                                 replacementStrings [i] = values [i].ToString ();
525                                 }
526                         } else {
527                                 replacementStrings = new string [0];
528                         }
529
530                         WriteEntry (replacementStrings, instance.EntryType, instance
531                                 .InstanceId, (short) instance.CategoryId, data);
532                 }
533
534                 public static void WriteEvent (string source, EventInstance instance, params object [] values)
535                 {
536                         WriteEvent (source, instance, null, values);
537                 }
538
539                 public static void WriteEvent (string source, EventInstance instance, byte [] data, params object [] values)
540                 {
541                         using (EventLog eventLog = new EventLog ()) {
542                                 eventLog.Source = source;
543                                 eventLog.WriteEvent (instance, data, values);
544                         }
545                 }
546 #endif
547
548                 internal void OnEntryWritten (EventLogEntry newEntry)
549                 {
550                         if (doRaiseEvents && EntryWritten != null)
551                                 EntryWritten (this, new EntryWrittenEventArgs (newEntry));
552                 }
553
554                 [MonitoringDescription ("Raised for each EventLog entry written.")]
555                 public event EntryWrittenEventHandler EntryWritten;
556
557                 internal string GetLogName ()
558                 {
559                         if (logName != null && logName.Length > 0)
560                                 return logName;
561
562                         // if no log name has been set, then use source to determine name of log
563                         logName = LogNameFromSourceName (source, machineName);
564                         return logName;
565                 }
566
567                 private static EventLogImpl CreateEventLogImpl (string logName, string machineName, string source)
568                 {
569                         EventLog eventLog = new EventLog (logName, machineName, source);
570                         return CreateEventLogImpl (eventLog);
571                 }
572
573                 private static EventLogImpl CreateEventLogImpl (EventLog eventLog)
574                 {
575                         switch (EventLogImplType) {
576                         case LOCAL_FILE_IMPL:
577                                 return new LocalFileEventLog (eventLog);
578                         case WIN32_IMPL:
579                                 return new Win32EventLog (eventLog);
580                         case NULL_IMPL:
581                                 return new NullEventLog (eventLog);
582                         default:
583                                 // we should never get here
584                                 throw new NotSupportedException (string.Format (
585                                         CultureInfo.InvariantCulture, "Eventlog implementation"
586                                         + " '{0}' is not supported.", EventLogImplType));
587                         }
588                 }
589
590                 private static bool Win32EventLogEnabled {
591                         get {
592                                 return (Environment.OSVersion.Platform == PlatformID.Win32NT);
593                         }
594                 }
595
596                 // IMPORTANT: also modify corresponding property in EventLogTest
597                 private static string EventLogImplType {
598                         get {
599                                 string implType = Environment.GetEnvironmentVariable (EVENTLOG_TYPE_VAR);
600                                 if (implType == null) {
601                                         if (Win32EventLogEnabled)
602                                                 return WIN32_IMPL;
603                                         implType = NULL_IMPL;
604                                 } else {
605                                         if (Win32EventLogEnabled && string.Compare (implType, WIN32_IMPL, true) == 0)
606                                                 implType = WIN32_IMPL;
607                                         else if (string.Compare (implType, NULL_IMPL, true) == 0)
608                                                 implType = NULL_IMPL;
609                                         else if (string.Compare (implType, 0, LOCAL_FILE_IMPL, 0, LOCAL_FILE_IMPL.Length, true) == 0)
610                                                 implType = LOCAL_FILE_IMPL;
611                                         else
612                                                 throw new NotSupportedException (string.Format (
613                                                         CultureInfo.InvariantCulture, "Eventlog implementation"
614                                                         + " '{0}' is not supported.", implType));
615                                 }
616                                 return implType;
617                         }
618                 }
619
620                 private void WriteEntry (string [] replacementStrings, EventLogEntryType type, long instanceID, short category, byte [] rawData)
621                 {
622                         if (Source.Length == 0)
623                                 throw new ArgumentException ("Source property was not set"
624                                         + "before writing to the event log.");
625
626                         if (!Enum.IsDefined (typeof (EventLogEntryType), type))
627                                 throw new InvalidEnumArgumentException ("type", (int) type,
628                                         typeof (EventLogEntryType));
629
630 #if NET_2_0
631                         ValidateEventID (instanceID);
632 #endif
633
634                         if (!SourceExists (Source, MachineName)) {
635                                 if (Log == null || Log.Length == 0) {
636                                         Log = "Application";
637                                 }
638                                 CreateEventSource (Source, Log, MachineName);
639
640 #if ONLY_1_1
641                                 ValidateEventID (instanceID);
642 #endif
643                         } else if (logName != null && logName.Length != 0) {
644 #if ONLY_1_1
645                                 ValidateEventID (instanceID);
646 #endif
647                                 string actualLog = LogNameFromSourceName (Source, MachineName);
648                                 if (string.Compare (logName, actualLog, true, CultureInfo.InvariantCulture) != 0)
649                                         throw new ArgumentException (string.Format (
650                                                 CultureInfo.InvariantCulture, "The source '{0}' is not"
651                                                 + " registered in log '{1}' (it is registered in log"
652                                                 + " '{2}'). The Source and Log properties must be"
653                                                 + " matched, or you may set Log to the empty string,"
654                                                 + " and it will automatically be matched to the Source"
655                                                 + " property.", Source, logName, actualLog));
656                         }
657
658 #if ONLY_1_1
659                         ValidateEventID (instanceID);
660 #endif
661
662                         if (rawData == null)
663                                 rawData = new byte [0];
664
665                         Impl.WriteEntry (replacementStrings, type, (uint) instanceID, category, rawData);
666                 }
667
668                 private void ValidateEventID (long instanceID)
669                 {
670                         int eventID = GetEventID (instanceID);
671                         if (eventID < ushort.MinValue || eventID > ushort.MaxValue)
672                                 throw new ArgumentException (string.Format (CultureInfo.InvariantCulture,
673                                         "Invalid eventID value '{0}'. It must be in the range between"
674                                         + " '{1}' and '{2}'.", instanceID, ushort.MinValue, ushort.MaxValue));
675                 }
676
677                 internal static int GetEventID (long instanceID)
678                 {
679                         long inst = (instanceID < 0) ? -instanceID : instanceID;
680
681                         // MSDN: eventID equals the InstanceId with the top two bits masked
682                         int eventID = (int) (inst & 0x3fffffff);
683                         return (instanceID < 0) ? -eventID : eventID;
684                 }
685         }
686 }