Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Services / Monitoring / system / Diagnosticts / EventLogInternal.cs / 1305376 / EventLogInternal.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
//#define RETRY_ON_ALL_ERRORS
/*
* EventLogInternal contains most of the logic for interacting with the Windows Event Log.
* The reason for this class existing (instead of the logic being in EventLog itself) is
* that we'd like to be able to have the invariant that the Source, MachineName and Log Name
* don't change across the lifetime of an event log object, but we exposed public setters for
* these properties. EventLog holds a reference to an EventLogInternal instance, plumbs all
* calls to it and replaces it when any of these properites change.
*
* Note that EventLogInternal also holds a reference back to the EventLog instnace that is
* exposing it so it is not prematurely collected.
*/
namespace System.Diagnostics {
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics;
using System;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.ComponentModel.Design;
using System.Security;
using System.Security.Permissions;
using System.Reflection;
using System.Runtime.Versioning;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
///
///
/// Provides interaction with Windows 2000 event logs.
///
///
internal class EventLogInternal : IDisposable, ISupportInitialize {
// a collection over all our entries. Since the class holds no state, we
// can just hand the same instance out every time.
private EventLogEntryCollection entriesCollection;
// the name of the log we're reading from or writing to
internal string logName;
// used in monitoring for event postings.
private int lastSeenCount;
// holds the machine we're on, or null if it's the local machine
internal string machineName;
// the delegate to call when an event arrives
internal EntryWrittenEventHandler onEntryWrittenHandler;
// holds onto the handle for reading
private SafeEventLogReadHandle readHandle;
// the source name - used only when writing
internal string sourceName;
// holds onto the handle for writing
private SafeEventLogWriteHandle writeHandle;
private string logDisplayName;
// cache system state variables
// the initial size of the buffer (it can be made larger if necessary)
private const int BUF_SIZE = 40000;
// the number of bytes in the cache that belong to entries (not necessarily
// the same as BUF_SIZE, because the cache only holds whole entries)
private int bytesCached;
// the actual cache buffer
private byte[] cache;
// the number of the entry at the beginning of the cache
private int firstCachedEntry = -1;
// the number of the entry that we got out of the cache most recently
private int lastSeenEntry;
// where that entry was
private int lastSeenPos;
//support for threadpool based deferred execution
private ISynchronizeInvoke synchronizingObject;
// the EventLog object that publicly exposes this instance.
private EventLog m_Parent;
private const string EventLogKey = "SYSTEM\\CurrentControlSet\\Services\\EventLog";
internal const string DllName = "EventLogMessages.dll";
private const string eventLogMutexName = "netfxeventlog.1.0";
private const int SecondsPerDay = 60 * 60 * 24;
private const int DefaultMaxSize = 512*1024;
private const int DefaultRetention = 7*SecondsPerDay;
private const int Flag_notifying = 0x1; // keeps track of whether we're notifying our listeners - to prevent double notifications
private const int Flag_forwards = 0x2; // whether the cache contains entries in forwards order (true) or backwards (false)
private const int Flag_initializing = 0x4;
internal const int Flag_monitoring = 0x8;
private const int Flag_registeredAsListener = 0x10;
private const int Flag_writeGranted = 0x20;
private const int Flag_disposed = 0x100;
private const int Flag_sourceVerified= 0x200;
private BitVector32 boolFlags = new BitVector32();
private Hashtable messageLibraries;
private static Hashtable listenerInfos = new Hashtable(StringComparer.OrdinalIgnoreCase);
private Object m_InstanceLockObject;
private Object InstanceLockObject {
get {
if (m_InstanceLockObject == null) {
Object o = new Object();
Interlocked.CompareExchange(ref m_InstanceLockObject, o, null);
}
return m_InstanceLockObject;
}
}
private static Object s_InternalSyncObject;
private static Object InternalSyncObject {
get {
if (s_InternalSyncObject == null) {
Object o = new Object();
Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
}
return s_InternalSyncObject;
}
}
// Whether we need backward compatible OS patch work or not
private static bool s_CheckedOsVersion;
private static bool s_SkipRegPatch;
private static bool SkipRegPatch {
get {
if (!s_CheckedOsVersion) {
OperatingSystem os = Environment.OSVersion;
s_SkipRegPatch = (os.Platform == PlatformID.Win32NT) && (os.Version.Major > 5);
s_CheckedOsVersion = true;
}
return s_SkipRegPatch;
}
}
///
///
/// Initializes a new instance of the
/// class.
///
///
public EventLogInternal() : this("", ".", "", null) {
}
///
/// [To be supplied.]
///
public EventLogInternal(string logName) : this(logName, ".", "", null) {
}
///
/// [To be supplied.]
///
public EventLogInternal(string logName, string machineName) : this(logName, machineName, "", null) {
}
///
/// [To be supplied.]
///
public EventLogInternal(string logName, string machineName, string source) : this(logName, machineName, source, null) {
}
///
/// [To be supplied.]
///
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, oldLog.machineName doesn't change")]
public EventLogInternal(string logName, string machineName, string source, EventLog parent) {
//look out for invalid log names
if (logName == null)
throw new ArgumentNullException("logName");
if (!ValidLogName(logName, true))
throw new ArgumentException(SR.GetString(SR.BadLogName));
if (!SyntaxCheck.CheckMachineName(machineName))
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
permission.Demand();
this.machineName = machineName;
this.logName = logName;
this.sourceName = source;
readHandle = null;
writeHandle = null;
boolFlags[Flag_forwards] = true;
m_Parent = parent;
}
///
///
/// Gets the contents of the event log.
///
///
public EventLogEntryCollection Entries {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
if (entriesCollection == null)
entriesCollection = new EventLogEntryCollection(this);
return entriesCollection;
}
}
///
/// Gets the number of entries in the log
///
internal int EntryCount {
get {
if (!IsOpenForRead)
OpenForRead(this.machineName);
int count;
bool success = UnsafeNativeMethods.GetNumberOfEventLogRecords(readHandle, out count);
if (!success)
throw SharedUtils.CreateSafeWin32Exception();
return count;
}
}
///
/// Determines whether the event log is open in either read or write access
///
private bool IsOpen {
get {
return readHandle != null || writeHandle != null;
}
}
///
/// Determines whether the event log is open with read access
///
private bool IsOpenForRead {
get {
return readHandle != null;
}
}
///
/// Determines whether the event log is open with write access.
///
private bool IsOpenForWrite {
get {
return writeHandle != null;
}
}
///
///
///
///
public string LogDisplayName {
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
get {
if (logDisplayName == null) {
string currentMachineName = this.machineName;
if (GetLogName(currentMachineName) != null) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLogPermission is just demmanded above
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey logkey = null;
try {
// we figure out what logs are on the machine by looking in the registry.
logkey = GetLogRegKey(currentMachineName, false);
if (logkey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, GetLogName(currentMachineName), currentMachineName));
string resourceDll = (string)logkey.GetValue("DisplayNameFile");
if (resourceDll == null)
logDisplayName = GetLogName(currentMachineName);
else {
int resourceId = (int)logkey.GetValue("DisplayNameID");
logDisplayName = FormatMessageWrapper(resourceDll, (uint) resourceId, null);
if (logDisplayName == null)
logDisplayName = GetLogName(currentMachineName);
}
}
finally {
if (logkey != null) logkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
}
return logDisplayName;
}
}
///
///
/// Gets or sets the name of the log to read from and write to.
///
///
public string Log {
get {
string currentMachineName = this.machineName;
if (logName == null || logName.Length == 0) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
}
return GetLogName(currentMachineName);
}
}
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private string GetLogName(string currentMachineName)
{
if ((logName == null || logName.Length == 0) && sourceName != null && sourceName.Length!=0) {
// they've told us a source, but they haven't told us a log name.
// try to deduce the log name from the source name.
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
logName = _InternalLogNameFromSourceName(sourceName, currentMachineName);
}
return logName;
}
///
///
/// Gets or sets the name of the computer on which to read or write events.
///
///
public string MachineName {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
return currentMachineName;
}
}
[ComVisible(false)]
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "[....]: MaximumKilobytes is the name of this property.")]
public long MaximumKilobytes {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
object val = GetLogRegValue(currentMachineName, "MaxSize");
if (val != null) {
int intval = (int) val; // cast to an int first to unbox
return ((uint)intval) / 1024; // then convert to kilobytes
}
// 512k is the default value
return 0x200;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
set {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
// valid range is 64 KB to 4 GB
if (value < 64 || value > 0x3FFFC0 || value % 64 != 0)
throw new ArgumentOutOfRangeException("MaximumKilobytes", SR.GetString(SR.MaximumKilobytesOutOfRange));
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
long regvalue = value * 1024; // convert to bytes
int i = unchecked((int)regvalue);
using (RegistryKey logkey = GetLogRegKey(currentMachineName, true))
logkey.SetValue("MaxSize", i, RegistryValueKind.DWord);
}
}
internal Hashtable MessageLibraries {
get {
if (messageLibraries == null)
messageLibraries = new Hashtable(StringComparer.OrdinalIgnoreCase);
return messageLibraries;
}
}
[ComVisible(false)]
public OverflowAction OverflowAction {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
object retentionobj = GetLogRegValue(currentMachineName, "Retention");
if (retentionobj != null) {
int retention = (int) retentionobj;
if (retention == 0)
return OverflowAction.OverwriteAsNeeded;
else if (retention == -1)
return OverflowAction.DoNotOverwrite;
else
return OverflowAction.OverwriteOlder;
}
// default value as listed in MSDN
return OverflowAction.OverwriteOlder;
}
}
[ComVisible(false)]
public int MinimumRetentionDays {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
object retentionobj = GetLogRegValue(currentMachineName, "Retention");
if (retentionobj != null) {
int retention = (int) retentionobj;
if (retention == 0 || retention == -1)
return retention;
else
return (int) (((double) retention) / SecondsPerDay);
}
return 7;
}
}
///
///
public bool EnableRaisingEvents {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
return boolFlags[Flag_monitoring];
}
set {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
if (m_Parent.ComponentDesignMode)
this.boolFlags[Flag_monitoring] = value;
else {
if (value)
StartRaisingEvents(currentMachineName, GetLogName(currentMachineName));
else
StopRaisingEvents(/*currentMachineName,*/ GetLogName(currentMachineName));
}
}
}
private int OldestEntryNumber {
get {
if (!IsOpenForRead)
OpenForRead(this.machineName);
int num;
bool success = UnsafeNativeMethods.GetOldestEventLogRecord(readHandle, out num);
if (!success)
throw SharedUtils.CreateSafeWin32Exception();
// When the event log is empty, GetOldestEventLogRecord returns 0.
// But then after an entry is written, it returns 1. We need to go from
// the last num to the current.
if (num == 0)
num = 1;
return num;
}
}
internal SafeEventLogReadHandle ReadHandle {
get {
if (!IsOpenForRead)
OpenForRead(this.machineName);
return readHandle;
}
}
///
///
/// Represents the object used to marshal the event handler
/// calls issued as a result of an
/// change.
///
///
public ISynchronizeInvoke SynchronizingObject {
[HostProtection(Synchronization=true)]
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
if (this.synchronizingObject == null && m_Parent.ComponentDesignMode) {
IDesignerHost host = (IDesignerHost)m_Parent.ComponentGetService(typeof(IDesignerHost));
if (host != null) {
object baseComponent = host.RootComponent;
if (baseComponent != null && baseComponent is ISynchronizeInvoke)
this.synchronizingObject = (ISynchronizeInvoke)baseComponent;
}
}
return this.synchronizingObject;
}
set {
this.synchronizingObject = value;
}
}
///
///
/// Gets or
/// sets the application name (source name) to register and use when writing to the event log.
///
///
public string Source {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
return sourceName;
}
}
[HostProtection(Synchronization=true)]
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private static void AddListenerComponent(EventLogInternal component, string compMachineName, string compLogName) {
lock (InternalSyncObject) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::AddListenerComponent(" + compLogName + ")");
LogListeningInfo info = (LogListeningInfo) listenerInfos[compLogName];
if (info != null) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::AddListenerComponent: listener already active.");
info.listeningComponents.Add(component);
return;
}
info = new LogListeningInfo();
info.listeningComponents.Add(component);
info.handleOwner = new EventLogInternal(compLogName, compMachineName);
// tell the event log system about it
info.waitHandle = new AutoResetEvent(false);
bool success = UnsafeNativeMethods.NotifyChangeEventLog(info.handleOwner.ReadHandle, info.waitHandle.SafeWaitHandle);
if (!success)
throw new InvalidOperationException(SR.GetString(SR.CantMonitorEventLog), SharedUtils.CreateSafeWin32Exception());
info.registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(info.waitHandle, new WaitOrTimerCallback(StaticCompletionCallback), info, -1, false);
listenerInfos[compLogName] = info;
}
}
///
///
/// Occurs when an entry is written to the event log.
///
///
public event EntryWrittenEventHandler EntryWritten {
add {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
onEntryWrittenHandler += value;
}
remove {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
onEntryWrittenHandler -= value;
}
}
///
///
public void BeginInit() {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
if (boolFlags[Flag_initializing]) throw new InvalidOperationException(SR.GetString(SR.InitTwice));
boolFlags[Flag_initializing] = true;
if (boolFlags[Flag_monitoring])
StopListening(GetLogName(currentMachineName));
}
///
///
/// Clears
/// the event log by removing all entries from it.
///
///
[ResourceExposure(ResourceScope.Machine)] // Should anyone ever call this, other than an event log viewer?
[ResourceConsumption(ResourceScope.Machine)]
public void Clear() {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
if (!IsOpenForRead)
OpenForRead(currentMachineName);
bool success = UnsafeNativeMethods.ClearEventLog(readHandle, NativeMethods.NullHandleRef);
if (!success) {
// Ignore file not found errors. ClearEventLog seems to try to delete the file where the event log is
// stored. If it can't find it, it gives an error.
int error = Marshal.GetLastWin32Error();
if (error != NativeMethods.ERROR_FILE_NOT_FOUND)
throw SharedUtils.CreateSafeWin32Exception();
}
// now that we've cleared the event log, we need to re-open our handles, because
// the internal state of the event log has changed.
Reset(currentMachineName);
}
///
///
/// Closes the event log and releases read and write handles.
///
///
[ResourceExposure(ResourceScope.None)]
public void Close() {
Close(this.machineName);
}
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, currentMachineName doesn't change")]
private void Close(string currentMachineName) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close");
//Trace("Close", "Closing the event log");
if (readHandle != null) {
try {
readHandle.Close();
}
catch (IOException) {
throw SharedUtils.CreateSafeWin32Exception();
}
readHandle = null;
//Trace("Close", "Closed read handle");
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close: closed read handle");
}
if (writeHandle != null) {
try {
writeHandle.Close();
}
catch (IOException) {
throw SharedUtils.CreateSafeWin32Exception();
}
writeHandle = null;
//Trace("Close", "Closed write handle");
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close: closed write handle");
}
if (boolFlags[Flag_monitoring])
StopRaisingEvents(/*currentMachineName,*/ GetLogName(currentMachineName));
if (messageLibraries != null) {
foreach (SafeLibraryHandle handle in messageLibraries.Values)
handle.Close();
messageLibraries = null;
}
boolFlags[Flag_sourceVerified] = false;
}
///
///
/// Called when the threadpool is ready for us to handle a status change.
///
private void CompletionCallback(object context) {
if (boolFlags[Flag_disposed]) {
// This object has been disposed previously, ignore firing the event.
return;
}
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: starting at " + lastSeenCount.ToString(CultureInfo.InvariantCulture));
lock (InstanceLockObject) {
if (boolFlags[Flag_notifying]) {
// don't do double notifications.
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: aborting because we're already notifying.");
return;
}
boolFlags[Flag_notifying] = true;
}
int i = lastSeenCount;
try {
int oldest = OldestEntryNumber;
int count = EntryCount + oldest;
// Ensure lastSeenCount is within bounds. This deals with the case where the event log has been cleared between
// notifications.
if (lastSeenCount < oldest || lastSeenCount > count) {
lastSeenCount = oldest;
i = lastSeenCount;
}
// NOTE, [....]: We have a double loop here so that we access the
// EntryCount property as infrequently as possible. (It may be expensive
// to get the property.) Even though there are two loops, they will together
// only execute as many times as (final value of EntryCount) - lastSeenCount.
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: OldestEntryNumber is " + OldestEntryNumber + ", EntryCount is " + EntryCount);
while (i < count) {
while (i < count) {
EventLogEntry entry = GetEntryWithOldest(i);
if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
this.SynchronizingObject.BeginInvoke(this.onEntryWrittenHandler, new object[]{this, new EntryWrittenEventArgs(entry)});
else
onEntryWrittenHandler(this, new EntryWrittenEventArgs(entry));
i++;
}
oldest = OldestEntryNumber;
count = EntryCount + oldest;
}
}
catch (Exception e) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: Caught exception notifying event handlers: " + e.ToString());
}
// if the user cleared the log while we were receiving events, the call to GetEntryWithOldest above could have
// thrown an exception and i could be too large. Make sure we don't set lastSeenCount to something bogus.
int newCount = EntryCount + OldestEntryNumber;
if (i > newCount)
lastSeenCount = newCount;
else
lastSeenCount = i;
lock (InstanceLockObject) {
boolFlags[Flag_notifying] = false;
}
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: finishing at " + lastSeenCount.ToString(CultureInfo.InvariantCulture));
}
///
/// Establishes an application, using the
/// specified , as a valid event source for
/// writing entries
/// to a log on the local computer. This method
/// can also be used to create
/// a new custom log on the local computer.
///
public static void CreateEventSource(string source, string logName) {
CreateEventSource(new EventSourceCreationData(source, logName, "."));
}
///
/// Establishes an application, using the specified
/// as a valid event source for writing
/// entries to a log on the computer
/// specified by . This method can also be used to create a new
/// custom log on the given computer.
///
[Obsolete("This method has been deprecated. Please use System.Diagnostics.EventLog.CreateEventSource(EventSourceCreationData sourceData) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
public static void CreateEventSource(string source, string logName, string machineName) {
CreateEventSource(new EventSourceCreationData(source, logName, machineName));
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public static void CreateEventSource(EventSourceCreationData sourceData) {
if (sourceData == null)
throw new ArgumentNullException("sourceData");
string logName = sourceData.LogName;
string source = sourceData.Source;
string machineName = sourceData.MachineName;
// verify parameters
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Checking arguments");
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
if (logName == null || logName.Length==0)
logName = "Application";
if (!ValidLogName(logName, false))
throw new ArgumentException(SR.GetString(SR.BadLogName));
if (source == null || source.Length==0)
throw new ArgumentException(SR.GetString(SR.MissingParameter, "source"));
if (source.Length + EventLogKey.Length > 254)
throw new ArgumentException(SR.GetString(SR.ParameterTooLong, "source", 254 - EventLogKey.Length));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Calling SourceExists");
if (SourceExists(source, machineName, true)) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: SourceExists returned true");
// don't let them register a source if it already exists
// this makes more sense than just doing it anyway, because the source might
// be registered under a different log name, and we don't want to create
// duplicates.
if (".".Equals(machineName))
throw new ArgumentException(SR.GetString(SR.LocalSourceAlreadyExists, source));
else
throw new ArgumentException(SR.GetString(SR.SourceAlreadyExists, source, machineName));
}
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting DllPath");
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey baseKey = null;
RegistryKey eventKey = null;
RegistryKey logKey = null;
RegistryKey sourceLogKey = null;
RegistryKey sourceKey = null;
try {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting local machine regkey");
if (machineName == ".")
baseKey = Registry.LocalMachine;
else
baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName);
eventKey = baseKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\EventLog", true);
if (eventKey == null) {
if (!".".Equals(machineName))
throw new InvalidOperationException(SR.GetString(SR.RegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source, machineName));
else
throw new InvalidOperationException(SR.GetString(SR.LocalRegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source));
}
// The event log system only treats the first 8 characters of the log name as
// significant. If they're creating a new log, but that new log has the same
// first 8 characters as another log, the system will think they're the same.
// Throw an exception to let them know.
logKey = eventKey.OpenSubKey(logName, true);
if (logKey == null && logName.Length >= 8) {
// check for Windows embedded logs file names
string logNameFirst8 = logName.Substring(0,8);
if ( string.Compare(logNameFirst8,"AppEvent",StringComparison.OrdinalIgnoreCase) ==0 ||
string.Compare(logNameFirst8,"SecEvent",StringComparison.OrdinalIgnoreCase) ==0 ||
string.Compare(logNameFirst8,"SysEvent",StringComparison.OrdinalIgnoreCase) ==0 )
throw new ArgumentException(SR.GetString(SR.InvalidCustomerLogName, logName));
string sameLogName = FindSame8FirstCharsLog(eventKey, logName);
if ( sameLogName != null )
throw new ArgumentException(SR.GetString(SR.DuplicateLogName, logName, sameLogName));
}
bool createLogKey = (logKey == null);
if (createLogKey) {
if (SourceExists(logName, machineName, true)) {
// don't let them register a log name that already
// exists as source name, a source with the same
// name as the log will have to be created by default
if (".".Equals(machineName))
throw new ArgumentException(SR.GetString(SR.LocalLogAlreadyExistsAsSource, logName));
else
throw new ArgumentException(SR.GetString(SR.LogAlreadyExistsAsSource, logName, machineName));
}
logKey = eventKey.CreateSubKey(logName);
// NOTE: We shouldn't set "Sources" explicitly, the OS will automatically set it.
// The EventLog service doesn't use it for anything it is just an helping hand for event viewer filters.
// Writing this value explicitly might confuse the service as it might perceive it as a change and
// start initializing again
if (!SkipRegPatch)
logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
SetSpecialLogRegValues(logKey, logName);
// A source with the same name as the log has to be created
// by default. It is the behavior expected by EventLog API.
sourceLogKey = logKey.CreateSubKey(logName);
SetSpecialSourceRegValues(sourceLogKey, sourceData);
}
if (logName != source) {
if (!createLogKey) {
SetSpecialLogRegValues(logKey, logName);
if (!SkipRegPatch) {
string[] sources = logKey.GetValue("Sources") as string[];
if (sources == null)
logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
else {
// We have a ---- with OS EventLog here.
// OS might update Sources as well. We should avoid writing the
// source name if OS beats us.
if( Array.IndexOf(sources, source) == -1) {
string[] newsources = new string[sources.Length + 1];
Array.Copy(sources, newsources, sources.Length);
newsources[sources.Length] = source;
logKey.SetValue("Sources", newsources, RegistryValueKind.MultiString);
}
}
}
}
sourceKey = logKey.CreateSubKey(source);
SetSpecialSourceRegValues(sourceKey, sourceData);
}
}
finally {
if (baseKey != null)
baseKey.Close();
if (eventKey != null)
eventKey.Close();
if (logKey != null) {
logKey.Flush();
logKey.Close();
}
if (sourceLogKey != null) {
sourceLogKey.Flush();
sourceLogKey.Close();
}
if (sourceKey != null) {
sourceKey.Flush();
sourceKey.Close();
}
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
finally {
if (mutex != null) {
mutex.ReleaseMutex();
mutex.Close();
}
}
}
///
///
/// Removes
/// an event
/// log from the local computer.
///
///
[ResourceExposure(ResourceScope.Machine)] // See why someone would delete an event log
[ResourceConsumption(ResourceScope.Machine)]
public static void Delete(string logName) {
Delete(logName, ".");
}
///
///
/// Removes
/// an
/// event
/// log from the specified computer.
///
///
[ResourceExposure(ResourceScope.Machine)] // See why someone would delete an event log
[ResourceConsumption(ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static void Delete(string logName, string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName))
throw new ArgumentException(SR.GetString(SR.InvalidParameterFormat, "machineName"));
if (logName == null || logName.Length==0)
throw new ArgumentException(SR.GetString(SR.NoLogName));
if (!ValidLogName(logName, false))
throw new InvalidOperationException(SR.GetString(SR.BadLogName));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
//Check environment before even trying to play with the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventlogkey = null;
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
try {
eventlogkey = GetEventLogRegKey(machineName, true);
if (eventlogkey == null) {
// there's not even an event log service on the machine.
// or, more likely, we don't have the access to read the registry.
throw new InvalidOperationException(SR.GetString(SR.RegKeyNoAccess, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog", machineName));
}
using (RegistryKey logKey = eventlogkey.OpenSubKey(logName)) {
if (logKey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, logName, machineName));
//clear out log before trying to delete it
//that way, if we can't delete the log file, no entries will persist because it has been cleared
EventLog logToClear = new EventLog(logName, machineName);
try {
logToClear.Clear();
}
finally {
logToClear.Close();
}
//
string filename = null;
try {
//most of the time, the "File" key does not exist, but we'll still give it a whirl
filename = (string) logKey.GetValue("File");
}
catch { }
if (filename != null) {
try {
File.Delete(filename);
}
catch { }
}
}
// now delete the registry entry
eventlogkey.DeleteSubKeyTree(logName);
}
finally {
if (eventlogkey != null) eventlogkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
finally {
if (mutex != null) mutex.ReleaseMutex();
}
}
///
///
/// Removes the event source
/// registration from the event log of the local computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public static void DeleteEventSource(string source) {
DeleteEventSource(source, ".");
}
///
///
/// Removes
/// the application's event source registration from the specified computer.
///
///
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static void DeleteEventSource(string source, string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
RegistryKey key = null;
// First open the key read only so we can do some checks. This is important so we get the same
// exceptions even if we don't have write access to the reg key.
using (key = FindSourceRegistration(source, machineName, true)) {
if (key == null) {
if (machineName == null)
throw new ArgumentException(SR.GetString(SR.LocalSourceNotRegistered, source));
else
throw new ArgumentException(SR.GetString(SR.SourceNotRegistered, source, machineName, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog"));
}
// Check parent registry key (Event Log Name) and if it's equal to source, then throw an exception.
// The reason: each log registry key must always contain subkey (i.e. source) with the same name.
string keyname = key.Name;
int index = keyname.LastIndexOf('\\');
if ( string.Compare(keyname, index+1, source, 0, keyname.Length - index, StringComparison.Ordinal) == 0 )
throw new InvalidOperationException(SR.GetString(SR.CannotDeleteEqualSource, source));
}
try {
// now open it read/write to try to do the actual delete
key = FindSourceRegistration(source, machineName, false);
key.DeleteSubKeyTree(source);
if (!SkipRegPatch) {
string[] sources = (string[]) key.GetValue("Sources");
ArrayList newsources = new ArrayList(sources.Length - 1);
for (int i=0; i
///
internal void Dispose(bool disposing) {
try {
if (disposing) {
//Dispose unmanaged and managed resources
if (IsOpen) {
Close();
}
// This is probably unnecessary
if (readHandle != null) {
readHandle.Close();
readHandle = null;
}
if (writeHandle != null) {
writeHandle.Close();
writeHandle = null;
}
}
}
finally {
messageLibraries = null;
this.boolFlags[Flag_disposed] = true;
}
}
///
///
public void EndInit() {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
boolFlags[Flag_initializing] = false;
if (boolFlags[Flag_monitoring])
StartListening(currentMachineName, GetLogName(currentMachineName));
}
///
///
/// Determines whether the log
/// exists on the local computer.
///
///
public static bool Exists(string logName) {
return Exists(logName, ".");
}
///
///
/// Determines whether the
/// log exists on the specified computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static bool Exists(string logName, string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName))
throw new ArgumentException(SR.GetString(SR.InvalidParameterFormat, "machineName"));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
if (logName == null || logName.Length==0)
return false;
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventkey = null;
RegistryKey logKey = null;
try {
eventkey = GetEventLogRegKey(machineName, false);
if (eventkey == null)
return false;
logKey = eventkey.OpenSubKey(logName, false); // try to find log file key immediately.
return (logKey != null );
}
finally {
if (eventkey != null) eventkey.Close();
if (logKey != null) logKey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
// Try to find log file name with the same 8 first characters.
// Returns 'null' if no "same first 8 chars" log is found. logName.Length must be > 7
private static string FindSame8FirstCharsLog(RegistryKey keyParent, string logName) {
string logNameFirst8 = logName.Substring(0, 8);
string[] logNames = keyParent.GetSubKeyNames();
for (int i = 0; i < logNames.Length; i++) {
string currentLogName = logNames[i];
if ( currentLogName.Length >= 8 &&
string.Compare(currentLogName.Substring(0, 8), logNameFirst8, StringComparison.OrdinalIgnoreCase) == 0)
return currentLogName;
}
return null; // not found
}
///
/// Gets a RegistryKey that points to the LogName entry in the registry that is
/// the parent of the given source on the given machine, or null if none is found.
///
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static RegistryKey FindSourceRegistration(string source, string machineName, bool readOnly) {
return FindSourceRegistration(source, machineName, readOnly, false);
}
///
/// Gets a RegistryKey that points to the LogName entry in the registry that is
/// the parent of the given source on the given machine, or null if none is found.
///
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static RegistryKey FindSourceRegistration(string source, string machineName, bool readOnly, bool wantToCreate) {
if (source != null && source.Length != 0) {
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Any call to this function must have demmanded
// EventLogPermission before.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventkey = null;
try {
eventkey = GetEventLogRegKey(machineName, !readOnly);
if (eventkey == null) {
// there's not even an event log service on the machine.
// or, more likely, we don't have the access to read the registry.
return null;
}
StringBuilder inaccessibleLogs = null;
// Most machines will return only { "Application", "System", "Security" },
// but you can create your own if you want.
string[] logNames = eventkey.GetSubKeyNames();
for (int i = 0; i < logNames.Length; i++) {
// see if the source is registered in this log.
// NOTE: A source name must be unique across ALL LOGS!
RegistryKey sourceKey = null;
try {
RegistryKey logKey = eventkey.OpenSubKey(logNames[i], /*writable*/!readOnly);
if (logKey != null) {
sourceKey = logKey.OpenSubKey(source, /*writable*/!readOnly);
if (sourceKey != null) {
// found it
return logKey;
}
}
// else logKey is null, so we don't need to Close it
}
catch (UnauthorizedAccessException) {
if (inaccessibleLogs == null) {
inaccessibleLogs = new StringBuilder(logNames[i]);
}
else {
inaccessibleLogs.Append(", ");
inaccessibleLogs.Append(logNames[i]);
}
}
catch (SecurityException) {
if (inaccessibleLogs == null) {
inaccessibleLogs = new StringBuilder(logNames[i]);
}
else {
inaccessibleLogs.Append(", ");
inaccessibleLogs.Append(logNames[i]);
}
}
finally {
if (sourceKey != null) sourceKey.Close();
}
}
if (inaccessibleLogs != null)
throw new SecurityException(SR.GetString(wantToCreate ? SR.SomeLogsInaccessibleToCreate : SR.SomeLogsInaccessible, inaccessibleLogs.ToString()));
}
finally {
if (eventkey != null) eventkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
// didn't see it anywhere
}
return null;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal string FormatMessageWrapper(string dllNameList, uint messageNum, string[] insertionStrings) {
if (dllNameList == null)
return null;
if (insertionStrings == null)
insertionStrings = new string[0];
string[] listDll = dllNameList.Split(';');
// Find first mesage in DLL list
foreach ( string dllName in listDll) {
if (dllName == null || dllName.Length == 0)
continue;
SafeLibraryHandle hModule = null;
// if the EventLog is open, then we want to cache the library in our hashtable. Otherwise
// we'll just load it and free it after we're done.
if (IsOpen) {
hModule = MessageLibraries[dllName] as SafeLibraryHandle;
if (hModule == null || hModule.IsInvalid) {
hModule = SafeLibraryHandle.LoadLibraryEx(dllName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
MessageLibraries[dllName] = hModule;
}
}
else {
hModule = SafeLibraryHandle.LoadLibraryEx(dllName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
}
if (hModule.IsInvalid)
continue;
string msg = null;
try {
msg = EventLog.TryFormatMessage(hModule, messageNum, insertionStrings);
}
finally {
if (!IsOpen) {
hModule.Close();
}
}
if ( msg != null ) {
return msg;
}
}
return null;
}
///
/// Gets an array of EventLogEntry's, one for each entry in the log.
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal EventLogEntry[] GetAllEntries() {
// we could just call getEntryAt() on all the entries, but it'll be faster
// if we grab multiple entries at once.
string currentMachineName = this.machineName;
if (!IsOpenForRead)
OpenForRead(currentMachineName);
EventLogEntry[] entries = new EventLogEntry[EntryCount];
int idx = 0;
int oldestEntry = OldestEntryNumber;
int bytesRead;
int minBytesNeeded;
int error = 0;
while (idx < entries.Length) {
byte[] buf = new byte[BUF_SIZE];
bool success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ,
oldestEntry+idx, buf, buf.Length, out bytesRead, out minBytesNeeded);
if (!success) {
error = Marshal.GetLastWin32Error();
// NOTE, [....]: ERROR_PROC_NOT_FOUND used to get returned, but I think that
// was because I was calling GetLastError directly instead of GetLastWin32Error.
// Making the buffer bigger and trying again seemed to work. I've removed the check
// for ERROR_PROC_NOT_FOUND because I don't think it's necessary any more, but
// I can't prove it...
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Error from ReadEventLog is " + error.ToString(CultureInfo.InvariantCulture));
#if !RETRY_ON_ALL_ERRORS
if (error == NativeMethods.ERROR_INSUFFICIENT_BUFFER || error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
#endif
if (error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
// somewhere along the way the event log file changed - probably it
// got cleared while we were looping here. Reset the handle and
// try again.
Reset(currentMachineName);
}
// try again with a bigger buffer if necessary
else if (minBytesNeeded > buf.Length) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Increasing buffer size from " + buf.Length.ToString(CultureInfo.InvariantCulture) + " to " + minBytesNeeded.ToString(CultureInfo.InvariantCulture) + " bytes");
buf = new byte[minBytesNeeded];
}
success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ,
oldestEntry+idx, buf, buf.Length, out bytesRead, out minBytesNeeded);
if (!success)
// we'll just stop right here.
break;
#if !RETRY_ON_ALL_ERRORS
}
else {
break;
}
#endif
error = 0;
}
entries[idx] = new EventLogEntry(buf, 0, this);
int sum = IntFrom(buf, 0);
idx++;
while (sum < bytesRead && idx < entries.Length) {
entries[idx] = new EventLogEntry(buf, sum, this);
sum += IntFrom(buf, sum);
idx++;
}
}
if (idx != entries.Length) {
if (error != 0)
throw new InvalidOperationException(SR.GetString(SR.CantRetrieveEntries), SharedUtils.CreateSafeWin32Exception(error));
else
throw new InvalidOperationException(SR.GetString(SR.CantRetrieveEntries));
}
return entries;
}
///
///
/// Searches for all event logs on the local computer and
/// creates an array of
/// objects to contain the
/// list.
///
///
public static EventLog[] GetEventLogs() {
return GetEventLogs(".");
}
///
///
/// Searches for all event logs on the given computer and
/// creates an array of
/// objects to contain the
/// list.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static EventLog[] GetEventLogs(string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
string[] logNames = new string[0];
//SECREVIEW: Note that EventLogPermission is just demmanded above
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventkey = null;
try {
// we figure out what logs are on the machine by looking in the registry.
eventkey = GetEventLogRegKey(machineName, false);
if (eventkey == null)
// there's not even an event log service on the machine.
// or, more likely, we don't have the access to read the registry.
throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, EventLogKey, machineName));
// Most machines will return only { "Application", "System", "Security" },
// but you can create your own if you want.
logNames = eventkey.GetSubKeyNames();
}
finally {
if (eventkey != null) eventkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
// now create EventLog objects that point to those logs
EventLog[] logs = new EventLog[logNames.Length];
for (int i = 0; i < logNames.Length; i++) {
EventLog log = new EventLog(logNames[i], machineName);
logs[i] = log;
}
return logs;
}
///
/// Searches the cache for an entry with the given index
///
private int GetCachedEntryPos(int entryIndex) {
if (cache == null || (boolFlags[Flag_forwards] && entryIndex < firstCachedEntry) ||
(!boolFlags[Flag_forwards] && entryIndex > firstCachedEntry) || firstCachedEntry == -1) {
// the index falls before anything we have in the cache, or the cache
// is not yet valid
return -1;
}
// we only know where the beginning of the cache is, not the end, so even
// if it's past the end of the cache, we'll have to search through the whole
// cache to find out.
// we're betting heavily that the one they want to see now is close
// to the one they asked for last time. We start looking where we
// stopped last time.
// We have two loops, one to go forwards and one to go backwards. Only one
// of them will ever be executed.
while (lastSeenEntry < entryIndex) {
lastSeenEntry++;
if (boolFlags[Flag_forwards]) {
lastSeenPos = GetNextEntryPos(lastSeenPos);
if (lastSeenPos >= bytesCached)
break;
}
else {
lastSeenPos = GetPreviousEntryPos(lastSeenPos);
if (lastSeenPos < 0)
break;
}
}
while (lastSeenEntry > entryIndex) {
lastSeenEntry--;
if (boolFlags[Flag_forwards]) {
lastSeenPos = GetPreviousEntryPos(lastSeenPos);
if (lastSeenPos < 0)
break;
}
else {
lastSeenPos = GetNextEntryPos(lastSeenPos);
if (lastSeenPos >= bytesCached)
break;
}
}
if (lastSeenPos >= bytesCached) {
// we ran past the end. move back to the last one and return -1
lastSeenPos = GetPreviousEntryPos(lastSeenPos);
if (boolFlags[Flag_forwards])
lastSeenEntry--;
else
lastSeenEntry++;
return -1;
}
else if (lastSeenPos < 0) {
// we ran past the beginning. move back to the first one and return -1
lastSeenPos = 0;
if (boolFlags[Flag_forwards])
lastSeenEntry++;
else
lastSeenEntry--;
return -1;
}
else {
// we found it.
return lastSeenPos;
}
}
///
/// Gets the entry at the given index
///
internal EventLogEntry GetEntryAt(int index) {
EventLogEntry entry = GetEntryAtNoThrow(index);
if (entry == null)
throw new ArgumentException(SR.GetString(SR.IndexOutOfBounds, index.ToString(CultureInfo.CurrentCulture)));
return entry;
}
internal EventLogEntry GetEntryAtNoThrow(int index) {
if (!IsOpenForRead)
OpenForRead(this.machineName);
if (index < 0 || index >= EntryCount)
return null;
//
index += OldestEntryNumber;
EventLogEntry entry = null;
try {
entry = GetEntryWithOldest(index);
}
catch (InvalidOperationException) {
// This would be common in rapidly spinning EventLog (i.e. logs which are rapidly receiving
// new events while discarding old ones in a rolling fashion) or if the EventLog is cleared asynchronously.
//
// EventLogEntryCollection heuristics is little bit convoluted due to the inherent ----s.
// The enumerator predominantly operates on the index from the last “known” oldest entry
// (refreshing on every iteration is probalby not right here) and it has no notion of the
// collection size when it is created or while it is operating. It would keep on enumerating
// until the index become invalid.
//
// Throwing InvalidOperationException to let you know that your enumerator has been invalidated
// because of changes underneath is probably not the most useful behavior.
}
return entry;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private EventLogEntry GetEntryWithOldest(int index) {
EventLogEntry entry = null;
int entryPos = GetCachedEntryPos(index);
if (entryPos >= 0) {
entry = new EventLogEntry(cache, entryPos, this);
return entry;
}
string currentMachineName = this.machineName;
// if we haven't seen the one after this, we were probably going
// forwards.
int flags = 0;
if (GetCachedEntryPos(index+1) < 0) {
flags = NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ;
boolFlags[Flag_forwards] = true;
}
else {
flags = NativeMethods.BACKWARDS_READ | NativeMethods.SEEK_READ;
boolFlags[Flag_forwards] = false;
}
cache = new byte[BUF_SIZE];
int bytesRead;
int minBytesNeeded;
bool success = UnsafeNativeMethods.ReadEventLog(readHandle, flags, index,
cache, cache.Length, out bytesRead, out minBytesNeeded);
if (!success) {
int error = Marshal.GetLastWin32Error();
// NOTE, [....]: ERROR_PROC_NOT_FOUND used to get returned, but I think that
// was because I was calling GetLastError directly instead of GetLastWin32Error.
// Making the buffer bigger and trying again seemed to work. I've removed the check
// for ERROR_PROC_NOT_FOUND because I don't think it's necessary any more, but
// I can't prove it...
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Error from ReadEventLog is " + error.ToString(CultureInfo.InvariantCulture));
if (error == NativeMethods.ERROR_INSUFFICIENT_BUFFER || error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
if (error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
// Reset() sets the cache null. But since we're going to call ReadEventLog right after this,
// we need the cache to be something valid. We'll reuse the old byte array rather
// than creating a new one.
byte[] tempcache = cache;
Reset(currentMachineName);
cache = tempcache;
} else {
// try again with a bigger buffer.
if (minBytesNeeded > cache.Length) {
cache = new byte[minBytesNeeded];
}
}
success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ, index,
cache, cache.Length, out bytesRead, out minBytesNeeded);
}
if (!success) {
throw new InvalidOperationException(SR.GetString(SR.CantReadLogEntryAt, index.ToString(CultureInfo.CurrentCulture)), SharedUtils.CreateSafeWin32Exception());
}
}
bytesCached = bytesRead;
firstCachedEntry = index;
lastSeenEntry = index;
lastSeenPos = 0;
return new EventLogEntry(cache, 0, this);
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal static RegistryKey GetEventLogRegKey(string machine, bool writable) {
RegistryKey lmkey = null;
try {
if (machine.Equals(".")) {
lmkey = Registry.LocalMachine;
}
else {
lmkey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine);
}
if (lmkey != null)
return lmkey.OpenSubKey(EventLogKey, writable);
}
finally {
if (lmkey != null) lmkey.Close();
}
return null;
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private RegistryKey GetLogRegKey(string currentMachineName, bool writable) {
string logname = GetLogName(currentMachineName);
// we need to verify the logname here again because we might have tried to look it up
// based on the source and failed.
if (!ValidLogName(logname, false))
throw new InvalidOperationException(SR.GetString(SR.BadLogName));
RegistryKey eventkey = null;
RegistryKey logkey = null;
try {
eventkey = GetEventLogRegKey(currentMachineName, false);
if (eventkey == null)
throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, EventLogKey, currentMachineName));
logkey = eventkey.OpenSubKey(logname, writable);
if (logkey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, logname, currentMachineName));
}
finally {
if (eventkey != null) eventkey.Close();
}
return logkey;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private object GetLogRegValue(string currentMachineName, string valuename) {
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey logkey = null;
try {
logkey = GetLogRegKey(currentMachineName, false);
if (logkey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, GetLogName(currentMachineName), currentMachineName));
object val = logkey.GetValue(valuename);
return val;
}
finally {
if (logkey != null) logkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
///
/// Finds the index into the cache where the next entry starts
///
private int GetNextEntryPos(int pos) {
return pos + IntFrom(cache, pos);
}
///
/// Finds the index into the cache where the previous entry starts
///
private int GetPreviousEntryPos(int pos) {
// the entries in our buffer come back like this:
// ... ... ... ... ...
// In other words, the length for each entry is repeated at the beginning and
// at the end. This makes it easy to navigate forwards and backwards through
// the buffer.
return pos - IntFrom(cache, pos - 4);
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal static string GetDllPath(string machineName) {
return Path.Combine(SharedUtils.GetLatestBuildDllDirectory(machineName), DllName);
}
///
/// Extracts a 32-bit integer from the ubyte buffer, beginning at the byte offset
/// specified in offset.
///
private static int IntFrom(byte[] buf, int offset) {
// assumes Little Endian byte order.
return(unchecked((int)0xFF000000) & (buf[offset+3] << 24)) | (0xFF0000 & (buf[offset+2] << 16)) |
(0xFF00 & (buf[offset+1] << 8)) | (0xFF & (buf[offset]));
}
///
///
/// Determines whether an event source is registered on the local computer.
///
///
public static bool SourceExists(string source) {
return SourceExists(source, ".");
}
///
///
/// Determines whether an event
/// source is registered on a specified computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public static bool SourceExists(string source, string machineName) {
return SourceExists(source, machineName, false);
}
///
///
/// Determines whether an event
/// source is registered on a specified computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private static bool SourceExists(string source, string machineName, bool wantToCreate) {
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
permission.Demand();
using (RegistryKey keyFound = FindSourceRegistration(source, machineName, true, wantToCreate)) {
return (keyFound != null);
}
}
///
/// Gets the name of the log that the given source name is registered in.
///
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static string LogNameFromSourceName(string source, string machineName) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
return _InternalLogNameFromSourceName(source, machineName);
}
// No permission check, use with care!
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private static string _InternalLogNameFromSourceName(string source, string machineName) {
using (RegistryKey key = FindSourceRegistration(source, machineName, true)) {
if (key == null)
return "";
else {
string name = key.Name;
int whackPos = name.LastIndexOf('\\');
// this will work even if whackPos is -1
return name.Substring(whackPos+1);
}
}
}
[ComVisible(false)]
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
if (action < OverflowAction.DoNotOverwrite || action > OverflowAction.OverwriteOlder)
throw new InvalidEnumArgumentException("action", (int)action, typeof(OverflowAction));
// this is a long because in the if statement we may need to store values as
// large as UInt32.MaxValue - 1. This would overflow an int.
long retentionvalue = (long) action;
if (action == OverflowAction.OverwriteOlder) {
if (retentionDays < 1 || retentionDays > 365)
throw new ArgumentOutOfRangeException(SR.GetString(SR.RentionDaysOutOfRange));
retentionvalue = (long) retentionDays * SecondsPerDay;
}
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
using (RegistryKey logkey = GetLogRegKey(currentMachineName, true))
logkey.SetValue("Retention", retentionvalue, RegistryValueKind.DWord);
}
///
/// Opens the event log with read access
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private void OpenForRead(string currentMachineName) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::OpenForRead");
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
//Cannot allocate the readHandle if the object has been disposed, since finalization has been suppressed.
if (this.boolFlags[Flag_disposed])
throw new ObjectDisposedException(GetType().Name);
string logname = GetLogName(currentMachineName);
if (logname == null || logname.Length==0)
throw new ArgumentException(SR.GetString(SR.MissingLogProperty));
if (! Exists(logname, currentMachineName) ) // do not open non-existing Log [[....]]
throw new InvalidOperationException( SR.GetString(SR.LogDoesNotExists, logname, currentMachineName) );
//Check environment before calling api
SharedUtils.CheckEnvironment();
// Clean up cache variables.
// [[....]] The initilizing code is put here to guarantee, that first read of events
// from log file will start by filling up the cache buffer.
lastSeenEntry = 0;
lastSeenPos = 0;
bytesCached = 0;
firstCachedEntry = -1;
readHandle = SafeEventLogReadHandle.OpenEventLog(currentMachineName, logname);
if (readHandle.IsInvalid) {
Win32Exception e = null;
if (Marshal.GetLastWin32Error() != 0) {
e = SharedUtils.CreateSafeWin32Exception();
}
throw new InvalidOperationException(SR.GetString(SR.CantOpenLog, logname.ToString(), currentMachineName), e);
}
}
///
/// Opens the event log with write access
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private void OpenForWrite(string currentMachineName) {
//Cannot allocate the writeHandle if the object has been disposed, since finalization has been suppressed.
if (this.boolFlags[Flag_disposed])
throw new ObjectDisposedException(GetType().Name);
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::OpenForWrite");
if (sourceName == null || sourceName.Length==0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToOpen));
//Check environment before calling api
SharedUtils.CheckEnvironment();
writeHandle = SafeEventLogWriteHandle.RegisterEventSource(currentMachineName, sourceName);
if (writeHandle.IsInvalid) {
Win32Exception e = null;
if (Marshal.GetLastWin32Error() != 0) {
e = SharedUtils.CreateSafeWin32Exception();
}
throw new InvalidOperationException(SR.GetString(SR.CantOpenLogAccess, sourceName), e);
}
}
[ComVisible(false)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public void RegisterDisplayName(string resourceFile, long resourceId) {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
using (RegistryKey logkey = GetLogRegKey(currentMachineName, true)) {
logkey.SetValue("DisplayNameFile", resourceFile, RegistryValueKind.ExpandString);
logkey.SetValue("DisplayNameID", resourceId, RegistryValueKind.DWord);
}
}
private void Reset(string currentMachineName) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Reset");
// save the state we're in now
bool openRead = IsOpenForRead;
bool openWrite = IsOpenForWrite;
bool isMonitoring = boolFlags[Flag_monitoring];
bool isListening = boolFlags[Flag_registeredAsListener];
// close everything down
Close(currentMachineName);
cache = null;
// and get us back into the same state as before
if (openRead)
OpenForRead(currentMachineName);
if (openWrite)
OpenForWrite(currentMachineName);
if (isListening)
StartListening(currentMachineName, GetLogName(currentMachineName));
boolFlags[Flag_monitoring] = isMonitoring;
}
[HostProtection(Synchronization=true)]
private static void RemoveListenerComponent(EventLogInternal component, string compLogName) {
lock (InternalSyncObject) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::RemoveListenerComponent(" + compLogName + ")");
LogListeningInfo info = (LogListeningInfo) listenerInfos[compLogName];
Debug.Assert(info != null);
// remove the requested component from the list.
info.listeningComponents.Remove(component);
if (info.listeningComponents.Count != 0)
return;
// if that was the last interested compononent, destroy the handles and stop listening.
info.handleOwner.Dispose();
//Unregister the thread pool wait handle
info.registeredWaitHandle.Unregister(info.waitHandle);
// close the handle
info.waitHandle.Close();
listenerInfos[compLogName] = null;
}
}
// The reasoning behind filling these values is historical. WS03 RTM had a ----
// between registry changes and EventLog service, which made the service wait 2 secs
// before retrying to see whether all regkey values are present. To avoid this
// potential lag (worst case up to n*2 secs where n is the number of required regkeys)
// between creation and being able to write events, we started filling some of these
// values explicitly but for XP and latter OS releases like WS03 SP1 and Vista this
// is not necessary and in some cases like the "File" key it's plain wrong to write.
private static void SetSpecialLogRegValues(RegistryKey logKey, string logName) {
// Set all the default values for this log. AutoBackupLogfiles only makes sense in
// Win2000 SP4, WinXP SP1, and Win2003, but it should alright elsewhere.
// Since we use this method on the existing system logs as well as our own,
// we need to make sure we don't overwrite any existing values.
if (logKey.GetValue("MaxSize") == null)
logKey.SetValue("MaxSize", DefaultMaxSize, RegistryValueKind.DWord);
if (logKey.GetValue("AutoBackupLogFiles") == null)
logKey.SetValue("AutoBackupLogFiles", 0, RegistryValueKind.DWord);
if (!SkipRegPatch) {
// In Vista, "retention of events for 'n' days" concept is removed
if (logKey.GetValue("Retention") == null)
logKey.SetValue("Retention", DefaultRetention, RegistryValueKind.DWord);
if (logKey.GetValue("File") == null) {
string filename;
if (logName.Length > 8)
filename = @"%SystemRoot%\System32\config\" + logName.Substring(0,8) + ".evt";
else
filename = @"%SystemRoot%\System32\config\" + logName + ".evt";
logKey.SetValue("File", filename, RegistryValueKind.ExpandString);
}
}
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private static void SetSpecialSourceRegValues(RegistryKey sourceLogKey, EventSourceCreationData sourceData) {
if (String.IsNullOrEmpty(sourceData.MessageResourceFile))
sourceLogKey.SetValue("EventMessageFile", GetDllPath(sourceData.MachineName), RegistryValueKind.ExpandString);
else
sourceLogKey.SetValue("EventMessageFile", FixupPath(sourceData.MessageResourceFile), RegistryValueKind.ExpandString);
if (!String.IsNullOrEmpty(sourceData.ParameterResourceFile))
sourceLogKey.SetValue("ParameterMessageFile", FixupPath(sourceData.ParameterResourceFile), RegistryValueKind.ExpandString);
if (!String.IsNullOrEmpty(sourceData.CategoryResourceFile)) {
sourceLogKey.SetValue("CategoryMessageFile", FixupPath(sourceData.CategoryResourceFile), RegistryValueKind.ExpandString);
sourceLogKey.SetValue("CategoryCount", sourceData.CategoryCount, RegistryValueKind.DWord);
}
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static string FixupPath(string path) {
if (path[0] == '%')
return path;
else
return Path.GetFullPath(path);
}
///
/// Sets up the event monitoring mechanism. We don't track event log changes
/// unless someone is interested, so we set this up on demand.
///
[HostProtection(Synchronization=true, ExternalThreading=true)]
private void StartListening(string currentMachineName, string currentLogName) {
// make sure we don't fire events for entries that are already there
Debug.Assert(!boolFlags[Flag_registeredAsListener], "StartListening called with boolFlags[Flag_registeredAsListener] true.");
lastSeenCount = EntryCount + OldestEntryNumber;
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StartListening: lastSeenCount = " + lastSeenCount);
AddListenerComponent(this, currentMachineName, currentLogName);
boolFlags[Flag_registeredAsListener] = true;
}
private void StartRaisingEvents(string currentMachineName, string currentLogName) {
if (!boolFlags[Flag_initializing] && !boolFlags[Flag_monitoring] && !m_Parent.ComponentDesignMode) {
StartListening(currentMachineName, currentLogName);
}
boolFlags[Flag_monitoring] = true;
}
private static void StaticCompletionCallback(object context, bool wasSignaled) {
LogListeningInfo info = (LogListeningInfo) context;
// get a snapshot of the components to fire the event on
EventLogInternal[] interestedComponents = (EventLogInternal[])info.listeningComponents.ToArray(typeof(EventLogInternal));
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StaticCompletionCallback: notifying " + interestedComponents.Length + " components.");
for (int i = 0; i < interestedComponents.Length; i++) {
try {
if (interestedComponents[i] != null) {
interestedComponents[i].CompletionCallback(null);
}
} catch (ObjectDisposedException) {
// The EventLog that was registered to listen has been disposed. Nothing much we can do here
// we don't want to propigate this error up as it will likely be unhandled and will cause the app
// to crash.
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StaticCompletionCallback: ignored an ObjectDisposedException");
}
}
}
///
/// Tears down the event listening mechanism. This is called when the last
/// interested party removes their event handler.
///
[HostProtection(Synchronization=true, ExternalThreading=true)]
private void StopListening(/*string currentMachineName,*/ string currentLogName) {
Debug.Assert(boolFlags[Flag_registeredAsListener], "StopListening called without StartListening.");
RemoveListenerComponent(this, currentLogName);
boolFlags[Flag_registeredAsListener] = false;
}
///
///
private void StopRaisingEvents(/*string currentMachineName,*/ string currentLogName) {
if (!boolFlags[Flag_initializing] && boolFlags[Flag_monitoring] && !m_Parent.ComponentDesignMode) {
StopListening(currentLogName);
}
boolFlags[Flag_monitoring] = false;
}
// CharIsPrintable used to be Char.IsPrintable, but Jay removed it and
// is forcing people to use the Unicode categories themselves. Copied
// the code here.
private static bool CharIsPrintable(char c) {
UnicodeCategory uc = Char.GetUnicodeCategory(c);
return (!(uc == UnicodeCategory.Control) || (uc == UnicodeCategory.Format) ||
(uc == UnicodeCategory.LineSeparator) || (uc == UnicodeCategory.ParagraphSeparator) ||
(uc == UnicodeCategory.OtherNotAssigned));
}
// SECREVIEW: Make sure this method catches all the strange cases.
internal static bool ValidLogName(string logName, bool ignoreEmpty) {
// No need to trim here since the next check will verify that there are no spaces.
// We need to ignore the empty string as an invalid log name sometimes because it can
// be passed in from our default constructor.
if (logName.Length == 0 && !ignoreEmpty)
return false;
//any space, backslash, asterisk, or question mark is bad
//any non-printable characters are also bad
foreach (char c in logName)
if (!CharIsPrintable(c) || (c == '\\') || (c == '*') || (c == '?'))
return false;
return true;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private void VerifyAndCreateSource(string sourceName, string currentMachineName) {
if (boolFlags[Flag_sourceVerified])
return;
if (!SourceExists(sourceName, currentMachineName, true)) {
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
if (!SourceExists(sourceName, currentMachineName, true)) {
if (GetLogName(currentMachineName) == null)
this.logName = "Application";
// we automatically add an entry in the registry if there's not already
// one there for this source
CreateEventSource(new EventSourceCreationData(sourceName, GetLogName(currentMachineName), currentMachineName));
// The user may have set a custom log and tried to read it before trying to
// write. Due to a quirk in the event log API, we would have opened the Application
// log to read (because the custom log wasn't there). Now that we've created
// the custom log, we should close so that when we re-open, we get a read
// handle on the _new_ log instead of the Application log.
Reset(currentMachineName);
}
else {
string rightLogName = LogNameFromSourceName(sourceName, currentMachineName);
string currentLogName = GetLogName(currentMachineName);
if (rightLogName != null && currentLogName != null && String.Compare(rightLogName, currentLogName, StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException(SR.GetString(SR.LogSourceMismatch, Source.ToString(), currentLogName, rightLogName));
}
}
finally {
if (mutex != null) {
mutex.ReleaseMutex();
mutex.Close();
}
}
}
else {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
string rightLogName = _InternalLogNameFromSourceName(sourceName, currentMachineName);
string currentLogName = GetLogName(currentMachineName);
if (rightLogName != null && currentLogName != null && String.Compare(rightLogName, currentLogName, StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException(SR.GetString(SR.LogSourceMismatch, Source.ToString(), currentLogName, rightLogName));
}
boolFlags[Flag_sourceVerified] = true;
}
///
///
/// Writes an information type entry with the given message text to the event log.
///
///
public void WriteEntry(string message) {
WriteEntry(message, EventLogEntryType.Information, (short) 0, 0, null);
}
///
///
/// Writes an entry of the specified to the event log. Valid types are
/// , , ,
/// , and .
///
///
public void WriteEntry(string message, EventLogEntryType type) {
WriteEntry(message, type, (short) 0, 0, null);
}
///
///
/// Writes an entry of the specified
/// and with the
/// user-defined
/// to
/// the event log.
///
///
public void WriteEntry(string message, EventLogEntryType type, int eventID) {
WriteEntry(message, type, eventID, 0, null);
}
///
///
/// Writes an entry of the specified type with the
/// user-defined and
/// to the event log. The
/// can be used by the event viewer to filter events in the log.
///
///
public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) {
WriteEntry(message, type, eventID, category, null);
}
///
///
/// Writes an entry of the specified type with the
/// user-defined and to the event log, and appends binary data to
/// the message. The Event Viewer does not interpret this data; it
/// displays raw data only in a combined hexadecimal and text format.
///
///
public void WriteEntry(string message, EventLogEntryType type, int eventID, short category,
byte[] rawData) {
if (eventID < 0 || eventID > ushort.MaxValue)
throw new ArgumentException(SR.GetString(SR.EventID, eventID, 0, (int)ushort.MaxValue));
if (Source.Length == 0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
if (!Enum.IsDefined(typeof(EventLogEntryType), type))
throw new InvalidEnumArgumentException("type", (int)type, typeof(EventLogEntryType));
string currentMachineName = machineName;
if (!boolFlags[Flag_writeGranted]) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
boolFlags[Flag_writeGranted] = true;
}
VerifyAndCreateSource(sourceName, currentMachineName);
// now that the source has been hooked up to our DLL, we can use "normal"
// (message-file driven) logging techniques.
// Our DLL has 64K different entries; all of them just display the first
// insertion string.
InternalWriteEvent((uint)eventID, (ushort)category, type, new string[] { message}, rawData, currentMachineName);
}
[ComVisible(false)]
public void WriteEvent(EventInstance instance, params Object[] values) {
WriteEvent(instance, null, values);
}
[ComVisible(false)]
public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) {
if (instance == null)
throw new ArgumentNullException("instance");
if (Source.Length == 0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
string currentMachineName = machineName;
if (!boolFlags[Flag_writeGranted]) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
boolFlags[Flag_writeGranted] = true;
}
VerifyAndCreateSource(Source, currentMachineName);
string[] strings = null;
if (values != null) {
strings = new string[values.Length];
for (int i=0; i= 256)
throw new ArgumentException(SR.GetString(SR.TooManyReplacementStrings));
for (int i = 0; i < strings.Length; i++) {
if (strings[i] == null)
strings[i] = String.Empty;
// make sure the strings aren't too long. MSDN says each string has a limit of 32k (32768) characters, but
// experimentation shows that it doesn't like anything larger than 32766
if (strings[i].Length > 32766)
throw new ArgumentException(SR.GetString(SR.LogEntryTooLong));
}
if (rawData == null)
rawData = new byte[0];
if (Source.Length == 0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
if (!IsOpenForWrite)
OpenForWrite(currentMachineName);
// pin each of the strings in memory
IntPtr[] stringRoots = new IntPtr[strings.Length];
GCHandle[] stringHandles = new GCHandle[strings.Length];
GCHandle stringsRootHandle = GCHandle.Alloc(stringRoots, GCHandleType.Pinned);
try {
for (int strIndex = 0; strIndex < strings.Length; strIndex++) {
stringHandles[strIndex] = GCHandle.Alloc(strings[strIndex], GCHandleType.Pinned);
stringRoots[strIndex] = stringHandles[strIndex].AddrOfPinnedObject();
}
byte[] sid = null;
// actually report the event
bool success = UnsafeNativeMethods.ReportEvent(writeHandle, (short) type, category, eventID,
sid, (short) strings.Length, rawData.Length, new HandleRef(this, stringsRootHandle.AddrOfPinnedObject()), rawData);
if (!success) {
//Trace("WriteEvent", "Throwing Win32Exception");
throw SharedUtils.CreateSafeWin32Exception();
}
}
finally {
// now free the pinned strings
for (int i = 0; i < strings.Length; i++) {
if (stringHandles[i].IsAllocated)
stringHandles[i].Free();
}
stringsRootHandle.Free();
}
}
private class LogListeningInfo {
public EventLogInternal handleOwner;
public RegisteredWaitHandle registeredWaitHandle;
public WaitHandle waitHandle;
public ArrayList listeningComponents = new ArrayList();
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
//#define RETRY_ON_ALL_ERRORS
/*
* EventLogInternal contains most of the logic for interacting with the Windows Event Log.
* The reason for this class existing (instead of the logic being in EventLog itself) is
* that we'd like to be able to have the invariant that the Source, MachineName and Log Name
* don't change across the lifetime of an event log object, but we exposed public setters for
* these properties. EventLog holds a reference to an EventLogInternal instance, plumbs all
* calls to it and replaces it when any of these properites change.
*
* Note that EventLogInternal also holds a reference back to the EventLog instnace that is
* exposing it so it is not prematurely collected.
*/
namespace System.Diagnostics {
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics;
using System;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.ComponentModel.Design;
using System.Security;
using System.Security.Permissions;
using System.Reflection;
using System.Runtime.Versioning;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
///
///
/// Provides interaction with Windows 2000 event logs.
///
///
internal class EventLogInternal : IDisposable, ISupportInitialize {
// a collection over all our entries. Since the class holds no state, we
// can just hand the same instance out every time.
private EventLogEntryCollection entriesCollection;
// the name of the log we're reading from or writing to
internal string logName;
// used in monitoring for event postings.
private int lastSeenCount;
// holds the machine we're on, or null if it's the local machine
internal string machineName;
// the delegate to call when an event arrives
internal EntryWrittenEventHandler onEntryWrittenHandler;
// holds onto the handle for reading
private SafeEventLogReadHandle readHandle;
// the source name - used only when writing
internal string sourceName;
// holds onto the handle for writing
private SafeEventLogWriteHandle writeHandle;
private string logDisplayName;
// cache system state variables
// the initial size of the buffer (it can be made larger if necessary)
private const int BUF_SIZE = 40000;
// the number of bytes in the cache that belong to entries (not necessarily
// the same as BUF_SIZE, because the cache only holds whole entries)
private int bytesCached;
// the actual cache buffer
private byte[] cache;
// the number of the entry at the beginning of the cache
private int firstCachedEntry = -1;
// the number of the entry that we got out of the cache most recently
private int lastSeenEntry;
// where that entry was
private int lastSeenPos;
//support for threadpool based deferred execution
private ISynchronizeInvoke synchronizingObject;
// the EventLog object that publicly exposes this instance.
private EventLog m_Parent;
private const string EventLogKey = "SYSTEM\\CurrentControlSet\\Services\\EventLog";
internal const string DllName = "EventLogMessages.dll";
private const string eventLogMutexName = "netfxeventlog.1.0";
private const int SecondsPerDay = 60 * 60 * 24;
private const int DefaultMaxSize = 512*1024;
private const int DefaultRetention = 7*SecondsPerDay;
private const int Flag_notifying = 0x1; // keeps track of whether we're notifying our listeners - to prevent double notifications
private const int Flag_forwards = 0x2; // whether the cache contains entries in forwards order (true) or backwards (false)
private const int Flag_initializing = 0x4;
internal const int Flag_monitoring = 0x8;
private const int Flag_registeredAsListener = 0x10;
private const int Flag_writeGranted = 0x20;
private const int Flag_disposed = 0x100;
private const int Flag_sourceVerified= 0x200;
private BitVector32 boolFlags = new BitVector32();
private Hashtable messageLibraries;
private static Hashtable listenerInfos = new Hashtable(StringComparer.OrdinalIgnoreCase);
private Object m_InstanceLockObject;
private Object InstanceLockObject {
get {
if (m_InstanceLockObject == null) {
Object o = new Object();
Interlocked.CompareExchange(ref m_InstanceLockObject, o, null);
}
return m_InstanceLockObject;
}
}
private static Object s_InternalSyncObject;
private static Object InternalSyncObject {
get {
if (s_InternalSyncObject == null) {
Object o = new Object();
Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
}
return s_InternalSyncObject;
}
}
// Whether we need backward compatible OS patch work or not
private static bool s_CheckedOsVersion;
private static bool s_SkipRegPatch;
private static bool SkipRegPatch {
get {
if (!s_CheckedOsVersion) {
OperatingSystem os = Environment.OSVersion;
s_SkipRegPatch = (os.Platform == PlatformID.Win32NT) && (os.Version.Major > 5);
s_CheckedOsVersion = true;
}
return s_SkipRegPatch;
}
}
///
///
/// Initializes a new instance of the
/// class.
///
///
public EventLogInternal() : this("", ".", "", null) {
}
///
/// [To be supplied.]
///
public EventLogInternal(string logName) : this(logName, ".", "", null) {
}
///
/// [To be supplied.]
///
public EventLogInternal(string logName, string machineName) : this(logName, machineName, "", null) {
}
///
/// [To be supplied.]
///
public EventLogInternal(string logName, string machineName, string source) : this(logName, machineName, source, null) {
}
///
/// [To be supplied.]
///
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, oldLog.machineName doesn't change")]
public EventLogInternal(string logName, string machineName, string source, EventLog parent) {
//look out for invalid log names
if (logName == null)
throw new ArgumentNullException("logName");
if (!ValidLogName(logName, true))
throw new ArgumentException(SR.GetString(SR.BadLogName));
if (!SyntaxCheck.CheckMachineName(machineName))
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
permission.Demand();
this.machineName = machineName;
this.logName = logName;
this.sourceName = source;
readHandle = null;
writeHandle = null;
boolFlags[Flag_forwards] = true;
m_Parent = parent;
}
///
///
/// Gets the contents of the event log.
///
///
public EventLogEntryCollection Entries {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
if (entriesCollection == null)
entriesCollection = new EventLogEntryCollection(this);
return entriesCollection;
}
}
///
/// Gets the number of entries in the log
///
internal int EntryCount {
get {
if (!IsOpenForRead)
OpenForRead(this.machineName);
int count;
bool success = UnsafeNativeMethods.GetNumberOfEventLogRecords(readHandle, out count);
if (!success)
throw SharedUtils.CreateSafeWin32Exception();
return count;
}
}
///
/// Determines whether the event log is open in either read or write access
///
private bool IsOpen {
get {
return readHandle != null || writeHandle != null;
}
}
///
/// Determines whether the event log is open with read access
///
private bool IsOpenForRead {
get {
return readHandle != null;
}
}
///
/// Determines whether the event log is open with write access.
///
private bool IsOpenForWrite {
get {
return writeHandle != null;
}
}
///
///
///
///
public string LogDisplayName {
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
get {
if (logDisplayName == null) {
string currentMachineName = this.machineName;
if (GetLogName(currentMachineName) != null) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLogPermission is just demmanded above
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey logkey = null;
try {
// we figure out what logs are on the machine by looking in the registry.
logkey = GetLogRegKey(currentMachineName, false);
if (logkey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, GetLogName(currentMachineName), currentMachineName));
string resourceDll = (string)logkey.GetValue("DisplayNameFile");
if (resourceDll == null)
logDisplayName = GetLogName(currentMachineName);
else {
int resourceId = (int)logkey.GetValue("DisplayNameID");
logDisplayName = FormatMessageWrapper(resourceDll, (uint) resourceId, null);
if (logDisplayName == null)
logDisplayName = GetLogName(currentMachineName);
}
}
finally {
if (logkey != null) logkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
}
return logDisplayName;
}
}
///
///
/// Gets or sets the name of the log to read from and write to.
///
///
public string Log {
get {
string currentMachineName = this.machineName;
if (logName == null || logName.Length == 0) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
}
return GetLogName(currentMachineName);
}
}
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private string GetLogName(string currentMachineName)
{
if ((logName == null || logName.Length == 0) && sourceName != null && sourceName.Length!=0) {
// they've told us a source, but they haven't told us a log name.
// try to deduce the log name from the source name.
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
logName = _InternalLogNameFromSourceName(sourceName, currentMachineName);
}
return logName;
}
///
///
/// Gets or sets the name of the computer on which to read or write events.
///
///
public string MachineName {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
return currentMachineName;
}
}
[ComVisible(false)]
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "[....]: MaximumKilobytes is the name of this property.")]
public long MaximumKilobytes {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
object val = GetLogRegValue(currentMachineName, "MaxSize");
if (val != null) {
int intval = (int) val; // cast to an int first to unbox
return ((uint)intval) / 1024; // then convert to kilobytes
}
// 512k is the default value
return 0x200;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
set {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
// valid range is 64 KB to 4 GB
if (value < 64 || value > 0x3FFFC0 || value % 64 != 0)
throw new ArgumentOutOfRangeException("MaximumKilobytes", SR.GetString(SR.MaximumKilobytesOutOfRange));
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
long regvalue = value * 1024; // convert to bytes
int i = unchecked((int)regvalue);
using (RegistryKey logkey = GetLogRegKey(currentMachineName, true))
logkey.SetValue("MaxSize", i, RegistryValueKind.DWord);
}
}
internal Hashtable MessageLibraries {
get {
if (messageLibraries == null)
messageLibraries = new Hashtable(StringComparer.OrdinalIgnoreCase);
return messageLibraries;
}
}
[ComVisible(false)]
public OverflowAction OverflowAction {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
object retentionobj = GetLogRegValue(currentMachineName, "Retention");
if (retentionobj != null) {
int retention = (int) retentionobj;
if (retention == 0)
return OverflowAction.OverwriteAsNeeded;
else if (retention == -1)
return OverflowAction.DoNotOverwrite;
else
return OverflowAction.OverwriteOlder;
}
// default value as listed in MSDN
return OverflowAction.OverwriteOlder;
}
}
[ComVisible(false)]
public int MinimumRetentionDays {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
object retentionobj = GetLogRegValue(currentMachineName, "Retention");
if (retentionobj != null) {
int retention = (int) retentionobj;
if (retention == 0 || retention == -1)
return retention;
else
return (int) (((double) retention) / SecondsPerDay);
}
return 7;
}
}
///
///
public bool EnableRaisingEvents {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
return boolFlags[Flag_monitoring];
}
set {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
if (m_Parent.ComponentDesignMode)
this.boolFlags[Flag_monitoring] = value;
else {
if (value)
StartRaisingEvents(currentMachineName, GetLogName(currentMachineName));
else
StopRaisingEvents(/*currentMachineName,*/ GetLogName(currentMachineName));
}
}
}
private int OldestEntryNumber {
get {
if (!IsOpenForRead)
OpenForRead(this.machineName);
int num;
bool success = UnsafeNativeMethods.GetOldestEventLogRecord(readHandle, out num);
if (!success)
throw SharedUtils.CreateSafeWin32Exception();
// When the event log is empty, GetOldestEventLogRecord returns 0.
// But then after an entry is written, it returns 1. We need to go from
// the last num to the current.
if (num == 0)
num = 1;
return num;
}
}
internal SafeEventLogReadHandle ReadHandle {
get {
if (!IsOpenForRead)
OpenForRead(this.machineName);
return readHandle;
}
}
///
///
/// Represents the object used to marshal the event handler
/// calls issued as a result of an
/// change.
///
///
public ISynchronizeInvoke SynchronizingObject {
[HostProtection(Synchronization=true)]
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
if (this.synchronizingObject == null && m_Parent.ComponentDesignMode) {
IDesignerHost host = (IDesignerHost)m_Parent.ComponentGetService(typeof(IDesignerHost));
if (host != null) {
object baseComponent = host.RootComponent;
if (baseComponent != null && baseComponent is ISynchronizeInvoke)
this.synchronizingObject = (ISynchronizeInvoke)baseComponent;
}
}
return this.synchronizingObject;
}
set {
this.synchronizingObject = value;
}
}
///
///
/// Gets or
/// sets the application name (source name) to register and use when writing to the event log.
///
///
public string Source {
get {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
return sourceName;
}
}
[HostProtection(Synchronization=true)]
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private static void AddListenerComponent(EventLogInternal component, string compMachineName, string compLogName) {
lock (InternalSyncObject) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::AddListenerComponent(" + compLogName + ")");
LogListeningInfo info = (LogListeningInfo) listenerInfos[compLogName];
if (info != null) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::AddListenerComponent: listener already active.");
info.listeningComponents.Add(component);
return;
}
info = new LogListeningInfo();
info.listeningComponents.Add(component);
info.handleOwner = new EventLogInternal(compLogName, compMachineName);
// tell the event log system about it
info.waitHandle = new AutoResetEvent(false);
bool success = UnsafeNativeMethods.NotifyChangeEventLog(info.handleOwner.ReadHandle, info.waitHandle.SafeWaitHandle);
if (!success)
throw new InvalidOperationException(SR.GetString(SR.CantMonitorEventLog), SharedUtils.CreateSafeWin32Exception());
info.registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(info.waitHandle, new WaitOrTimerCallback(StaticCompletionCallback), info, -1, false);
listenerInfos[compLogName] = info;
}
}
///
///
/// Occurs when an entry is written to the event log.
///
///
public event EntryWrittenEventHandler EntryWritten {
add {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
onEntryWrittenHandler += value;
}
remove {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
onEntryWrittenHandler -= value;
}
}
///
///
public void BeginInit() {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
if (boolFlags[Flag_initializing]) throw new InvalidOperationException(SR.GetString(SR.InitTwice));
boolFlags[Flag_initializing] = true;
if (boolFlags[Flag_monitoring])
StopListening(GetLogName(currentMachineName));
}
///
///
/// Clears
/// the event log by removing all entries from it.
///
///
[ResourceExposure(ResourceScope.Machine)] // Should anyone ever call this, other than an event log viewer?
[ResourceConsumption(ResourceScope.Machine)]
public void Clear() {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
if (!IsOpenForRead)
OpenForRead(currentMachineName);
bool success = UnsafeNativeMethods.ClearEventLog(readHandle, NativeMethods.NullHandleRef);
if (!success) {
// Ignore file not found errors. ClearEventLog seems to try to delete the file where the event log is
// stored. If it can't find it, it gives an error.
int error = Marshal.GetLastWin32Error();
if (error != NativeMethods.ERROR_FILE_NOT_FOUND)
throw SharedUtils.CreateSafeWin32Exception();
}
// now that we've cleared the event log, we need to re-open our handles, because
// the internal state of the event log has changed.
Reset(currentMachineName);
}
///
///
/// Closes the event log and releases read and write handles.
///
///
[ResourceExposure(ResourceScope.None)]
public void Close() {
Close(this.machineName);
}
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, currentMachineName doesn't change")]
private void Close(string currentMachineName) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close");
//Trace("Close", "Closing the event log");
if (readHandle != null) {
try {
readHandle.Close();
}
catch (IOException) {
throw SharedUtils.CreateSafeWin32Exception();
}
readHandle = null;
//Trace("Close", "Closed read handle");
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close: closed read handle");
}
if (writeHandle != null) {
try {
writeHandle.Close();
}
catch (IOException) {
throw SharedUtils.CreateSafeWin32Exception();
}
writeHandle = null;
//Trace("Close", "Closed write handle");
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Close: closed write handle");
}
if (boolFlags[Flag_monitoring])
StopRaisingEvents(/*currentMachineName,*/ GetLogName(currentMachineName));
if (messageLibraries != null) {
foreach (SafeLibraryHandle handle in messageLibraries.Values)
handle.Close();
messageLibraries = null;
}
boolFlags[Flag_sourceVerified] = false;
}
///
///
/// Called when the threadpool is ready for us to handle a status change.
///
private void CompletionCallback(object context) {
if (boolFlags[Flag_disposed]) {
// This object has been disposed previously, ignore firing the event.
return;
}
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: starting at " + lastSeenCount.ToString(CultureInfo.InvariantCulture));
lock (InstanceLockObject) {
if (boolFlags[Flag_notifying]) {
// don't do double notifications.
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: aborting because we're already notifying.");
return;
}
boolFlags[Flag_notifying] = true;
}
int i = lastSeenCount;
try {
int oldest = OldestEntryNumber;
int count = EntryCount + oldest;
// Ensure lastSeenCount is within bounds. This deals with the case where the event log has been cleared between
// notifications.
if (lastSeenCount < oldest || lastSeenCount > count) {
lastSeenCount = oldest;
i = lastSeenCount;
}
// NOTE, [....]: We have a double loop here so that we access the
// EntryCount property as infrequently as possible. (It may be expensive
// to get the property.) Even though there are two loops, they will together
// only execute as many times as (final value of EntryCount) - lastSeenCount.
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: OldestEntryNumber is " + OldestEntryNumber + ", EntryCount is " + EntryCount);
while (i < count) {
while (i < count) {
EventLogEntry entry = GetEntryWithOldest(i);
if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired)
this.SynchronizingObject.BeginInvoke(this.onEntryWrittenHandler, new object[]{this, new EntryWrittenEventArgs(entry)});
else
onEntryWrittenHandler(this, new EntryWrittenEventArgs(entry));
i++;
}
oldest = OldestEntryNumber;
count = EntryCount + oldest;
}
}
catch (Exception e) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: Caught exception notifying event handlers: " + e.ToString());
}
// if the user cleared the log while we were receiving events, the call to GetEntryWithOldest above could have
// thrown an exception and i could be too large. Make sure we don't set lastSeenCount to something bogus.
int newCount = EntryCount + OldestEntryNumber;
if (i > newCount)
lastSeenCount = newCount;
else
lastSeenCount = i;
lock (InstanceLockObject) {
boolFlags[Flag_notifying] = false;
}
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::CompletionStatusChanged: finishing at " + lastSeenCount.ToString(CultureInfo.InvariantCulture));
}
///
/// Establishes an application, using the
/// specified , as a valid event source for
/// writing entries
/// to a log on the local computer. This method
/// can also be used to create
/// a new custom log on the local computer.
///
public static void CreateEventSource(string source, string logName) {
CreateEventSource(new EventSourceCreationData(source, logName, "."));
}
///
/// Establishes an application, using the specified
/// as a valid event source for writing
/// entries to a log on the computer
/// specified by . This method can also be used to create a new
/// custom log on the given computer.
///
[Obsolete("This method has been deprecated. Please use System.Diagnostics.EventLog.CreateEventSource(EventSourceCreationData sourceData) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
public static void CreateEventSource(string source, string logName, string machineName) {
CreateEventSource(new EventSourceCreationData(source, logName, machineName));
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public static void CreateEventSource(EventSourceCreationData sourceData) {
if (sourceData == null)
throw new ArgumentNullException("sourceData");
string logName = sourceData.LogName;
string source = sourceData.Source;
string machineName = sourceData.MachineName;
// verify parameters
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Checking arguments");
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
if (logName == null || logName.Length==0)
logName = "Application";
if (!ValidLogName(logName, false))
throw new ArgumentException(SR.GetString(SR.BadLogName));
if (source == null || source.Length==0)
throw new ArgumentException(SR.GetString(SR.MissingParameter, "source"));
if (source.Length + EventLogKey.Length > 254)
throw new ArgumentException(SR.GetString(SR.ParameterTooLong, "source", 254 - EventLogKey.Length));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Calling SourceExists");
if (SourceExists(source, machineName, true)) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: SourceExists returned true");
// don't let them register a source if it already exists
// this makes more sense than just doing it anyway, because the source might
// be registered under a different log name, and we don't want to create
// duplicates.
if (".".Equals(machineName))
throw new ArgumentException(SR.GetString(SR.LocalSourceAlreadyExists, source));
else
throw new ArgumentException(SR.GetString(SR.SourceAlreadyExists, source, machineName));
}
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting DllPath");
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey baseKey = null;
RegistryKey eventKey = null;
RegistryKey logKey = null;
RegistryKey sourceLogKey = null;
RegistryKey sourceKey = null;
try {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "CreateEventSource: Getting local machine regkey");
if (machineName == ".")
baseKey = Registry.LocalMachine;
else
baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName);
eventKey = baseKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\EventLog", true);
if (eventKey == null) {
if (!".".Equals(machineName))
throw new InvalidOperationException(SR.GetString(SR.RegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source, machineName));
else
throw new InvalidOperationException(SR.GetString(SR.LocalRegKeyMissing, "SYSTEM\\CurrentControlSet\\Services\\EventLog", logName, source));
}
// The event log system only treats the first 8 characters of the log name as
// significant. If they're creating a new log, but that new log has the same
// first 8 characters as another log, the system will think they're the same.
// Throw an exception to let them know.
logKey = eventKey.OpenSubKey(logName, true);
if (logKey == null && logName.Length >= 8) {
// check for Windows embedded logs file names
string logNameFirst8 = logName.Substring(0,8);
if ( string.Compare(logNameFirst8,"AppEvent",StringComparison.OrdinalIgnoreCase) ==0 ||
string.Compare(logNameFirst8,"SecEvent",StringComparison.OrdinalIgnoreCase) ==0 ||
string.Compare(logNameFirst8,"SysEvent",StringComparison.OrdinalIgnoreCase) ==0 )
throw new ArgumentException(SR.GetString(SR.InvalidCustomerLogName, logName));
string sameLogName = FindSame8FirstCharsLog(eventKey, logName);
if ( sameLogName != null )
throw new ArgumentException(SR.GetString(SR.DuplicateLogName, logName, sameLogName));
}
bool createLogKey = (logKey == null);
if (createLogKey) {
if (SourceExists(logName, machineName, true)) {
// don't let them register a log name that already
// exists as source name, a source with the same
// name as the log will have to be created by default
if (".".Equals(machineName))
throw new ArgumentException(SR.GetString(SR.LocalLogAlreadyExistsAsSource, logName));
else
throw new ArgumentException(SR.GetString(SR.LogAlreadyExistsAsSource, logName, machineName));
}
logKey = eventKey.CreateSubKey(logName);
// NOTE: We shouldn't set "Sources" explicitly, the OS will automatically set it.
// The EventLog service doesn't use it for anything it is just an helping hand for event viewer filters.
// Writing this value explicitly might confuse the service as it might perceive it as a change and
// start initializing again
if (!SkipRegPatch)
logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
SetSpecialLogRegValues(logKey, logName);
// A source with the same name as the log has to be created
// by default. It is the behavior expected by EventLog API.
sourceLogKey = logKey.CreateSubKey(logName);
SetSpecialSourceRegValues(sourceLogKey, sourceData);
}
if (logName != source) {
if (!createLogKey) {
SetSpecialLogRegValues(logKey, logName);
if (!SkipRegPatch) {
string[] sources = logKey.GetValue("Sources") as string[];
if (sources == null)
logKey.SetValue("Sources", new string[] {logName, source}, RegistryValueKind.MultiString);
else {
// We have a ---- with OS EventLog here.
// OS might update Sources as well. We should avoid writing the
// source name if OS beats us.
if( Array.IndexOf(sources, source) == -1) {
string[] newsources = new string[sources.Length + 1];
Array.Copy(sources, newsources, sources.Length);
newsources[sources.Length] = source;
logKey.SetValue("Sources", newsources, RegistryValueKind.MultiString);
}
}
}
}
sourceKey = logKey.CreateSubKey(source);
SetSpecialSourceRegValues(sourceKey, sourceData);
}
}
finally {
if (baseKey != null)
baseKey.Close();
if (eventKey != null)
eventKey.Close();
if (logKey != null) {
logKey.Flush();
logKey.Close();
}
if (sourceLogKey != null) {
sourceLogKey.Flush();
sourceLogKey.Close();
}
if (sourceKey != null) {
sourceKey.Flush();
sourceKey.Close();
}
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
finally {
if (mutex != null) {
mutex.ReleaseMutex();
mutex.Close();
}
}
}
///
///
/// Removes
/// an event
/// log from the local computer.
///
///
[ResourceExposure(ResourceScope.Machine)] // See why someone would delete an event log
[ResourceConsumption(ResourceScope.Machine)]
public static void Delete(string logName) {
Delete(logName, ".");
}
///
///
/// Removes
/// an
/// event
/// log from the specified computer.
///
///
[ResourceExposure(ResourceScope.Machine)] // See why someone would delete an event log
[ResourceConsumption(ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static void Delete(string logName, string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName))
throw new ArgumentException(SR.GetString(SR.InvalidParameterFormat, "machineName"));
if (logName == null || logName.Length==0)
throw new ArgumentException(SR.GetString(SR.NoLogName));
if (!ValidLogName(logName, false))
throw new InvalidOperationException(SR.GetString(SR.BadLogName));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
//Check environment before even trying to play with the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventlogkey = null;
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
try {
eventlogkey = GetEventLogRegKey(machineName, true);
if (eventlogkey == null) {
// there's not even an event log service on the machine.
// or, more likely, we don't have the access to read the registry.
throw new InvalidOperationException(SR.GetString(SR.RegKeyNoAccess, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog", machineName));
}
using (RegistryKey logKey = eventlogkey.OpenSubKey(logName)) {
if (logKey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, logName, machineName));
//clear out log before trying to delete it
//that way, if we can't delete the log file, no entries will persist because it has been cleared
EventLog logToClear = new EventLog(logName, machineName);
try {
logToClear.Clear();
}
finally {
logToClear.Close();
}
//
string filename = null;
try {
//most of the time, the "File" key does not exist, but we'll still give it a whirl
filename = (string) logKey.GetValue("File");
}
catch { }
if (filename != null) {
try {
File.Delete(filename);
}
catch { }
}
}
// now delete the registry entry
eventlogkey.DeleteSubKeyTree(logName);
}
finally {
if (eventlogkey != null) eventlogkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
finally {
if (mutex != null) mutex.ReleaseMutex();
}
}
///
///
/// Removes the event source
/// registration from the event log of the local computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public static void DeleteEventSource(string source) {
DeleteEventSource(source, ".");
}
///
///
/// Removes
/// the application's event source registration from the specified computer.
///
///
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static void DeleteEventSource(string source, string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
RegistryKey key = null;
// First open the key read only so we can do some checks. This is important so we get the same
// exceptions even if we don't have write access to the reg key.
using (key = FindSourceRegistration(source, machineName, true)) {
if (key == null) {
if (machineName == null)
throw new ArgumentException(SR.GetString(SR.LocalSourceNotRegistered, source));
else
throw new ArgumentException(SR.GetString(SR.SourceNotRegistered, source, machineName, "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\EventLog"));
}
// Check parent registry key (Event Log Name) and if it's equal to source, then throw an exception.
// The reason: each log registry key must always contain subkey (i.e. source) with the same name.
string keyname = key.Name;
int index = keyname.LastIndexOf('\\');
if ( string.Compare(keyname, index+1, source, 0, keyname.Length - index, StringComparison.Ordinal) == 0 )
throw new InvalidOperationException(SR.GetString(SR.CannotDeleteEqualSource, source));
}
try {
// now open it read/write to try to do the actual delete
key = FindSourceRegistration(source, machineName, false);
key.DeleteSubKeyTree(source);
if (!SkipRegPatch) {
string[] sources = (string[]) key.GetValue("Sources");
ArrayList newsources = new ArrayList(sources.Length - 1);
for (int i=0; i
///
internal void Dispose(bool disposing) {
try {
if (disposing) {
//Dispose unmanaged and managed resources
if (IsOpen) {
Close();
}
// This is probably unnecessary
if (readHandle != null) {
readHandle.Close();
readHandle = null;
}
if (writeHandle != null) {
writeHandle.Close();
writeHandle = null;
}
}
}
finally {
messageLibraries = null;
this.boolFlags[Flag_disposed] = true;
}
}
///
///
public void EndInit() {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
boolFlags[Flag_initializing] = false;
if (boolFlags[Flag_monitoring])
StartListening(currentMachineName, GetLogName(currentMachineName));
}
///
///
/// Determines whether the log
/// exists on the local computer.
///
///
public static bool Exists(string logName) {
return Exists(logName, ".");
}
///
///
/// Determines whether the
/// log exists on the specified computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static bool Exists(string logName, string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName))
throw new ArgumentException(SR.GetString(SR.InvalidParameterFormat, "machineName"));
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
if (logName == null || logName.Length==0)
return false;
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Note that EventLog permission is demanded above.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventkey = null;
RegistryKey logKey = null;
try {
eventkey = GetEventLogRegKey(machineName, false);
if (eventkey == null)
return false;
logKey = eventkey.OpenSubKey(logName, false); // try to find log file key immediately.
return (logKey != null );
}
finally {
if (eventkey != null) eventkey.Close();
if (logKey != null) logKey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
// Try to find log file name with the same 8 first characters.
// Returns 'null' if no "same first 8 chars" log is found. logName.Length must be > 7
private static string FindSame8FirstCharsLog(RegistryKey keyParent, string logName) {
string logNameFirst8 = logName.Substring(0, 8);
string[] logNames = keyParent.GetSubKeyNames();
for (int i = 0; i < logNames.Length; i++) {
string currentLogName = logNames[i];
if ( currentLogName.Length >= 8 &&
string.Compare(currentLogName.Substring(0, 8), logNameFirst8, StringComparison.OrdinalIgnoreCase) == 0)
return currentLogName;
}
return null; // not found
}
///
/// Gets a RegistryKey that points to the LogName entry in the registry that is
/// the parent of the given source on the given machine, or null if none is found.
///
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static RegistryKey FindSourceRegistration(string source, string machineName, bool readOnly) {
return FindSourceRegistration(source, machineName, readOnly, false);
}
///
/// Gets a RegistryKey that points to the LogName entry in the registry that is
/// the parent of the given source on the given machine, or null if none is found.
///
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static RegistryKey FindSourceRegistration(string source, string machineName, bool readOnly, bool wantToCreate) {
if (source != null && source.Length != 0) {
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
//SECREVIEW: Any call to this function must have demmanded
// EventLogPermission before.
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventkey = null;
try {
eventkey = GetEventLogRegKey(machineName, !readOnly);
if (eventkey == null) {
// there's not even an event log service on the machine.
// or, more likely, we don't have the access to read the registry.
return null;
}
StringBuilder inaccessibleLogs = null;
// Most machines will return only { "Application", "System", "Security" },
// but you can create your own if you want.
string[] logNames = eventkey.GetSubKeyNames();
for (int i = 0; i < logNames.Length; i++) {
// see if the source is registered in this log.
// NOTE: A source name must be unique across ALL LOGS!
RegistryKey sourceKey = null;
try {
RegistryKey logKey = eventkey.OpenSubKey(logNames[i], /*writable*/!readOnly);
if (logKey != null) {
sourceKey = logKey.OpenSubKey(source, /*writable*/!readOnly);
if (sourceKey != null) {
// found it
return logKey;
}
}
// else logKey is null, so we don't need to Close it
}
catch (UnauthorizedAccessException) {
if (inaccessibleLogs == null) {
inaccessibleLogs = new StringBuilder(logNames[i]);
}
else {
inaccessibleLogs.Append(", ");
inaccessibleLogs.Append(logNames[i]);
}
}
catch (SecurityException) {
if (inaccessibleLogs == null) {
inaccessibleLogs = new StringBuilder(logNames[i]);
}
else {
inaccessibleLogs.Append(", ");
inaccessibleLogs.Append(logNames[i]);
}
}
finally {
if (sourceKey != null) sourceKey.Close();
}
}
if (inaccessibleLogs != null)
throw new SecurityException(SR.GetString(wantToCreate ? SR.SomeLogsInaccessibleToCreate : SR.SomeLogsInaccessible, inaccessibleLogs.ToString()));
}
finally {
if (eventkey != null) eventkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
// didn't see it anywhere
}
return null;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal string FormatMessageWrapper(string dllNameList, uint messageNum, string[] insertionStrings) {
if (dllNameList == null)
return null;
if (insertionStrings == null)
insertionStrings = new string[0];
string[] listDll = dllNameList.Split(';');
// Find first mesage in DLL list
foreach ( string dllName in listDll) {
if (dllName == null || dllName.Length == 0)
continue;
SafeLibraryHandle hModule = null;
// if the EventLog is open, then we want to cache the library in our hashtable. Otherwise
// we'll just load it and free it after we're done.
if (IsOpen) {
hModule = MessageLibraries[dllName] as SafeLibraryHandle;
if (hModule == null || hModule.IsInvalid) {
hModule = SafeLibraryHandle.LoadLibraryEx(dllName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
MessageLibraries[dllName] = hModule;
}
}
else {
hModule = SafeLibraryHandle.LoadLibraryEx(dllName, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
}
if (hModule.IsInvalid)
continue;
string msg = null;
try {
msg = EventLog.TryFormatMessage(hModule, messageNum, insertionStrings);
}
finally {
if (!IsOpen) {
hModule.Close();
}
}
if ( msg != null ) {
return msg;
}
}
return null;
}
///
/// Gets an array of EventLogEntry's, one for each entry in the log.
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal EventLogEntry[] GetAllEntries() {
// we could just call getEntryAt() on all the entries, but it'll be faster
// if we grab multiple entries at once.
string currentMachineName = this.machineName;
if (!IsOpenForRead)
OpenForRead(currentMachineName);
EventLogEntry[] entries = new EventLogEntry[EntryCount];
int idx = 0;
int oldestEntry = OldestEntryNumber;
int bytesRead;
int minBytesNeeded;
int error = 0;
while (idx < entries.Length) {
byte[] buf = new byte[BUF_SIZE];
bool success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ,
oldestEntry+idx, buf, buf.Length, out bytesRead, out minBytesNeeded);
if (!success) {
error = Marshal.GetLastWin32Error();
// NOTE, [....]: ERROR_PROC_NOT_FOUND used to get returned, but I think that
// was because I was calling GetLastError directly instead of GetLastWin32Error.
// Making the buffer bigger and trying again seemed to work. I've removed the check
// for ERROR_PROC_NOT_FOUND because I don't think it's necessary any more, but
// I can't prove it...
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Error from ReadEventLog is " + error.ToString(CultureInfo.InvariantCulture));
#if !RETRY_ON_ALL_ERRORS
if (error == NativeMethods.ERROR_INSUFFICIENT_BUFFER || error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
#endif
if (error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
// somewhere along the way the event log file changed - probably it
// got cleared while we were looping here. Reset the handle and
// try again.
Reset(currentMachineName);
}
// try again with a bigger buffer if necessary
else if (minBytesNeeded > buf.Length) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Increasing buffer size from " + buf.Length.ToString(CultureInfo.InvariantCulture) + " to " + minBytesNeeded.ToString(CultureInfo.InvariantCulture) + " bytes");
buf = new byte[minBytesNeeded];
}
success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ,
oldestEntry+idx, buf, buf.Length, out bytesRead, out minBytesNeeded);
if (!success)
// we'll just stop right here.
break;
#if !RETRY_ON_ALL_ERRORS
}
else {
break;
}
#endif
error = 0;
}
entries[idx] = new EventLogEntry(buf, 0, this);
int sum = IntFrom(buf, 0);
idx++;
while (sum < bytesRead && idx < entries.Length) {
entries[idx] = new EventLogEntry(buf, sum, this);
sum += IntFrom(buf, sum);
idx++;
}
}
if (idx != entries.Length) {
if (error != 0)
throw new InvalidOperationException(SR.GetString(SR.CantRetrieveEntries), SharedUtils.CreateSafeWin32Exception(error));
else
throw new InvalidOperationException(SR.GetString(SR.CantRetrieveEntries));
}
return entries;
}
///
///
/// Searches for all event logs on the local computer and
/// creates an array of
/// objects to contain the
/// list.
///
///
public static EventLog[] GetEventLogs() {
return GetEventLogs(".");
}
///
///
/// Searches for all event logs on the given computer and
/// creates an array of
/// objects to contain the
/// list.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static EventLog[] GetEventLogs(string machineName) {
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
//Check environment before looking at the registry
SharedUtils.CheckEnvironment();
string[] logNames = new string[0];
//SECREVIEW: Note that EventLogPermission is just demmanded above
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey eventkey = null;
try {
// we figure out what logs are on the machine by looking in the registry.
eventkey = GetEventLogRegKey(machineName, false);
if (eventkey == null)
// there's not even an event log service on the machine.
// or, more likely, we don't have the access to read the registry.
throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, EventLogKey, machineName));
// Most machines will return only { "Application", "System", "Security" },
// but you can create your own if you want.
logNames = eventkey.GetSubKeyNames();
}
finally {
if (eventkey != null) eventkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
// now create EventLog objects that point to those logs
EventLog[] logs = new EventLog[logNames.Length];
for (int i = 0; i < logNames.Length; i++) {
EventLog log = new EventLog(logNames[i], machineName);
logs[i] = log;
}
return logs;
}
///
/// Searches the cache for an entry with the given index
///
private int GetCachedEntryPos(int entryIndex) {
if (cache == null || (boolFlags[Flag_forwards] && entryIndex < firstCachedEntry) ||
(!boolFlags[Flag_forwards] && entryIndex > firstCachedEntry) || firstCachedEntry == -1) {
// the index falls before anything we have in the cache, or the cache
// is not yet valid
return -1;
}
// we only know where the beginning of the cache is, not the end, so even
// if it's past the end of the cache, we'll have to search through the whole
// cache to find out.
// we're betting heavily that the one they want to see now is close
// to the one they asked for last time. We start looking where we
// stopped last time.
// We have two loops, one to go forwards and one to go backwards. Only one
// of them will ever be executed.
while (lastSeenEntry < entryIndex) {
lastSeenEntry++;
if (boolFlags[Flag_forwards]) {
lastSeenPos = GetNextEntryPos(lastSeenPos);
if (lastSeenPos >= bytesCached)
break;
}
else {
lastSeenPos = GetPreviousEntryPos(lastSeenPos);
if (lastSeenPos < 0)
break;
}
}
while (lastSeenEntry > entryIndex) {
lastSeenEntry--;
if (boolFlags[Flag_forwards]) {
lastSeenPos = GetPreviousEntryPos(lastSeenPos);
if (lastSeenPos < 0)
break;
}
else {
lastSeenPos = GetNextEntryPos(lastSeenPos);
if (lastSeenPos >= bytesCached)
break;
}
}
if (lastSeenPos >= bytesCached) {
// we ran past the end. move back to the last one and return -1
lastSeenPos = GetPreviousEntryPos(lastSeenPos);
if (boolFlags[Flag_forwards])
lastSeenEntry--;
else
lastSeenEntry++;
return -1;
}
else if (lastSeenPos < 0) {
// we ran past the beginning. move back to the first one and return -1
lastSeenPos = 0;
if (boolFlags[Flag_forwards])
lastSeenEntry++;
else
lastSeenEntry--;
return -1;
}
else {
// we found it.
return lastSeenPos;
}
}
///
/// Gets the entry at the given index
///
internal EventLogEntry GetEntryAt(int index) {
EventLogEntry entry = GetEntryAtNoThrow(index);
if (entry == null)
throw new ArgumentException(SR.GetString(SR.IndexOutOfBounds, index.ToString(CultureInfo.CurrentCulture)));
return entry;
}
internal EventLogEntry GetEntryAtNoThrow(int index) {
if (!IsOpenForRead)
OpenForRead(this.machineName);
if (index < 0 || index >= EntryCount)
return null;
//
index += OldestEntryNumber;
EventLogEntry entry = null;
try {
entry = GetEntryWithOldest(index);
}
catch (InvalidOperationException) {
// This would be common in rapidly spinning EventLog (i.e. logs which are rapidly receiving
// new events while discarding old ones in a rolling fashion) or if the EventLog is cleared asynchronously.
//
// EventLogEntryCollection heuristics is little bit convoluted due to the inherent ----s.
// The enumerator predominantly operates on the index from the last “known” oldest entry
// (refreshing on every iteration is probalby not right here) and it has no notion of the
// collection size when it is created or while it is operating. It would keep on enumerating
// until the index become invalid.
//
// Throwing InvalidOperationException to let you know that your enumerator has been invalidated
// because of changes underneath is probably not the most useful behavior.
}
return entry;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private EventLogEntry GetEntryWithOldest(int index) {
EventLogEntry entry = null;
int entryPos = GetCachedEntryPos(index);
if (entryPos >= 0) {
entry = new EventLogEntry(cache, entryPos, this);
return entry;
}
string currentMachineName = this.machineName;
// if we haven't seen the one after this, we were probably going
// forwards.
int flags = 0;
if (GetCachedEntryPos(index+1) < 0) {
flags = NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ;
boolFlags[Flag_forwards] = true;
}
else {
flags = NativeMethods.BACKWARDS_READ | NativeMethods.SEEK_READ;
boolFlags[Flag_forwards] = false;
}
cache = new byte[BUF_SIZE];
int bytesRead;
int minBytesNeeded;
bool success = UnsafeNativeMethods.ReadEventLog(readHandle, flags, index,
cache, cache.Length, out bytesRead, out minBytesNeeded);
if (!success) {
int error = Marshal.GetLastWin32Error();
// NOTE, [....]: ERROR_PROC_NOT_FOUND used to get returned, but I think that
// was because I was calling GetLastError directly instead of GetLastWin32Error.
// Making the buffer bigger and trying again seemed to work. I've removed the check
// for ERROR_PROC_NOT_FOUND because I don't think it's necessary any more, but
// I can't prove it...
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "Error from ReadEventLog is " + error.ToString(CultureInfo.InvariantCulture));
if (error == NativeMethods.ERROR_INSUFFICIENT_BUFFER || error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
if (error == NativeMethods.ERROR_EVENTLOG_FILE_CHANGED) {
// Reset() sets the cache null. But since we're going to call ReadEventLog right after this,
// we need the cache to be something valid. We'll reuse the old byte array rather
// than creating a new one.
byte[] tempcache = cache;
Reset(currentMachineName);
cache = tempcache;
} else {
// try again with a bigger buffer.
if (minBytesNeeded > cache.Length) {
cache = new byte[minBytesNeeded];
}
}
success = UnsafeNativeMethods.ReadEventLog(readHandle, NativeMethods.FORWARDS_READ | NativeMethods.SEEK_READ, index,
cache, cache.Length, out bytesRead, out minBytesNeeded);
}
if (!success) {
throw new InvalidOperationException(SR.GetString(SR.CantReadLogEntryAt, index.ToString(CultureInfo.CurrentCulture)), SharedUtils.CreateSafeWin32Exception());
}
}
bytesCached = bytesRead;
firstCachedEntry = index;
lastSeenEntry = index;
lastSeenPos = 0;
return new EventLogEntry(cache, 0, this);
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal static RegistryKey GetEventLogRegKey(string machine, bool writable) {
RegistryKey lmkey = null;
try {
if (machine.Equals(".")) {
lmkey = Registry.LocalMachine;
}
else {
lmkey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine);
}
if (lmkey != null)
return lmkey.OpenSubKey(EventLogKey, writable);
}
finally {
if (lmkey != null) lmkey.Close();
}
return null;
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private RegistryKey GetLogRegKey(string currentMachineName, bool writable) {
string logname = GetLogName(currentMachineName);
// we need to verify the logname here again because we might have tried to look it up
// based on the source and failed.
if (!ValidLogName(logname, false))
throw new InvalidOperationException(SR.GetString(SR.BadLogName));
RegistryKey eventkey = null;
RegistryKey logkey = null;
try {
eventkey = GetEventLogRegKey(currentMachineName, false);
if (eventkey == null)
throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, EventLogKey, currentMachineName));
logkey = eventkey.OpenSubKey(logname, writable);
if (logkey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, logname, currentMachineName));
}
finally {
if (eventkey != null) eventkey.Close();
}
return logkey;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private object GetLogRegValue(string currentMachineName, string valuename) {
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
RegistryKey logkey = null;
try {
logkey = GetLogRegKey(currentMachineName, false);
if (logkey == null)
throw new InvalidOperationException(SR.GetString(SR.MissingLog, GetLogName(currentMachineName), currentMachineName));
object val = logkey.GetValue(valuename);
return val;
}
finally {
if (logkey != null) logkey.Close();
// Revert registry and environment permission asserts
CodeAccessPermission.RevertAssert();
}
}
///
/// Finds the index into the cache where the next entry starts
///
private int GetNextEntryPos(int pos) {
return pos + IntFrom(cache, pos);
}
///
/// Finds the index into the cache where the previous entry starts
///
private int GetPreviousEntryPos(int pos) {
// the entries in our buffer come back like this:
// ... ... ... ... ...
// In other words, the length for each entry is repeated at the beginning and
// at the end. This makes it easy to navigate forwards and backwards through
// the buffer.
return pos - IntFrom(cache, pos - 4);
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal static string GetDllPath(string machineName) {
return Path.Combine(SharedUtils.GetLatestBuildDllDirectory(machineName), DllName);
}
///
/// Extracts a 32-bit integer from the ubyte buffer, beginning at the byte offset
/// specified in offset.
///
private static int IntFrom(byte[] buf, int offset) {
// assumes Little Endian byte order.
return(unchecked((int)0xFF000000) & (buf[offset+3] << 24)) | (0xFF0000 & (buf[offset+2] << 16)) |
(0xFF00 & (buf[offset+1] << 8)) | (0xFF & (buf[offset]));
}
///
///
/// Determines whether an event source is registered on the local computer.
///
///
public static bool SourceExists(string source) {
return SourceExists(source, ".");
}
///
///
/// Determines whether an event
/// source is registered on a specified computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public static bool SourceExists(string source, string machineName) {
return SourceExists(source, machineName, false);
}
///
///
/// Determines whether an event
/// source is registered on a specified computer.
///
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private static bool SourceExists(string source, string machineName, bool wantToCreate) {
if (!SyntaxCheck.CheckMachineName(machineName)) {
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
}
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, machineName);
permission.Demand();
using (RegistryKey keyFound = FindSourceRegistration(source, machineName, true, wantToCreate)) {
return (keyFound != null);
}
}
///
/// Gets the name of the log that the given source name is registered in.
///
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
public static string LogNameFromSourceName(string source, string machineName) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, machineName);
permission.Demand();
return _InternalLogNameFromSourceName(source, machineName);
}
// No permission check, use with care!
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private static string _InternalLogNameFromSourceName(string source, string machineName) {
using (RegistryKey key = FindSourceRegistration(source, machineName, true)) {
if (key == null)
return "";
else {
string name = key.Name;
int whackPos = name.LastIndexOf('\\');
// this will work even if whackPos is -1
return name.Substring(whackPos+1);
}
}
}
[ComVisible(false)]
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
public void ModifyOverflowPolicy(OverflowAction action, int retentionDays) {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
if (action < OverflowAction.DoNotOverwrite || action > OverflowAction.OverwriteOlder)
throw new InvalidEnumArgumentException("action", (int)action, typeof(OverflowAction));
// this is a long because in the if statement we may need to store values as
// large as UInt32.MaxValue - 1. This would overflow an int.
long retentionvalue = (long) action;
if (action == OverflowAction.OverwriteOlder) {
if (retentionDays < 1 || retentionDays > 365)
throw new ArgumentOutOfRangeException(SR.GetString(SR.RentionDaysOutOfRange));
retentionvalue = (long) retentionDays * SecondsPerDay;
}
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
using (RegistryKey logkey = GetLogRegKey(currentMachineName, true))
logkey.SetValue("Retention", retentionvalue, RegistryValueKind.DWord);
}
///
/// Opens the event log with read access
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private void OpenForRead(string currentMachineName) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::OpenForRead");
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
//Cannot allocate the readHandle if the object has been disposed, since finalization has been suppressed.
if (this.boolFlags[Flag_disposed])
throw new ObjectDisposedException(GetType().Name);
string logname = GetLogName(currentMachineName);
if (logname == null || logname.Length==0)
throw new ArgumentException(SR.GetString(SR.MissingLogProperty));
if (! Exists(logname, currentMachineName) ) // do not open non-existing Log [[....]]
throw new InvalidOperationException( SR.GetString(SR.LogDoesNotExists, logname, currentMachineName) );
//Check environment before calling api
SharedUtils.CheckEnvironment();
// Clean up cache variables.
// [[....]] The initilizing code is put here to guarantee, that first read of events
// from log file will start by filling up the cache buffer.
lastSeenEntry = 0;
lastSeenPos = 0;
bytesCached = 0;
firstCachedEntry = -1;
readHandle = SafeEventLogReadHandle.OpenEventLog(currentMachineName, logname);
if (readHandle.IsInvalid) {
Win32Exception e = null;
if (Marshal.GetLastWin32Error() != 0) {
e = SharedUtils.CreateSafeWin32Exception();
}
throw new InvalidOperationException(SR.GetString(SR.CantOpenLog, logname.ToString(), currentMachineName), e);
}
}
///
/// Opens the event log with write access
///
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private void OpenForWrite(string currentMachineName) {
//Cannot allocate the writeHandle if the object has been disposed, since finalization has been suppressed.
if (this.boolFlags[Flag_disposed])
throw new ObjectDisposedException(GetType().Name);
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::OpenForWrite");
if (sourceName == null || sourceName.Length==0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToOpen));
//Check environment before calling api
SharedUtils.CheckEnvironment();
writeHandle = SafeEventLogWriteHandle.RegisterEventSource(currentMachineName, sourceName);
if (writeHandle.IsInvalid) {
Win32Exception e = null;
if (Marshal.GetLastWin32Error() != 0) {
e = SharedUtils.CreateSafeWin32Exception();
}
throw new InvalidOperationException(SR.GetString(SR.CantOpenLogAccess, sourceName), e);
}
}
[ComVisible(false)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public void RegisterDisplayName(string resourceFile, long resourceId) {
string currentMachineName = this.machineName;
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Administer, currentMachineName);
permission.Demand();
PermissionSet permissionSet = EventLog._UnsafeGetAssertPermSet();
permissionSet.Assert();
using (RegistryKey logkey = GetLogRegKey(currentMachineName, true)) {
logkey.SetValue("DisplayNameFile", resourceFile, RegistryValueKind.ExpandString);
logkey.SetValue("DisplayNameID", resourceId, RegistryValueKind.DWord);
}
}
private void Reset(string currentMachineName) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::Reset");
// save the state we're in now
bool openRead = IsOpenForRead;
bool openWrite = IsOpenForWrite;
bool isMonitoring = boolFlags[Flag_monitoring];
bool isListening = boolFlags[Flag_registeredAsListener];
// close everything down
Close(currentMachineName);
cache = null;
// and get us back into the same state as before
if (openRead)
OpenForRead(currentMachineName);
if (openWrite)
OpenForWrite(currentMachineName);
if (isListening)
StartListening(currentMachineName, GetLogName(currentMachineName));
boolFlags[Flag_monitoring] = isMonitoring;
}
[HostProtection(Synchronization=true)]
private static void RemoveListenerComponent(EventLogInternal component, string compLogName) {
lock (InternalSyncObject) {
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::RemoveListenerComponent(" + compLogName + ")");
LogListeningInfo info = (LogListeningInfo) listenerInfos[compLogName];
Debug.Assert(info != null);
// remove the requested component from the list.
info.listeningComponents.Remove(component);
if (info.listeningComponents.Count != 0)
return;
// if that was the last interested compononent, destroy the handles and stop listening.
info.handleOwner.Dispose();
//Unregister the thread pool wait handle
info.registeredWaitHandle.Unregister(info.waitHandle);
// close the handle
info.waitHandle.Close();
listenerInfos[compLogName] = null;
}
}
// The reasoning behind filling these values is historical. WS03 RTM had a ----
// between registry changes and EventLog service, which made the service wait 2 secs
// before retrying to see whether all regkey values are present. To avoid this
// potential lag (worst case up to n*2 secs where n is the number of required regkeys)
// between creation and being able to write events, we started filling some of these
// values explicitly but for XP and latter OS releases like WS03 SP1 and Vista this
// is not necessary and in some cases like the "File" key it's plain wrong to write.
private static void SetSpecialLogRegValues(RegistryKey logKey, string logName) {
// Set all the default values for this log. AutoBackupLogfiles only makes sense in
// Win2000 SP4, WinXP SP1, and Win2003, but it should alright elsewhere.
// Since we use this method on the existing system logs as well as our own,
// we need to make sure we don't overwrite any existing values.
if (logKey.GetValue("MaxSize") == null)
logKey.SetValue("MaxSize", DefaultMaxSize, RegistryValueKind.DWord);
if (logKey.GetValue("AutoBackupLogFiles") == null)
logKey.SetValue("AutoBackupLogFiles", 0, RegistryValueKind.DWord);
if (!SkipRegPatch) {
// In Vista, "retention of events for 'n' days" concept is removed
if (logKey.GetValue("Retention") == null)
logKey.SetValue("Retention", DefaultRetention, RegistryValueKind.DWord);
if (logKey.GetValue("File") == null) {
string filename;
if (logName.Length > 8)
filename = @"%SystemRoot%\System32\config\" + logName.Substring(0,8) + ".evt";
else
filename = @"%SystemRoot%\System32\config\" + logName + ".evt";
logKey.SetValue("File", filename, RegistryValueKind.ExpandString);
}
}
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
private static void SetSpecialSourceRegValues(RegistryKey sourceLogKey, EventSourceCreationData sourceData) {
if (String.IsNullOrEmpty(sourceData.MessageResourceFile))
sourceLogKey.SetValue("EventMessageFile", GetDllPath(sourceData.MachineName), RegistryValueKind.ExpandString);
else
sourceLogKey.SetValue("EventMessageFile", FixupPath(sourceData.MessageResourceFile), RegistryValueKind.ExpandString);
if (!String.IsNullOrEmpty(sourceData.ParameterResourceFile))
sourceLogKey.SetValue("ParameterMessageFile", FixupPath(sourceData.ParameterResourceFile), RegistryValueKind.ExpandString);
if (!String.IsNullOrEmpty(sourceData.CategoryResourceFile)) {
sourceLogKey.SetValue("CategoryMessageFile", FixupPath(sourceData.CategoryResourceFile), RegistryValueKind.ExpandString);
sourceLogKey.SetValue("CategoryCount", sourceData.CategoryCount, RegistryValueKind.DWord);
}
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static string FixupPath(string path) {
if (path[0] == '%')
return path;
else
return Path.GetFullPath(path);
}
///
/// Sets up the event monitoring mechanism. We don't track event log changes
/// unless someone is interested, so we set this up on demand.
///
[HostProtection(Synchronization=true, ExternalThreading=true)]
private void StartListening(string currentMachineName, string currentLogName) {
// make sure we don't fire events for entries that are already there
Debug.Assert(!boolFlags[Flag_registeredAsListener], "StartListening called with boolFlags[Flag_registeredAsListener] true.");
lastSeenCount = EntryCount + OldestEntryNumber;
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StartListening: lastSeenCount = " + lastSeenCount);
AddListenerComponent(this, currentMachineName, currentLogName);
boolFlags[Flag_registeredAsListener] = true;
}
private void StartRaisingEvents(string currentMachineName, string currentLogName) {
if (!boolFlags[Flag_initializing] && !boolFlags[Flag_monitoring] && !m_Parent.ComponentDesignMode) {
StartListening(currentMachineName, currentLogName);
}
boolFlags[Flag_monitoring] = true;
}
private static void StaticCompletionCallback(object context, bool wasSignaled) {
LogListeningInfo info = (LogListeningInfo) context;
// get a snapshot of the components to fire the event on
EventLogInternal[] interestedComponents = (EventLogInternal[])info.listeningComponents.ToArray(typeof(EventLogInternal));
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StaticCompletionCallback: notifying " + interestedComponents.Length + " components.");
for (int i = 0; i < interestedComponents.Length; i++) {
try {
if (interestedComponents[i] != null) {
interestedComponents[i].CompletionCallback(null);
}
} catch (ObjectDisposedException) {
// The EventLog that was registered to listen has been disposed. Nothing much we can do here
// we don't want to propigate this error up as it will likely be unhandled and will cause the app
// to crash.
Debug.WriteLineIf(CompModSwitches.EventLog.TraceVerbose, "EventLog::StaticCompletionCallback: ignored an ObjectDisposedException");
}
}
}
///
/// Tears down the event listening mechanism. This is called when the last
/// interested party removes their event handler.
///
[HostProtection(Synchronization=true, ExternalThreading=true)]
private void StopListening(/*string currentMachineName,*/ string currentLogName) {
Debug.Assert(boolFlags[Flag_registeredAsListener], "StopListening called without StartListening.");
RemoveListenerComponent(this, currentLogName);
boolFlags[Flag_registeredAsListener] = false;
}
///
///
private void StopRaisingEvents(/*string currentMachineName,*/ string currentLogName) {
if (!boolFlags[Flag_initializing] && boolFlags[Flag_monitoring] && !m_Parent.ComponentDesignMode) {
StopListening(currentLogName);
}
boolFlags[Flag_monitoring] = false;
}
// CharIsPrintable used to be Char.IsPrintable, but Jay removed it and
// is forcing people to use the Unicode categories themselves. Copied
// the code here.
private static bool CharIsPrintable(char c) {
UnicodeCategory uc = Char.GetUnicodeCategory(c);
return (!(uc == UnicodeCategory.Control) || (uc == UnicodeCategory.Format) ||
(uc == UnicodeCategory.LineSeparator) || (uc == UnicodeCategory.ParagraphSeparator) ||
(uc == UnicodeCategory.OtherNotAssigned));
}
// SECREVIEW: Make sure this method catches all the strange cases.
internal static bool ValidLogName(string logName, bool ignoreEmpty) {
// No need to trim here since the next check will verify that there are no spaces.
// We need to ignore the empty string as an invalid log name sometimes because it can
// be passed in from our default constructor.
if (logName.Length == 0 && !ignoreEmpty)
return false;
//any space, backslash, asterisk, or question mark is bad
//any non-printable characters are also bad
foreach (char c in logName)
if (!CharIsPrintable(c) || (c == '\\') || (c == '*') || (c == '?'))
return false;
return true;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
[SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity", Justification = "[....]: Safe, machineName doesn't change")]
private void VerifyAndCreateSource(string sourceName, string currentMachineName) {
if (boolFlags[Flag_sourceVerified])
return;
if (!SourceExists(sourceName, currentMachineName, true)) {
Mutex mutex = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
SharedUtils.EnterMutex(eventLogMutexName, ref mutex);
if (!SourceExists(sourceName, currentMachineName, true)) {
if (GetLogName(currentMachineName) == null)
this.logName = "Application";
// we automatically add an entry in the registry if there's not already
// one there for this source
CreateEventSource(new EventSourceCreationData(sourceName, GetLogName(currentMachineName), currentMachineName));
// The user may have set a custom log and tried to read it before trying to
// write. Due to a quirk in the event log API, we would have opened the Application
// log to read (because the custom log wasn't there). Now that we've created
// the custom log, we should close so that when we re-open, we get a read
// handle on the _new_ log instead of the Application log.
Reset(currentMachineName);
}
else {
string rightLogName = LogNameFromSourceName(sourceName, currentMachineName);
string currentLogName = GetLogName(currentMachineName);
if (rightLogName != null && currentLogName != null && String.Compare(rightLogName, currentLogName, StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException(SR.GetString(SR.LogSourceMismatch, Source.ToString(), currentLogName, rightLogName));
}
}
finally {
if (mutex != null) {
mutex.ReleaseMutex();
mutex.Close();
}
}
}
else {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
string rightLogName = _InternalLogNameFromSourceName(sourceName, currentMachineName);
string currentLogName = GetLogName(currentMachineName);
if (rightLogName != null && currentLogName != null && String.Compare(rightLogName, currentLogName, StringComparison.OrdinalIgnoreCase) != 0)
throw new ArgumentException(SR.GetString(SR.LogSourceMismatch, Source.ToString(), currentLogName, rightLogName));
}
boolFlags[Flag_sourceVerified] = true;
}
///
///
/// Writes an information type entry with the given message text to the event log.
///
///
public void WriteEntry(string message) {
WriteEntry(message, EventLogEntryType.Information, (short) 0, 0, null);
}
///
///
/// Writes an entry of the specified to the event log. Valid types are
/// , , ,
/// , and .
///
///
public void WriteEntry(string message, EventLogEntryType type) {
WriteEntry(message, type, (short) 0, 0, null);
}
///
///
/// Writes an entry of the specified
/// and with the
/// user-defined
/// to
/// the event log.
///
///
public void WriteEntry(string message, EventLogEntryType type, int eventID) {
WriteEntry(message, type, eventID, 0, null);
}
///
///
/// Writes an entry of the specified type with the
/// user-defined and
/// to the event log. The
/// can be used by the event viewer to filter events in the log.
///
///
public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) {
WriteEntry(message, type, eventID, category, null);
}
///
///
/// Writes an entry of the specified type with the
/// user-defined and to the event log, and appends binary data to
/// the message. The Event Viewer does not interpret this data; it
/// displays raw data only in a combined hexadecimal and text format.
///
///
public void WriteEntry(string message, EventLogEntryType type, int eventID, short category,
byte[] rawData) {
if (eventID < 0 || eventID > ushort.MaxValue)
throw new ArgumentException(SR.GetString(SR.EventID, eventID, 0, (int)ushort.MaxValue));
if (Source.Length == 0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
if (!Enum.IsDefined(typeof(EventLogEntryType), type))
throw new InvalidEnumArgumentException("type", (int)type, typeof(EventLogEntryType));
string currentMachineName = machineName;
if (!boolFlags[Flag_writeGranted]) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
boolFlags[Flag_writeGranted] = true;
}
VerifyAndCreateSource(sourceName, currentMachineName);
// now that the source has been hooked up to our DLL, we can use "normal"
// (message-file driven) logging techniques.
// Our DLL has 64K different entries; all of them just display the first
// insertion string.
InternalWriteEvent((uint)eventID, (ushort)category, type, new string[] { message}, rawData, currentMachineName);
}
[ComVisible(false)]
public void WriteEvent(EventInstance instance, params Object[] values) {
WriteEvent(instance, null, values);
}
[ComVisible(false)]
public void WriteEvent(EventInstance instance, byte[] data, params Object[] values) {
if (instance == null)
throw new ArgumentNullException("instance");
if (Source.Length == 0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
string currentMachineName = machineName;
if (!boolFlags[Flag_writeGranted]) {
EventLogPermission permission = new EventLogPermission(EventLogPermissionAccess.Write, currentMachineName);
permission.Demand();
boolFlags[Flag_writeGranted] = true;
}
VerifyAndCreateSource(Source, currentMachineName);
string[] strings = null;
if (values != null) {
strings = new string[values.Length];
for (int i=0; i= 256)
throw new ArgumentException(SR.GetString(SR.TooManyReplacementStrings));
for (int i = 0; i < strings.Length; i++) {
if (strings[i] == null)
strings[i] = String.Empty;
// make sure the strings aren't too long. MSDN says each string has a limit of 32k (32768) characters, but
// experimentation shows that it doesn't like anything larger than 32766
if (strings[i].Length > 32766)
throw new ArgumentException(SR.GetString(SR.LogEntryTooLong));
}
if (rawData == null)
rawData = new byte[0];
if (Source.Length == 0)
throw new ArgumentException(SR.GetString(SR.NeedSourceToWrite));
if (!IsOpenForWrite)
OpenForWrite(currentMachineName);
// pin each of the strings in memory
IntPtr[] stringRoots = new IntPtr[strings.Length];
GCHandle[] stringHandles = new GCHandle[strings.Length];
GCHandle stringsRootHandle = GCHandle.Alloc(stringRoots, GCHandleType.Pinned);
try {
for (int strIndex = 0; strIndex < strings.Length; strIndex++) {
stringHandles[strIndex] = GCHandle.Alloc(strings[strIndex], GCHandleType.Pinned);
stringRoots[strIndex] = stringHandles[strIndex].AddrOfPinnedObject();
}
byte[] sid = null;
// actually report the event
bool success = UnsafeNativeMethods.ReportEvent(writeHandle, (short) type, category, eventID,
sid, (short) strings.Length, rawData.Length, new HandleRef(this, stringsRootHandle.AddrOfPinnedObject()), rawData);
if (!success) {
//Trace("WriteEvent", "Throwing Win32Exception");
throw SharedUtils.CreateSafeWin32Exception();
}
}
finally {
// now free the pinned strings
for (int i = 0; i < strings.Length; i++) {
if (stringHandles[i].IsAllocated)
stringHandles[i].Free();
}
stringsRootHandle.Free();
}
}
private class LogListeningInfo {
public EventLogInternal handleOwner;
public RegisteredWaitHandle registeredWaitHandle;
public WaitHandle waitHandle;
public ArrayList listeningComponents = new ArrayList();
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- login.cs
- UnsafeNativeMethods.cs
- ListMarkerLine.cs
- BufferAllocator.cs
- SortQuery.cs
- DataGridParentRows.cs
- UniqueIdentifierService.cs
- FocusChangedEventArgs.cs
- VirtualPathProvider.cs
- SyntaxCheck.cs
- XmlUrlResolver.cs
- DataControlButton.cs
- NavigationCommands.cs
- WCFBuildProvider.cs
- StrokeIntersection.cs
- EntityCommand.cs
- TraceContextEventArgs.cs
- figurelength.cs
- future.cs
- BCLDebug.cs
- SignatureHelper.cs
- ItemList.cs
- UnregisterInfo.cs
- OutKeywords.cs
- ConfigXmlDocument.cs
- CancellationTokenSource.cs
- WaitHandle.cs
- DbSetClause.cs
- LayoutTableCell.cs
- ContextBase.cs
- Page.cs
- IssuanceLicense.cs
- TextTreeUndo.cs
- XmlAttributes.cs
- NumberFunctions.cs
- SqlDataSourceCache.cs
- ToolStripControlHost.cs
- DuplexChannelBinder.cs
- Geometry.cs
- basenumberconverter.cs
- UIElementParaClient.cs
- DateTimeUtil.cs
- HttpRuntimeSection.cs
- OledbConnectionStringbuilder.cs
- EntityProviderServices.cs
- AsyncOperation.cs
- TypeGeneratedEventArgs.cs
- StaticTextPointer.cs
- FileDialog.cs
- ModelItemDictionaryImpl.cs
- GPStream.cs
- NominalTypeEliminator.cs
- StringToken.cs
- ExtensionQuery.cs
- SqlParameterizer.cs
- TaskSchedulerException.cs
- ExternalException.cs
- ServiceDesigner.cs
- UnknownBitmapEncoder.cs
- KnownTypes.cs
- PointLight.cs
- TraceUtility.cs
- BitmapEffectrendercontext.cs
- unsafenativemethodstextservices.cs
- XmlUtf8RawTextWriter.cs
- UiaCoreProviderApi.cs
- DBSqlParser.cs
- MultipleCopiesCollection.cs
- FixedPageAutomationPeer.cs
- HyperLinkDataBindingHandler.cs
- CollectionType.cs
- Int64.cs
- loginstatus.cs
- BitmapFrameDecode.cs
- SiteMapNodeCollection.cs
- DataGridViewLinkCell.cs
- SecurityContext.cs
- cryptoapiTransform.cs
- HttpCacheVaryByContentEncodings.cs
- XmlnsDictionary.cs
- Model3D.cs
- ParameterRetriever.cs
- GAC.cs
- SqlStream.cs
- Part.cs
- StdValidatorsAndConverters.cs
- FixedFindEngine.cs
- Formatter.cs
- Rfc2898DeriveBytes.cs
- SafeFileMappingHandle.cs
- TraceProvider.cs
- PropertyPath.cs
- ProjectionCamera.cs
- ZipIOCentralDirectoryFileHeader.cs
- UITypeEditors.cs
- EventLogPermissionHolder.cs
- PathTooLongException.cs
- AffineTransform3D.cs
- ComplexTypeEmitter.cs
- TcpProcessProtocolHandler.cs