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