1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 /*============================================================ 7 ** 8 ** Class: ProviderMetadataCachedInformation 9 ** 10 ** Purpose: 11 ** This internal class exposes a limited set of cached Provider 12 ** metadata information. It is meant to support the Metadata 13 ** 14 ============================================================*/ 15 using System; 16 using System.Globalization; 17 using System.Collections.Generic; 18 using System.Threading; 19 using System.Timers; 20 using System.Security.Permissions; 21 using Microsoft.Win32; 22 23 namespace System.Diagnostics.Eventing.Reader { 24 25 // 26 // this class does not expose underlying Provider metadata objects. Instead it 27 // exposes a limited set of Provider metadata information from the cache. The reason 28 // for this is so the cache can easily Dispose the metadata object without worrying 29 // about who is using it. 30 // 31 internal class ProviderMetadataCachedInformation { 32 33 private Dictionary<ProviderMetadataId, CacheItem> cache; 34 private int maximumCacheSize; 35 private EventLogSession session; 36 private string logfile; 37 38 private class ProviderMetadataId { 39 private string providerName; 40 private CultureInfo cultureInfo; 41 ProviderMetadataId(string providerName, CultureInfo cultureInfo)42 public ProviderMetadataId(string providerName, CultureInfo cultureInfo) { 43 this.providerName = providerName; 44 this.cultureInfo = cultureInfo; 45 } 46 Equals(object obj)47 public override bool Equals(object obj) { 48 ProviderMetadataId rhs = obj as ProviderMetadataId; 49 if (rhs == null) return false; 50 if (this.providerName.Equals(rhs.providerName) && (cultureInfo == rhs.cultureInfo)) 51 return true; 52 return false; 53 } 54 GetHashCode()55 public override int GetHashCode() { 56 57 return this.providerName.GetHashCode() ^ cultureInfo.GetHashCode(); 58 59 } 60 61 public string ProviderName { 62 get { 63 return providerName; 64 } 65 } 66 public CultureInfo TheCultureInfo { 67 get { 68 return cultureInfo; 69 } 70 } 71 } 72 73 private class CacheItem { 74 private ProviderMetadata pm; 75 private DateTime theTime; 76 CacheItem(ProviderMetadata pm)77 public CacheItem(ProviderMetadata pm) { 78 this.pm = pm; 79 theTime = DateTime.Now; 80 } 81 82 public DateTime TheTime { 83 get { 84 return theTime; 85 } 86 set { 87 theTime = value; 88 } 89 } 90 91 public ProviderMetadata ProviderMetadata { 92 get { 93 return pm; 94 } 95 } 96 } 97 ProviderMetadataCachedInformation(EventLogSession session, string logfile, int maximumCacheSize)98 public ProviderMetadataCachedInformation(EventLogSession session, string logfile, int maximumCacheSize) { 99 Debug.Assert(session != null); 100 this.session = session; 101 this.logfile = logfile; 102 cache = new Dictionary<ProviderMetadataId, CacheItem>(); 103 this.maximumCacheSize = maximumCacheSize; 104 } 105 IsCacheFull()106 private bool IsCacheFull() { 107 return cache.Count == maximumCacheSize; 108 } 109 IsProviderinCache(ProviderMetadataId key)110 private bool IsProviderinCache(ProviderMetadataId key) { 111 return cache.ContainsKey(key); 112 } 113 DeleteCacheEntry(ProviderMetadataId key)114 private void DeleteCacheEntry(ProviderMetadataId key) { 115 if (!IsProviderinCache(key)) 116 return; 117 118 CacheItem value = cache[key]; 119 cache.Remove(key); 120 121 value.ProviderMetadata.Dispose(); 122 } 123 124 AddCacheEntry(ProviderMetadataId key, ProviderMetadata pm)125 private void AddCacheEntry(ProviderMetadataId key, ProviderMetadata pm) { 126 if (IsCacheFull()) 127 FlushOldestEntry(); 128 129 CacheItem value = new CacheItem(pm); 130 cache.Add(key, value); 131 return; 132 } 133 FlushOldestEntry()134 private void FlushOldestEntry() { 135 double maxPassedTime = -10; 136 DateTime timeNow = DateTime.Now; 137 ProviderMetadataId keyToDelete = null; 138 139 //get the entry in the cache which was not accessed for the longest time. 140 foreach (KeyValuePair<ProviderMetadataId, CacheItem> kvp in cache) { 141 //the time difference (in ms) between the timeNow and the last used time of each entry 142 TimeSpan timeDifference = timeNow.Subtract(kvp.Value.TheTime); 143 144 //for the "unused" items (with ReferenceCount == 0) -> can possible be deleted. 145 if (timeDifference.TotalMilliseconds >= maxPassedTime) { 146 maxPassedTime = timeDifference.TotalMilliseconds; 147 keyToDelete = kvp.Key; 148 } 149 } 150 151 if (keyToDelete != null) 152 DeleteCacheEntry(keyToDelete); 153 } 154 UpdateCacheValueInfoForHit(CacheItem cacheItem)155 private static void UpdateCacheValueInfoForHit(CacheItem cacheItem) { 156 cacheItem.TheTime = DateTime.Now; 157 } 158 GetProviderMetadata(ProviderMetadataId key)159 private ProviderMetadata GetProviderMetadata(ProviderMetadataId key) { 160 if (!IsProviderinCache(key)) { 161 ProviderMetadata pm; 162 try { 163 pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo, this.logfile); 164 } 165 catch (EventLogNotFoundException) { 166 pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo); 167 } 168 AddCacheEntry(key, pm); 169 return pm; 170 } 171 else { 172 CacheItem cacheItem = cache[key]; 173 174 ProviderMetadata pm = cacheItem.ProviderMetadata; 175 176 // 177 // check Provider metadata to be sure it's hasn't been 178 // uninstalled since last time it was used. 179 // 180 181 try { 182 pm.CheckReleased(); 183 UpdateCacheValueInfoForHit(cacheItem); 184 } 185 catch (EventLogException) { 186 DeleteCacheEntry(key); 187 try { 188 pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo, this.logfile); 189 } 190 catch (EventLogNotFoundException) { 191 pm = new ProviderMetadata(key.ProviderName, this.session, key.TheCultureInfo); 192 } 193 AddCacheEntry(key, pm); 194 } 195 196 return pm; 197 } 198 } 199 200 // marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe. 201 [System.Security.SecuritySafeCritical] GetFormatDescription(string ProviderName, EventLogHandle eventHandle)202 public string GetFormatDescription(string ProviderName, EventLogHandle eventHandle) { 203 204 lock (this) { 205 ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture); 206 207 try { 208 ProviderMetadata pm = GetProviderMetadata(key); 209 return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageEvent); 210 } 211 catch (EventLogNotFoundException) { 212 return null; 213 } 214 } 215 } 216 GetFormatDescription(string ProviderName, EventLogHandle eventHandle, string[] values)217 public string GetFormatDescription(string ProviderName, EventLogHandle eventHandle, string[] values) { 218 219 lock (this) { 220 ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture); 221 ProviderMetadata pm = GetProviderMetadata(key); 222 try { 223 return NativeWrapper.EvtFormatMessageFormatDescription(pm.Handle, eventHandle, values); 224 } 225 catch (EventLogNotFoundException) { 226 return null; 227 } 228 } 229 } 230 231 // marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe. 232 [System.Security.SecuritySafeCritical] GetLevelDisplayName(string ProviderName, EventLogHandle eventHandle)233 public string GetLevelDisplayName(string ProviderName, EventLogHandle eventHandle) { 234 lock (this) { 235 ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture); 236 ProviderMetadata pm = GetProviderMetadata(key); 237 return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageLevel); 238 } 239 } 240 241 // marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe. 242 [System.Security.SecuritySafeCritical] GetOpcodeDisplayName(string ProviderName, EventLogHandle eventHandle)243 public string GetOpcodeDisplayName(string ProviderName, EventLogHandle eventHandle) { 244 lock (this) { 245 ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture); 246 ProviderMetadata pm = GetProviderMetadata(key); 247 return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageOpcode); 248 } 249 } 250 251 // marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe. 252 [System.Security.SecuritySafeCritical] GetTaskDisplayName(string ProviderName, EventLogHandle eventHandle)253 public string GetTaskDisplayName(string ProviderName, EventLogHandle eventHandle) { 254 lock (this) { 255 ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture); 256 ProviderMetadata pm = GetProviderMetadata(key); 257 return NativeWrapper.EvtFormatMessageRenderName(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageTask); 258 } 259 } 260 261 // marking as TreatAsSafe because just passing around a reference to an EventLogHandle is safe. 262 [System.Security.SecuritySafeCritical] GetKeywordDisplayNames(string ProviderName, EventLogHandle eventHandle)263 public IEnumerable<string> GetKeywordDisplayNames(string ProviderName, EventLogHandle eventHandle) { 264 lock (this) { 265 ProviderMetadataId key = new ProviderMetadataId(ProviderName, CultureInfo.CurrentCulture); 266 ProviderMetadata pm = GetProviderMetadata(key); 267 return NativeWrapper.EvtFormatMessageRenderKeywords(pm.Handle, eventHandle, UnsafeNativeMethods.EvtFormatMessageFlags.EvtFormatMessageKeyword); 268 } 269 } 270 } 271 272 } 273