|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: ProviderMetadataCachedInformation
**
** Purpose:
** This internal class exposes a limited set of cached Provider
** metadata information. It is meant to support the Metadata
**
============================================================*/
using System;
using System.Globalization;
using System.Collections.Generic;
using System.Threading;
using System.Timers;
using System.Security.Permissions;
using Microsoft.Win32;
namespace System.Diagnostics.Eventing.Reader {
//
// this class does not expose underlying Provider metadata objects. Instead it
// exposes a limited set of Provider metadata information from the cache. The reason
// for this is so the cache can easily Dispose the metadata object without worrying
// about who is using it.
//
internal class ProviderMetadataCachedInformation {
private Dictionary<ProviderMetadataId, CacheItem> cache;
private int maximumCacheSize;
private EventLogSession session;
private string logfile;
private class ProviderMetadataId {
private string providerName;
private CultureInfo cultureInfo;
public ProviderMetadataId(string providerName, CultureInfo cultureInfo) {
this.providerName = providerName;
this.cultureInfo = cultureInfo;
}
public override bool Equals(object obj) {
ProviderMetadataId rhs = obj as ProviderMetadataId;
if (rhs == null) return false;
if (this.providerName.Equals(rhs.providerName) && (cultureInfo == rhs.cultureInfo))
return true;
return false;
}
public override int GetHashCode() {
return this.providerName.GetHashCode() ^ cultureInfo.GetHashCode();
}
public string ProviderName {
get {
return providerName;
}
}
public CultureInfo TheCultureInfo {
get {
return cultureInfo;
}
}
}
private class CacheItem {
private ProviderMetadata pm;
private DateTime theTime;
public CacheItem(ProviderMetadata pm) {
this.pm = pm;
theTime = DateTime.Now;
}
public DateTime TheTime {
get {
return theTime;
}
set {
theTime = value;
}
}
public ProviderMetadata ProviderMetadata {
get {
return pm;
}
}
}
public ProviderMetadataCachedInformation(EventLogSession session, string logfile, int maximumCacheSize) {
Debug.Assert(session != null);
this.session = session;
this.logfile = logfile;
cache = new Dictionary<ProviderMetadataId, CacheItem>();
this.maximumCacheSize = maximumCacheSize;
}
private bool IsCacheFull() {
return cache.Count == maximumCacheSize;
}
private bool IsProviderinCache(ProviderMetadataId key) {
return cache.ContainsKey(key);
}
private void DeleteCacheEntry(ProviderMetadataId key) {
if (!IsProviderinCache(key))
return;
CacheItem value = cache[key];
cache.Remove(key);
value.ProviderMetadata.Dispose();
}
private void AddCacheEntry(ProviderMetadataId key, ProviderMetadata pm) {
if (IsCacheFull())
FlushOldestEntry();
CacheItem value = new CacheItem(pm);
cache.Add(key, value);
return;
}
private void FlushOldestEntry() {
double maxPassedTime = -10;
DateTime timeNow = DateTime.Now;
ProviderMetadataId keyToDelete = null;
//get the entry in the cache which was not accessed for the longest time.
foreach (KeyValuePair<ProviderMetadataId, CacheItem> kvp in cache) {
//the time difference (in ms) between the timeNow and the last used time of each entry
TimeSpan timeDifference = timeNow.Subtract(kvp.Value.TheTime);
//for the "unused" items (with ReferenceCount == 0) -> can possible be deleted.
if (timeDifference.TotalMilliseconds >= maxPassedTime) {
maxPassedTime = timeDifference.TotalMilliseconds;
keyToDelete = kvp.Key;
}
}
if (keyToDelete != null)
DeleteCacheEntry(keyToDelete);
}
private static void UpdateCacheValueInfoForHit(CacheItem cacheItem) {
cacheItem.TheTime = DateTime.Now;
}
private ProviderMetadata GetProviderMetadata(ProviderMetadataId key) {
if (!IsProviderinCache(key)) {
ProviderMetadata pm;
try {
pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo, this.logfile);
}
catch (EventLogNotFoundException) {
pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo);
}
AddCacheEntry(key, pm);
return pm;
}
else {
CacheItem cacheItem = cache[key];
ProviderMetadata pm = cacheItem.ProviderMetadata;
//
// check Provider metadata to be sure it's hasn't been
// uninstalled since last time it was used.
//
try {
pm.CheckReleased();
UpdateCacheValueInfoForHit(cacheItem);
}
catch (EventLogException) {
DeleteCacheEntry(key);
try {
pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo, this.logfile);
}
catch (EventLogNotFoundException) {
pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo);
}
AddCacheEntry(key, pm);
}
return pm;
}
}
// marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe.
[System.Security.SecuritySafeCritical]
public string GetFormatDescription(string ProviderName, EventLogHandle eventHandle) {
lock (this) {
ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture);
try {
ProviderMetadata pm = GetProviderMetadata(key);
return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageEvent);
}
catch (EventLogNotFoundException) {
return null;
}
}
}
public string GetFormatDescription(string ProviderName, EventLogHandle eventHandle, string[] values) {
lock (this) {
ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture);
ProviderMetadata pm = GetProviderMetadata(key);
try {
return NativeWrapper.EvtFormatMessageFormatDescription(pm.Handle, eventHandle, values);
}
catch (EventLogNotFoundException) {
return null;
}
}
}
// marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe.
[System.Security.SecuritySafeCritical]
public string GetLevelDisplayName(string ProviderName, EventLogHandle eventHandle) {
lock (this) {
ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture);
ProviderMetadata pm = GetProviderMetadata(key);
return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageLevel);
}
}
// marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe.
[System.Security.SecuritySafeCritical]
public string GetOpcodeDisplayName(string ProviderName, EventLogHandle eventHandle) {
lock (this) {
ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture);
ProviderMetadata pm = GetProviderMetadata(key);
return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageOpcode);
}
}
// marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe.
[System.Security.SecuritySafeCritical]
public string GetTaskDisplayName(string ProviderName, EventLogHandle eventHandle) {
lock (this) {
ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture);
ProviderMetadata pm = GetProviderMetadata(key);
return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageTask);
}
}
// marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe.
[System.Security.SecuritySafeCritical]
public IEnumerable<string> GetKeywordDisplayNames(string ProviderName, EventLogHandle eventHandle) {
lock (this) {
ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture);
ProviderMetadata pm = GetProviderMetadata(key);
return NativeWrapper.EvtFormatMessageRenderKeywords(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageKeyword);
}
}
}
}
|