Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System / services / monitoring / system / diagnosticts / EventLog.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="EventLog.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 //#define RETRY_ON_ALL_ERRORS
8
9 namespace System.Diagnostics {
10     using System.Text;
11     using System.Text.RegularExpressions;
12     using System.Threading;
13     using System.Runtime.InteropServices;
14     using System.ComponentModel;
15     using System.Diagnostics;
16     using System;
17     using Microsoft.Win32;
18     using Microsoft.Win32.SafeHandles;
19     using System.IO;
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;
30
31     /// <devdoc>
32     ///    <para>
33     ///       Provides interaction with Windows 2000 event logs.
34     ///    </para>
35     /// </devdoc>
36     [
37     DefaultEvent("EntryWritten"),
38     InstallerType("System.Diagnostics.EventLogInstaller, " + AssemblyRef.SystemConfigurationInstall),
39     MonitoringDescription(SR.EventLogDesc)
40     ]
41     public class EventLog : Component, ISupportInitialize {
42
43         private const string EventLogKey = "SYSTEM\\CurrentControlSet\\Services\\EventLog";
44         internal const string DllName = "EventLogMessages.dll";
45         private const string eventLogMutexName = "netfxeventlog.1.0";
46
47         private const int DefaultMaxSize = 512 * 1024;
48         private const int DefaultRetention = 7 * SecondsPerDay;
49         private const int SecondsPerDay = 60 * 60 * 24;
50
51         private EventLogInternal m_underlyingEventLog;
52         
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;
56
57         private static bool SkipRegPatch {
58             get {
59                 if (!s_CheckedOsVersion) {
60                     OperatingSystem os = Environment.OSVersion;
61                     s_SkipRegPatch = (os.Platform == PlatformID.Win32NT) && (os.Version.Major > 5);
62                     s_CheckedOsVersion = true;
63                 }
64                 return s_SkipRegPatch;
65             }
66         }
67
68         internal static PermissionSet _UnsafeGetAssertPermSet() {
69             // SEC_NOTE: All callers should already be guarded by EventLogPermission demand.
70             PermissionSet permissionSet = new PermissionSet(PermissionState.None);
71
72             // We need RegistryPermission 
73             RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
74             permissionSet.AddPermission(registryPermission);
75
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);
80
81             // For remote machine registry access UnmanagdCodePermission is required.
82             SecurityPermission securityPermission = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
83             permissionSet.AddPermission(securityPermission);
84
85             return permissionSet;
86         }
87
88         /// <devdoc>
89         ///    <para>
90         ///       Initializes a new instance of the <see cref='System.Diagnostics.EventLog'/>
91         ///       class.
92         ///    </para>
93         /// </devdoc>
94         public EventLog() : this("", ".", "") {
95         }
96
97         /// <devdoc>
98         ///    <para>[To be supplied.]</para>
99         /// </devdoc>
100         public EventLog(string logName) : this(logName, ".", "") {
101         }
102
103         /// <devdoc>
104         ///    <para>[To be supplied.]</para>
105         /// </devdoc>
106         public EventLog(string logName, string machineName) : this(logName, machineName, "") {
107         }
108
109         /// <devdoc>
110         ///    <para>[To be supplied.]</para>
111         /// </devdoc>
112         public EventLog(string logName, string machineName, string source) {
113             m_underlyingEventLog = new EventLogInternal(logName, machineName, source, this);
114         }
115
116         /// <devdoc>
117         ///    <para>
118         ///       Gets the contents of the event log.
119         ///    </para>
120         /// </devdoc>
121         [Browsable(false)]
122         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
123         [MonitoringDescription(SR.LogEntries)]
124         public EventLogEntryCollection Entries {
125             get {
126                 return m_underlyingEventLog.Entries;
127             }
128         }
129
130         /// <devdoc>
131         ///    <para>
132         ///    </para>
133         /// </devdoc>
134         [Browsable(false)]
135         public string LogDisplayName {
136         [ResourceExposure(ResourceScope.Machine)]
137         [ResourceConsumption(ResourceScope.Machine)]
138             get {
139                 return m_underlyingEventLog.LogDisplayName;
140             }
141         }
142
143         /// <devdoc>
144         ///    <para>
145         ///       Gets or sets the name of the log to read from and write to.
146         ///    </para>
147         /// </devdoc>
148         [TypeConverter("System.Diagnostics.Design.LogConverter, " + AssemblyRef.SystemDesign)]
149         [ReadOnly(true)]
150         [MonitoringDescription(SR.LogLog)]
151         [DefaultValue("")]
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")]
155         public string Log {
156             get {
157                 return m_underlyingEventLog.Log;
158             }
159             set {
160                 EventLogInternal newLog = new EventLogInternal(value, m_underlyingEventLog.MachineName, m_underlyingEventLog.Source, this);
161                 EventLogInternal oldLog = m_underlyingEventLog;
162
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;
169                 }
170                 m_underlyingEventLog = newLog;
171                 oldLog.Close();
172             }
173         }
174
175         /// <devdoc>
176         ///    <para>
177         ///       Gets or sets the name of the computer on which to read or write events.
178         ///    </para>
179         /// </devdoc>
180         [ReadOnly(true)]
181         [MonitoringDescription(SR.LogMachineName)]
182         [DefaultValue(".")]
183         [SettingsBindable(true)]
184         [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "[....]: By design, see justification above assert")]
185         public string MachineName {
186             get {
187                 return m_underlyingEventLog.MachineName;
188             }
189             set {
190                 EventLogInternal newLog = new EventLogInternal(m_underlyingEventLog.logName, value, m_underlyingEventLog.sourceName, this);
191                 EventLogInternal oldLog = m_underlyingEventLog;
192
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;
199                 }
200                 m_underlyingEventLog = newLog;
201                 oldLog.Close();
202             }
203         }
204
205         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
206         [Browsable(false)]
207         [ComVisible(false)]
208         public long MaximumKilobytes {
209             get {
210                 return m_underlyingEventLog.MaximumKilobytes;
211             }
212
213             [ResourceExposure(ResourceScope.None)]
214             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
215             set {
216                 m_underlyingEventLog.MaximumKilobytes = value;
217             }
218         }
219
220         [Browsable(false)]
221         [ComVisible(false)]
222         public OverflowAction OverflowAction {
223             get {
224                 return m_underlyingEventLog.OverflowAction;
225             }
226         }
227
228         [Browsable(false)]
229         [ComVisible(false)]
230         public int MinimumRetentionDays {
231             get {
232                 return m_underlyingEventLog.MinimumRetentionDays;
233             }
234         }
235
236         // EventLogInternal needs to know if the component is in design mode but
237         // the DesignMode property is protected.
238         internal bool ComponentDesignMode {
239             get {
240                 return this.DesignMode;
241             }
242         }
243
244         // Expose for EventLogInternal
245         internal object ComponentGetService(Type service) {
246             return GetService(service);
247         }
248
249         /// <devdoc>
250         /// </devdoc>
251         [Browsable(false)]
252         [MonitoringDescription(SR.LogMonitoring)]
253         [DefaultValue(false)]
254         public bool EnableRaisingEvents {
255             get {
256                 return m_underlyingEventLog.EnableRaisingEvents;
257             }
258             set {
259                 m_underlyingEventLog.EnableRaisingEvents = value;
260             }
261         }
262
263         /// <devdoc>
264         ///    <para>
265         ///       Represents the object used to marshal the event handler
266         ///       calls issued as a result of an <see cref='System.Diagnostics.EventLog'/>
267         ///       change.
268         ///    </para>
269         /// </devdoc>
270         [Browsable(false)]
271         [DefaultValue(null)]
272         [MonitoringDescription(SR.LogSynchronizingObject)]
273         public ISynchronizeInvoke SynchronizingObject {
274         [HostProtection(Synchronization=true)]
275             get {
276                 return m_underlyingEventLog.SynchronizingObject;
277             }
278
279             set {
280                 m_underlyingEventLog.SynchronizingObject = value;
281             }
282         }
283
284         /// <devdoc>
285         ///    <para>
286         ///       Gets or
287         ///       sets the application name (source name) to register and use when writing to the event log.
288         ///    </para>
289         /// </devdoc>
290         [ReadOnly(true)]
291         [TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign)]
292         [MonitoringDescription(SR.LogSource)]
293         [DefaultValue("")]
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 {
298             get {
299                 return m_underlyingEventLog.Source;
300             }
301             set {
302                 EventLogInternal newLog = new EventLogInternal(m_underlyingEventLog.Log, m_underlyingEventLog.MachineName, CheckAndNormalizeSourceName(value), this);
303                 EventLogInternal oldLog = m_underlyingEventLog;
304
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;
311                 }
312                 m_underlyingEventLog = newLog;
313                 oldLog.Close();
314             }
315         }
316
317
318         /// <devdoc>
319         ///    <para>
320         ///       Occurs when an entry is written to the event log.
321         ///    </para>
322         /// </devdoc>
323         [MonitoringDescription(SR.LogEntryWritten)]
324         public event EntryWrittenEventHandler EntryWritten {
325             add {
326                 m_underlyingEventLog.EntryWritten += value;
327             }
328             remove {
329                 m_underlyingEventLog.EntryWritten -= value;
330             }
331         }
332
333         /// <devdoc>
334         /// </devdoc>
335         public void BeginInit() {
336             m_underlyingEventLog.BeginInit();
337         }
338
339         /// <devdoc>
340         ///    <para>
341         ///       Clears
342         ///       the event log by removing all entries from it.
343         ///    </para>
344         /// </devdoc>
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();
349         }
350
351         /// <devdoc>
352         ///    <para>
353         ///       Closes the event log and releases read and write handles.
354         ///    </para>
355         /// </devdoc>
356         [ResourceExposure(ResourceScope.None)]
357         public void Close() {
358             m_underlyingEventLog.Close();
359         }
360
361         /// <devdoc>
362         ///    <para> Establishes an application, using the
363         ///       specified <see cref='System.Diagnostics.EventLog.Source'/> , as a valid event source for
364         ///       writing entries
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>
368         /// </devdoc>
369         public static void CreateEventSource(string source, string logName) {
370             CreateEventSource(new EventSourceCreationData(source, logName, "."));
371         }
372
373         /// <devdoc>
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>
379         /// </devdoc>
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));
383         }
384
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");
390
391             string logName = sourceData.LogName;
392             string source = sourceData.Source;
393             string machineName = sourceData.MachineName;
394
395             // verify parameters
396             Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Checking arguments");
397             if (!SyntaxCheck.CheckMachineName(machineName)) {
398                 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
399             }
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));
408
409             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
410             permission.Demand();
411
412             Mutex mutex = null;
413             RuntimeHelpers.PrepareConstrainedRegions();
414             try {
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
422                     // duplicates.
423                     if (".".Equals(machineName))
424                         throw new ArgumentException(SR.GetString(SR.LocalSourceAlreadyExists, source));
425                     else
426                         throw new ArgumentException(SR.GetString(SR.SourceAlreadyExists, source, machineName));
427                 }
428
429                 Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting DllPath");
430
431                 //SECREVIEW: Note that EventLog permission is demanded above.
432                 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
433                 permissionSet.Assert();
434                 
435                 RegistryKey baseKey = null;
436                 RegistryKey eventKey = null;
437                 RegistryKey logKey = null;
438                 RegistryKey sourceLogKey = null;
439                 RegistryKey sourceKey = null;
440                 try {
441                     Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting local machine regkey");
442                     if (machineName == ".")
443                         baseKey = Registry.LocalMachine;
444                     else
445                         baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName);
446
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));
451                         else
452                             throw new InvalidOperationException(SR.GetString(SR.LocalRegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source));
453                     }
454
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) {
461
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));
468
469                         string sameLogName = FindSame8FirstCharsLog(eventKey, logName);
470                         if ( sameLogName != null )
471                             throw new ArgumentException(SR.GetString(SR.DuplicateLogName, logName, sameLogName));
472                     }
473
474                     bool createLogKey = (logKey == null);
475                     if (createLogKey) {
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));
482                             else
483                                 throw new ArgumentException(SR.GetString(SR.LogAlreadyExistsAsSource, logName, machineName));
484                         }
485
486                         logKey = eventKey.CreateSubKey(logName);
487                                                                         
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
492
493                         if (!SkipRegPatch) 
494                             logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
495
496                         SetSpecialLogRegValues(logKey, logName);
497
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);
502                     }
503
504                     if (logName != source) {
505                         if (!createLogKey) {
506                             SetSpecialLogRegValues(logKey, logName);
507                                                         
508                             if (!SkipRegPatch) {
509                                 string[] sources = logKey.GetValue("Sources") as string[];
510                                 if (sources == null)
511                                     logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
512                                 else {
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);
521                                     }
522                                 }
523                             }
524                         }
525
526                         sourceKey = logKey.CreateSubKey(source);
527                         SetSpecialSourceRegValues(sourceKey, sourceData);
528                     }
529                 }
530                 finally {
531                     if (baseKey != null) 
532                         baseKey.Close();
533
534                     if (eventKey != null)
535                         eventKey.Close();
536
537                     if (logKey != null) {
538                         logKey.Flush();
539                         logKey.Close();
540                     }
541
542                     if (sourceLogKey != null) {
543                         sourceLogKey.Flush();
544                         sourceLogKey.Close();
545                     }
546
547                     if (sourceKey != null) {
548                         sourceKey.Flush();
549                         sourceKey.Close();
550                     }
551
552                     // Revert registry and environment permission asserts
553                     CodeAccessPermission.RevertAssert();
554                 }
555             }
556             finally {
557                 if (mutex != null) {
558                     mutex.ReleaseMutex();
559                     mutex.Close();
560                 }
561             }
562         }
563
564         /// <devdoc>
565         ///    <para>
566         ///       Removes
567         ///       an event
568         ///       log from the local computer.
569         ///    </para>
570         /// </devdoc>
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, ".");
575         }
576
577         /// <devdoc>
578         ///    <para>
579         ///       Removes
580         ///       an
581         ///       event
582         ///       log from the specified computer.
583         ///    </para>
584         /// </devdoc>
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) {
588
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));
595
596             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
597             permission.Demand();
598
599             //Check environment before even trying to play with the registry
600             SharedUtils.CheckEnvironment();
601
602             //SECREVIEW: Note that EventLog permission is demanded above.
603             PermissionSet permissionSet = _UnsafeGetAssertPermSet();
604             permissionSet.Assert();
605             
606             RegistryKey eventlogkey = null;
607
608             Mutex mutex = null;
609             RuntimeHelpers.PrepareConstrainedRegions();
610             try {
611                 SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
612
613                 try {
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));
619                     }
620
621                     using (RegistryKey logKey = eventlogkey.OpenSubKey(logName)) {
622                         if (logKey == null)
623                             throw new InvalidOperationException(SR.GetString(SR.MissingLog, logName, machineName));
624
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);
628                         try {
629                             logToClear.Clear();
630                         }
631                         finally {
632                             logToClear.Close();
633                         }
634
635                         // 
636
637
638                         string filename = null;
639                         try {
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");
642                         }
643                         catch { }
644                         if (filename != null) {
645                             try {
646                                 File.Delete(filename);
647                             }
648                             catch { }
649                         }
650                     }
651
652                     // now delete the registry entry
653                     eventlogkey.DeleteSubKeyTree(logName);
654                 }
655                 finally {
656                     if (eventlogkey != null) eventlogkey.Close();
657                 
658                     // Revert registry and environment permission asserts
659                     CodeAccessPermission.RevertAssert();
660                 }
661             }
662             finally {
663                 if (mutex != null) mutex.ReleaseMutex();
664             }
665         }
666
667         /// <devdoc>
668         ///    <para>
669         ///       Removes the event source
670         ///       registration from the event log of the local computer.
671         ///    </para>
672         /// </devdoc>
673         [ResourceExposure(ResourceScope.None)]
674         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
675         public static void DeleteEventSource(string source) {
676             DeleteEventSource(source, ".");
677         }
678
679         /// <devdoc>
680         ///    <para>
681         ///       Removes
682         ///       the application's event source registration from the specified computer.
683         ///    </para>
684         /// </devdoc>
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));
690             }
691
692             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
693             permission.Demand();
694
695             //Check environment before looking at the registry
696             SharedUtils.CheckEnvironment();
697
698             //SECREVIEW: Note that EventLog permission is demanded above.
699             PermissionSet permissionSet = _UnsafeGetAssertPermSet();
700             permissionSet.Assert();
701             
702             Mutex mutex = null;
703             RuntimeHelpers.PrepareConstrainedRegions();
704             try {
705                 SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
706                 RegistryKey key = null;
707
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)) {
711                     if (key == null) {
712                         if (machineName == null)
713                             throw new ArgumentException(SR.GetString(SR.LocalSourceNotRegistered, source));
714                         else
715                             throw new ArgumentException(SR.GetString(SR.SourceNotRegistered, source, machineName, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog"));
716                     }
717             
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));
724                 }
725
726                 try {
727                     // now open it read/write to try to do the actual delete
728                     key = FindSourceRegistration(source, machineName, false);
729                     key.DeleteSubKeyTree(source);
730                                         
731                     if (!SkipRegPatch) { 
732                         string[] sources = (string[]) key.GetValue("Sources");
733                         ArrayList newsources = new ArrayList(sources.Length - 1);
734
735                         for (int i=0; i<sources.Length; i++) {
736                             if (sources[i] != source) {
737                                 newsources.Add(sources[i]);
738                             }
739                         }
740                         string[] newsourcesArray = new string[newsources.Count];
741                         newsources.CopyTo(newsourcesArray);
742
743                         key.SetValue("Sources", newsourcesArray, RegistryValueKind.MultiString);
744                     }
745                 }
746                 finally {
747                     if (key != null) {
748                         key.Flush();
749                         key.Close();
750                     }
751                 
752                     // Revert registry and environment permission asserts
753                     CodeAccessPermission.RevertAssert();
754                 }
755             }
756             finally {
757                 if (mutex != null)
758                     mutex.ReleaseMutex();
759             }
760         }
761
762         /// <devdoc>
763         /// </devdoc>
764         protected override void Dispose(bool disposing) {
765             if(m_underlyingEventLog != null) {
766                 m_underlyingEventLog.Dispose(disposing);
767             }
768
769             base.Dispose(disposing);
770         }
771
772         /// <devdoc>
773         /// </devdoc>
774         public void EndInit() {
775             m_underlyingEventLog.EndInit();
776         }
777
778         /// <devdoc>
779         ///    <para>
780         ///       Determines whether the log
781         ///       exists on the local computer.
782         ///    </para>
783         /// </devdoc>
784         public static bool Exists(string logName) {
785             return Exists(logName, ".");
786         }
787
788         /// <devdoc>
789         ///    <para>
790         ///       Determines whether the
791         ///       log exists on the specified computer.
792         ///    </para>
793         /// </devdoc>
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"));
799
800             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
801             permission.Demand();
802
803             if (logName == null || logName.Length==0)
804                 return false;
805
806             //Check environment before looking at the registry
807             SharedUtils.CheckEnvironment();
808
809             //SECREVIEW: Note that EventLog permission is demanded above.
810             PermissionSet permissionSet = _UnsafeGetAssertPermSet();
811             permissionSet.Assert();
812             
813             RegistryKey eventkey = null;
814             RegistryKey logKey = null;
815
816             try {
817                 eventkey = GetEventLogRegKey(machineName, false);
818                 if (eventkey == null)
819                     return false;
820
821                 logKey = eventkey.OpenSubKey(logName, false);         // try to find log file key immediately.
822                 return (logKey != null );
823             }
824             finally {
825                 if (eventkey != null) eventkey.Close();
826                 if (logKey != null) logKey.Close();
827                 
828                 // Revert registry and environment permission asserts
829                 CodeAccessPermission.RevertAssert();
830             }
831         }
832
833
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) {
837
838             string logNameFirst8 = logName.Substring(0, 8);
839             string[] logNames = keyParent.GetSubKeyNames();
840
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;
846             }
847
848             return null;   // not found
849         }
850
851         /// <devdoc>
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.
854         /// </devdoc>
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);
859         }
860
861         /// <devdoc>
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.
864         /// </devdoc>
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) {
869
870                 //Check environment before looking at the registry
871                 SharedUtils.CheckEnvironment();
872
873                 //SECREVIEW: Any call to this function must have demmanded
874                 //                         EventLogPermission before.
875                 PermissionSet permissionSet = _UnsafeGetAssertPermSet();
876                 permissionSet.Assert();
877                 
878                 RegistryKey eventkey = null;
879                 try {
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.
884                         return null;
885                     }
886
887                     StringBuilder inaccessibleLogs = null;
888                     
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;
896                         try {
897                             RegistryKey logKey = eventkey.OpenSubKey(logNames[i], /*writable*/!readOnly);
898                             if (logKey != null) {
899                                 sourceKey = logKey.OpenSubKey(source, /*writable*/!readOnly);
900                                 if (sourceKey != null) {
901                                     // found it
902                                     return logKey;
903                                 } else {
904                                     logKey.Close();
905                                 }
906                             }
907                             // else logKey is null, so we don't need to Close it
908                         }
909                         catch (UnauthorizedAccessException) {
910                             if (inaccessibleLogs == null) {
911                                 inaccessibleLogs = new StringBuilder(logNames[i]);
912                             }
913                             else {
914                                 inaccessibleLogs.Append(", ");
915                                 inaccessibleLogs.Append(logNames[i]);
916                             }
917                         }
918                         catch (SecurityException) {
919                             if (inaccessibleLogs == null) {
920                                 inaccessibleLogs = new StringBuilder(logNames[i]);
921                             }
922                             else {
923                                 inaccessibleLogs.Append(", ");
924                                 inaccessibleLogs.Append(logNames[i]);
925                             }
926                         }
927                         finally {
928                             if (sourceKey != null) sourceKey.Close();
929                         }
930                     }
931
932                     if (inaccessibleLogs != null)
933                         throw new SecurityException(SR.GetString(wantToCreate ? SR.SomeLogsInaccessibleToCreate : SR.SomeLogsInaccessible, inaccessibleLogs.ToString()));
934                     
935                 }
936                 finally {
937                     if (eventkey != null) eventkey.Close();
938                     
939                     // Revert registry and environment permission asserts
940                     CodeAccessPermission.RevertAssert();
941                 }
942                 // didn't see it anywhere
943             }
944
945             return null;
946         }
947
948         /// <devdoc>
949         ///    <para>
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
953         ///       list.
954         ///    </para>
955         /// </devdoc>
956         public static EventLog[] GetEventLogs() {
957             return GetEventLogs(".");
958         }
959
960         /// <devdoc>
961         ///    <para>
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
965         ///       list.
966         ///    </para>
967         /// </devdoc>
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));
973             }
974
975             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
976             permission.Demand();
977
978             //Check environment before looking at the registry
979             SharedUtils.CheckEnvironment();
980
981             string[] logNames = new string[0];
982             //SECREVIEW: Note that EventLogPermission is just demmanded above
983             PermissionSet permissionSet = _UnsafeGetAssertPermSet();
984             permissionSet.Assert();
985             
986             RegistryKey eventkey = null;
987             try {
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();
997             }
998             finally {
999                 if (eventkey != null) eventkey.Close();
1000                 // Revert registry and environment permission asserts
1001                 CodeAccessPermission.RevertAssert();
1002             }
1003
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);
1008                 logs[i] = log;
1009             }
1010
1011             return logs;
1012         }
1013
1014         [ResourceExposure(ResourceScope.Machine)]
1015         [ResourceConsumption(ResourceScope.Machine)]
1016         internal static RegistryKey GetEventLogRegKey(string machine, bool writable) {
1017             RegistryKey lmkey = null;
1018             
1019             try {
1020                 if (machine.Equals(".")) {
1021                     lmkey = Registry.LocalMachine;
1022                 }
1023                 else {
1024                     lmkey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine);
1025
1026                 }
1027                 if (lmkey != null)
1028                     return lmkey.OpenSubKey(EventLogKey, writable);
1029             }
1030             finally {
1031                 if (lmkey != null) lmkey.Close();
1032             }
1033
1034             return null;
1035         }
1036
1037         [ResourceExposure(ResourceScope.Machine)]
1038         [ResourceConsumption(ResourceScope.Machine)]
1039         internal static string GetDllPath(string machineName) {
1040             return Path.Combine(SharedUtils.GetLatestBuildDllDirectory(machineName), DllName);
1041         }
1042
1043         /// <devdoc>
1044         ///    <para>
1045         ///       Determines whether an event source is registered on the local computer.
1046         ///    </para>
1047         /// </devdoc>
1048         public static bool SourceExists(string source) {
1049             return SourceExists(source, ".");
1050         }
1051
1052         /// <devdoc>
1053         ///    <para>
1054         ///       Determines whether an event
1055         ///       source is registered on a specified computer.
1056         ///    </para>
1057         /// </devdoc>
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);
1062         }
1063
1064         /// <devdoc>
1065         ///    <para>
1066         ///       Determines whether an event
1067         ///       source is registered on a specified computer.
1068         ///    </para>
1069         /// </devdoc>
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));
1076             }
1077
1078             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
1079             permission.Demand();
1080
1081             using (RegistryKey keyFound = FindSourceRegistration(source, machineName, true, wantToCreate)) {
1082                 return (keyFound != null);
1083             }
1084         }
1085
1086         /// <devdoc>
1087         ///     Gets the name of the log that the given source name is registered in.
1088         /// </devdoc>
1089         public static string LogNameFromSourceName(string source, string machineName) {
1090             EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
1091             permission.Demand();
1092
1093             return _InternalLogNameFromSourceName(source, machineName);
1094         }
1095
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)) {
1101                 if (key == null)
1102                     return "";
1103                 else {
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);
1108                 }
1109             }
1110         }
1111
1112
1113         [ComVisible(false)]
1114         [ResourceExposure(ResourceScope.None)]
1115         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1116         public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) {
1117             m_underlyingEventLog.ModifyOverflowPolicy(action, retentionDays);
1118         }
1119
1120         [ComVisible(false)]
1121         [ResourceExposure(ResourceScope.Machine)]
1122         [ResourceConsumption(ResourceScope.Machine)]
1123         public void RegisterDisplayName(string resourceFile, long resourceId) {
1124             m_underlyingEventLog.RegisterDisplayName(resourceFile, resourceId);
1125         }
1126
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. 
1137
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);
1144
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);
1149                 
1150                 if (logKey.GetValue("File") == null) { 
1151                     string filename;
1152                     if (logName.Length > 8)
1153                         filename = @"%SystemRoot%\System32\config\" + logName.Substring(0,8) + ".evt";
1154                     else
1155                         filename = @"%SystemRoot%\System32\config\" + logName + ".evt";
1156
1157                     logKey.SetValue("File", filename, RegistryValueKind.ExpandString);
1158                 }
1159             }
1160         }
1161
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);
1167             else 
1168                 sourceLogKey.SetValue("EventMessageFile", FixupPath(sourceData.MessageResourceFile), RegistryValueKind.ExpandString);
1169
1170             if (!String.IsNullOrEmpty(sourceData.ParameterResourceFile))
1171                 sourceLogKey.SetValue("ParameterMessageFile", FixupPath(sourceData.ParameterResourceFile), RegistryValueKind.ExpandString);
1172
1173             if (!String.IsNullOrEmpty(sourceData.CategoryResourceFile)) {
1174                 sourceLogKey.SetValue("CategoryMessageFile", FixupPath(sourceData.CategoryResourceFile), RegistryValueKind.ExpandString);
1175                 sourceLogKey.SetValue("CategoryCount", sourceData.CategoryCount, RegistryValueKind.DWord);
1176             }
1177         }
1178
1179         [ResourceExposure(ResourceScope.Machine)]
1180         [ResourceConsumption(ResourceScope.Machine)]
1181         private static string FixupPath(string path) {
1182             if (path[0] == '%')
1183                 return path;
1184             else
1185                 return Path.GetFullPath(path);
1186         }
1187         
1188         // Format message in specific DLL. Return <null> on failure.
1189         internal static string TryFormatMessage(SafeLibraryHandle hModule, uint messageNum, string[] insertionStrings) {
1190
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);
1196             }
1197
1198             // If you pass in an empty array UnsafeTryFormatMessage will just pull out the message.
1199             string formatString = UnsafeTryFormatMessage(hModule, messageNum, new string[0]);
1200
1201             if (formatString == null) {
1202                 return null;
1203             }
1204
1205             int largestNumber = 0;
1206
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]);
1214                             i++;
1215                         }
1216
1217                         // move over the non number character that broke us out of the loop
1218                         i++;
1219
1220                         if (sb.Length > 0) {
1221                             int num = -1;
1222                             if (Int32.TryParse(sb.ToString(), NumberStyles.None, CultureInfo.InvariantCulture, out num)) {
1223                                 largestNumber = Math.Max(largestNumber, num);
1224                             }
1225                         }
1226                     }
1227                 }
1228             }
1229
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);
1236                 }
1237
1238                 insertionStrings = newStrings;
1239             }
1240
1241             return UnsafeTryFormatMessage(hModule, messageNum, insertionStrings);
1242         }
1243
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) {
1247             string msg = null;
1248
1249             int msgLen = 0;
1250             StringBuilder buf = new StringBuilder(1024);
1251             int flags = NativeMethods.FORMAT_MESSAGE_FROM_HMODULE | NativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY;
1252
1253             IntPtr[] addresses = new IntPtr[insertionStrings.Length];
1254             GCHandle[] handles = new GCHandle[insertionStrings.Length];
1255             GCHandle stringsRoot = GCHandle.Alloc(addresses, GCHandleType.Pinned);
1256
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;
1262             }
1263             
1264             try {
1265                 for (int i=0; i<handles.Length; i++) {
1266                     handles[i] = GCHandle.Alloc(insertionStrings[i], GCHandleType.Pinned);
1267                     addresses[i] = handles[i].AddrOfPinnedObject();
1268                 }
1269                 int lastError = NativeMethods.ERROR_INSUFFICIENT_BUFFER;
1270                 while (msgLen == 0 && lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER) {
1271                     msgLen = SafeNativeMethods.FormatMessage(
1272                         flags,
1273                         hModule,
1274                         messageNum,
1275                         0,
1276                         buf,
1277                         buf.Capacity,
1278                         addresses);
1279
1280                     if (msgLen == 0) {
1281                         lastError = Marshal.GetLastWin32Error();
1282                         if (lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER)
1283                             buf.Capacity = buf.Capacity * 2;
1284                     }
1285                 }
1286             }
1287             catch {
1288                 msgLen = 0;              // return empty on failure
1289             }
1290             finally  {
1291                 for (int i=0; i<handles.Length; i++) {
1292                     if (handles[i].IsAllocated) handles[i].Free();
1293                 }
1294                 stringsRoot.Free();
1295             }
1296             
1297             if (msgLen > 0) {
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);
1302             }
1303
1304             return msg;
1305         }
1306
1307
1308         // CharIsPrintable used to be Char.IsPrintable, but Jay removed it and
1309         // is forcing people to use the Unicode categories themselves.  Copied
1310         // the code here.  
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));
1316         }
1317
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)
1324                 return false;
1325
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 == '?'))
1330                     return false;
1331
1332             return true;
1333         }
1334
1335         /// <devdoc>
1336         ///    <para>
1337         ///       Writes an information type entry with the given message text to the event log.
1338         ///    </para>
1339         /// </devdoc>
1340         public void WriteEntry(string message) {
1341             WriteEntry(message, EventLogEntryType.Information, (short) 0, 0, null);
1342         }
1343
1344         /// <devdoc>
1345         /// </devdoc>
1346         public static void WriteEntry(string source, string message) {
1347             WriteEntry(source, message, EventLogEntryType.Information, (short) 0, 0, null);
1348         }
1349
1350         /// <devdoc>
1351         ///    <para>
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'/>.
1355         ///    </para>
1356         /// </devdoc>
1357         public void WriteEntry(string message, EventLogEntryType type) {
1358             WriteEntry(message, type, (short) 0, 0, null);
1359         }
1360
1361         /// <devdoc>
1362         ///    <para>[To be supplied.]</para>
1363         /// </devdoc>
1364         public static void WriteEntry(string source, string message, EventLogEntryType type) {
1365             WriteEntry(source, message, type, (short) 0, 0, null);
1366         }
1367
1368         /// <devdoc>
1369         ///    <para>
1370         ///       Writes an entry of the specified <see cref='System.Diagnostics.EventLogEntryType'/>
1371         ///       and with the
1372         ///       user-defined <paramref name="eventID"/>
1373         ///       to
1374         ///       the event log.
1375         ///    </para>
1376         /// </devdoc>
1377         public void WriteEntry(string message, EventLogEntryType type, int eventID) {
1378             WriteEntry(message, type, eventID, 0, null);
1379         }
1380
1381         /// <devdoc>
1382         ///    <para>[To be supplied.]</para>
1383         /// </devdoc>
1384         public static void WriteEntry(string source, string message, EventLogEntryType type, int eventID) {
1385             WriteEntry(source, message, type, eventID, 0, null);
1386         }
1387
1388         /// <devdoc>
1389         ///    <para>
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.
1394         ///    </para>
1395         /// </devdoc>
1396         public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) {
1397             WriteEntry(message, type, eventID, category, null);
1398         }
1399
1400         /// <devdoc>
1401         ///    <para>[To be supplied.]</para>
1402         /// </devdoc>
1403         public static void WriteEntry(string source, string message, EventLogEntryType type, int eventID, short category) {
1404             WriteEntry(source, message, type, eventID, category, null);
1405         }
1406
1407         /// <devdoc>
1408         ///    <para>[To be supplied.]</para>
1409         /// </devdoc>
1410         public static void WriteEntry(string source, string message, EventLogEntryType type, int eventID, short category,
1411                                byte[] rawData) {
1412             using (EventLogInternal log = new EventLogInternal("", ".", CheckAndNormalizeSourceName(source))) {
1413                 log.WriteEntry(message, type, eventID, category, rawData);
1414             }
1415         }
1416
1417         /// <devdoc>
1418         ///    <para>
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.
1423         ///    </para>
1424         /// </devdoc>
1425         public void WriteEntry(string message, EventLogEntryType type, int eventID, short category,
1426                                byte[] rawData) {
1427
1428             m_underlyingEventLog.WriteEntry(message, type, eventID, category, rawData);
1429         }
1430
1431         [ComVisible(false)]
1432         public void WriteEvent(EventInstance instance, params Object[] values) {
1433             WriteEvent(instance, null, values);
1434         }
1435
1436         [ComVisible(false)]
1437         public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) {
1438             m_underlyingEventLog.WriteEvent(instance, data, values);
1439         }
1440
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);
1444             }
1445         }
1446
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);
1450             }
1451         }
1452
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) {
1455             if (source == null)
1456                 source = string.Empty;
1457
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));
1461
1462             return source;
1463         }
1464     }
1465
1466 }