1 //------------------------------------------------------------------------------
2 // <copyright file="ClientUtils.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
9 #if Microsoft_NAMESPACE
10 namespace System.Windows.Forms
11 #elif DRAWING_NAMESPACE
12 namespace System.Drawing
13 #elif Microsoft_PUBLIC_GRAPHICS_LIBRARY
14 namespace System.Internal
15 #elif SYSTEM_NAMESPACE
18 namespace System.Windows.Forms
22 using System.Reflection;
23 using System.Diagnostics.CodeAnalysis;
24 using System.Globalization;
25 using System.Collections;
26 using System.Diagnostics;
27 using System.Security.Permissions;
29 // Miscellaneous utilities
30 static internal class ClientUtils {
32 // ExecutionEngineException is obsolete and shouldn't be used (to catch, throw or reference) anymore.
33 // Pragma added to prevent converting the "type is obsolete" warning into build error.
34 // File owner should fix this.
35 #pragma warning disable 618
36 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
37 public static bool IsCriticalException(Exception ex) {
38 return ex is NullReferenceException
39 || ex is StackOverflowException
40 || ex is OutOfMemoryException
41 || ex is System.Threading.ThreadAbortException
42 || ex is ExecutionEngineException
43 || ex is IndexOutOfRangeException
44 || ex is AccessViolationException;
46 #pragma warning restore 618
48 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
49 public static bool IsSecurityOrCriticalException(Exception ex) {
50 return (ex is System.Security.SecurityException) || IsCriticalException(ex);
53 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
54 public static int GetBitCount(uint x) {
65 // assumes sequential enum members 0,1,2,3,4 -etc.
67 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
68 public static bool IsEnumValid(Enum enumValue, int value, int minValue, int maxValue)
70 bool valid = (value >= minValue) && (value <= maxValue);
72 Debug_SequentialEnumIsDefinedCheck(enumValue, minValue, maxValue);
78 // Useful for sequential enum values which only use powers of two 0,1,2,4,8 etc: IsEnumValid(val, min, max, 1)
79 // Valid example: TextImageRelation 0,1,2,4,8 - only one bit can ever be on, and the value is between 0 and 8.
81 // ClientUtils.IsEnumValid((int)(relation), /*min*/(int)TextImageRelation.None, (int)TextImageRelation.TextBeforeImage,1);
83 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
84 [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
85 public static bool IsEnumValid(Enum enumValue, int value, int minValue, int maxValue, int maxNumberOfBitsOn) {
86 System.Diagnostics.Debug.Assert(maxNumberOfBitsOn >=0 && maxNumberOfBitsOn<32, "expect this to be greater than zero and less than 32");
88 bool valid = (value >= minValue) && (value <= maxValue);
89 //Note: if it's 0, it'll have no bits on. If it's a power of 2, it'll have 1.
90 valid = (valid && GetBitCount((uint)value) <= maxNumberOfBitsOn);
92 Debug_NonSequentialEnumIsDefinedCheck(enumValue, minValue, maxValue, maxNumberOfBitsOn, valid);
97 // Useful for enums that are a subset of a bitmask
98 // Valid example: EdgeEffects 0, 0x800 (FillInterior), 0x1000 (Flat), 0x4000(Soft), 0x8000(Mono)
100 // ClientUtils.IsEnumValid((int)(effects), /*mask*/ FillInterior | Flat | Soft | Mono,
103 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
104 [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
105 public static bool IsEnumValid_Masked(Enum enumValue, int value, UInt32 mask) {
106 bool valid = ((value & mask) == value);
109 Debug_ValidateMask(enumValue, mask);
119 // Useful for cases where you have discontiguous members of the enum.
120 // Valid example: AutoComplete source.
121 // if (!ClientUtils.IsEnumValid(value, AutoCompleteSource.None,
122 // AutoCompleteSource.AllSystemSources
123 // AutoCompleteSource.AllUrl,
124 // AutoCompleteSource.CustomSource,
125 // AutoCompleteSource.FileSystem,
126 // AutoCompleteSource.FileSystemDirectories,
127 // AutoCompleteSource.HistoryList,
128 // AutoCompleteSource.ListItems,
129 // AutoCompleteSource.RecentlyUsedList))
131 // PERF tip: put the default value in the enum towards the front of the argument list.
132 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
133 public static bool IsEnumValid_NotSequential(System.Enum enumValue, int value, params int[] enumValues) {
134 System.Diagnostics.Debug.Assert(Enum.GetValues(enumValue.GetType()).Length == enumValues.Length, "Not all the enum members were passed in.");
135 for (int i = 0; i < enumValues.Length; i++){
136 if (enumValues[i] == value){
145 private static Hashtable enumValueInfo;
146 public const int MAXCACHE = 300; // we think we're going to get O(100) of these, put in a tripwire if it gets larger.
148 [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
149 private class SequentialEnumInfo {
150 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
151 public SequentialEnumInfo(Type t) {
152 int actualMinimum = Int32.MaxValue;
153 int actualMaximum = Int32.MinValue;
154 int countEnumVals = 0;
156 foreach (int iVal in Enum.GetValues(t)){
157 actualMinimum = Math.Min(actualMinimum, iVal);
158 actualMaximum = Math.Max(actualMaximum, iVal);
162 if (countEnumVals -1 != (actualMaximum - actualMinimum)) {
163 Debug.Fail("this enum cannot be sequential.");
165 MinValue = actualMinimum;
166 MaxValue = actualMaximum;
174 [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
175 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
176 [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
177 private static void Debug_SequentialEnumIsDefinedCheck(System.Enum value, int minVal, int maxVal) {
178 Type t = value.GetType();
180 if (enumValueInfo == null) {
181 enumValueInfo = new Hashtable();
184 SequentialEnumInfo sequentialEnumInfo = null;
186 if (enumValueInfo.ContainsKey(t)) {
187 sequentialEnumInfo = enumValueInfo[t] as SequentialEnumInfo;
189 if (sequentialEnumInfo == null) {
190 sequentialEnumInfo = new SequentialEnumInfo(t);
192 if (enumValueInfo.Count > MAXCACHE) {
193 // see comment next to MAXCACHE declaration.
194 Debug.Fail("cache is too bloated, clearing out, we need to revisit this.");
195 enumValueInfo.Clear();
197 enumValueInfo[t] = sequentialEnumInfo;
200 if (minVal != sequentialEnumInfo.MinValue) {
201 // put string allocation in the IF block so the common case doesnt build up the string.
202 System.Diagnostics.Debug.Fail("Minimum passed in is not the actual minimum for the enum. Consider changing the parameters or using a different function.");
204 if (maxVal != sequentialEnumInfo.MaxValue) {
205 // put string allocation in the IF block so the common case doesnt build up the string.
206 Debug.Fail("Maximum passed in is not the actual maximum for the enum. Consider changing the parameters or using a different function.");
213 [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
214 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
215 private static void Debug_ValidateMask(System.Enum value, UInt32 mask) {
216 Type t = value.GetType();
218 foreach (int iVal in Enum.GetValues(t)){
219 newmask = newmask | (UInt32)iVal;
221 System.Diagnostics.Debug.Assert(newmask == mask, "Mask not valid in IsEnumValid!");
224 [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
225 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
226 [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
227 private static void Debug_NonSequentialEnumIsDefinedCheck(System.Enum value, int minVal, int maxVal, int maxBitsOn, bool isValid) {
228 Type t = value.GetType();
229 int actualMinimum = Int32.MaxValue;
230 int actualMaximum = Int32.MinValue;
231 int checkedValue = Convert.ToInt32(value, CultureInfo.InvariantCulture);
232 int maxBitsFound = 0;
233 bool foundValue = false;
234 foreach (int iVal in Enum.GetValues(t)){
235 actualMinimum = Math.Min(actualMinimum, iVal);
236 actualMaximum = Math.Max(actualMaximum, iVal);
237 maxBitsFound = Math.Max(maxBitsFound, GetBitCount((uint)iVal));
238 if (checkedValue == iVal) {
242 if (minVal != actualMinimum) {
243 // put string allocation in the IF block so the common case doesnt build up the string.
244 System.Diagnostics.Debug.Fail( "Minimum passed in is not the actual minimum for the enum. Consider changing the parameters or using a different function.");
246 if (maxVal != actualMaximum) {
247 // put string allocation in the IF block so the common case doesnt build up the string.
248 System.Diagnostics.Debug.Fail("Maximum passed in is not the actual maximum for the enum. Consider changing the parameters or using a different function.");
251 if (maxBitsFound != maxBitsOn) {
252 System.Diagnostics.Debug.Fail("Incorrect usage of IsEnumValid function. The bits set to 1 in this enum was found to be: " + maxBitsFound.ToString(CultureInfo.InvariantCulture) + "this does not match what's passed in: " + maxBitsOn.ToString(CultureInfo.InvariantCulture));
254 if (foundValue != isValid) {
255 System.Diagnostics.Debug.Fail(String.Format(CultureInfo.InvariantCulture, "Returning {0} but we actually {1} found the value in the enum! Consider using a different overload to IsValidEnum.", isValid, ((foundValue) ? "have" : "have not")));
262 /// WeakRefCollection - a collection that holds onto weak references
264 /// Essentially you pass in the object as it is, and under the covers
265 /// we only hold a weak reference to the object.
267 /// -----------------------------------------------------------------
268 /// !!!IMPORTANT USAGE NOTE!!!
269 /// Users of this class should set the RefCheckThreshold property
270 /// explicitly or call ScavengeReferences every once in a while to
271 /// remove dead references.
272 /// Also avoid calling Remove(item). Instead call RemoveByHashCode(item)
273 /// to make sure dead refs are removed.
274 /// -----------------------------------------------------------------
277 #if Microsoft_NAMESPACE || Microsoft_PUBLIC_GRAPHICS_LIBRARY || DRAWING_NAMESPACE
278 internal class WeakRefCollection : IList {
279 private int refCheckThreshold = Int32.MaxValue; // this means this is disabled by default.
280 private ArrayList _innerList;
282 internal WeakRefCollection() {
283 _innerList = new ArrayList(4);
286 internal WeakRefCollection(int size) {
287 _innerList = new ArrayList(size);
290 internal ArrayList InnerList {
291 get { return _innerList; }
295 /// Indicates the value where the collection should check its items to remove dead weakref left over.
296 /// Note: When GC collects weak refs from this collection the WeakRefObject identity changes since its
297 /// Target becomes null. This makes the item unrecognizable by the collection and cannot be
298 /// removed - Remove(item) and Contains(item) will not find it anymore.
301 public int RefCheckThreshold {
303 return this.refCheckThreshold;
306 this.refCheckThreshold = value;
310 public object this[int index] {
312 WeakRefObject weakRef = InnerList[index] as WeakRefObject;
314 if ((weakRef != null) && (weakRef.IsAlive)) {
315 return weakRef.Target;
321 InnerList[index] = CreateWeakRefObject(value);
325 public void ScavengeReferences() {
326 int currentIndex = 0;
327 int currentCount = Count;
328 for (int i = 0; i < currentCount; i++) {
329 object item = this[currentIndex];
332 InnerList.RemoveAt(currentIndex);
334 else { // only incriment if we have not removed the item
340 public override bool Equals(object obj) {
341 WeakRefCollection other = obj as WeakRefCollection;
347 if (other == null || Count != other.Count) {
351 for (int i = 0; i < Count; i++) {
352 if( this.InnerList[i] != other.InnerList[i] ) {
353 if( this.InnerList[i] == null || !this.InnerList[i].Equals(other.InnerList[i])) {
362 public override int GetHashCode() {
363 return base.GetHashCode();
366 private WeakRefObject CreateWeakRefObject(object value) {
370 return new WeakRefObject(value);
373 private static void Copy(WeakRefCollection sourceList, int sourceIndex, WeakRefCollection destinationList, int destinationIndex, int length) {
374 if (sourceIndex < destinationIndex) {
375 // We need to copy from the back forward to prevent overwrite if source and
376 // destination lists are the same, so we need to flip the source/dest indices
377 // to point at the end of the spans to be copied.
378 sourceIndex = sourceIndex + length;
379 destinationIndex = destinationIndex + length;
380 for (; length > 0; length--) {
381 destinationList.InnerList[--destinationIndex] = sourceList.InnerList[--sourceIndex];
385 for (; length > 0; length--) {
386 destinationList.InnerList[destinationIndex++] = sourceList.InnerList[sourceIndex++];
392 /// Removes the value using its hash code as its identity.
393 /// This is needed because the underlying item in the collection may have already been collected
394 /// changing the identity of the WeakRefObject making it impossible for the collection to identify
395 /// it. See WeakRefObject for more info.
397 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
398 public void RemoveByHashCode(object value) {
399 if( value == null ) {
403 int hash = value.GetHashCode();
405 for( int idx = 0; idx < this.InnerList.Count; idx++ ) {
406 if(this.InnerList[idx] != null && this.InnerList[idx].GetHashCode() == hash ) {
413 #region IList Members
414 public void Clear() { InnerList.Clear(); }
415 public bool IsFixedSize { get { return InnerList.IsFixedSize; } }
416 public bool Contains(object value) { return InnerList.Contains(CreateWeakRefObject(value)); }
417 public void RemoveAt(int index) { InnerList.RemoveAt(index); }
418 public void Remove(object value) { InnerList.Remove(CreateWeakRefObject(value)); }
419 public int IndexOf(object value) { return InnerList.IndexOf(CreateWeakRefObject(value)); }
420 public void Insert(int index, object value) { InnerList.Insert(index, CreateWeakRefObject(value)); }
421 public int Add(object value) {
422 if (this.Count > RefCheckThreshold) {
423 ScavengeReferences();
425 return InnerList.Add(CreateWeakRefObject(value));
429 #region ICollection Members
430 /// <include file='doc\ArrangedElementCollection.uex' path='docs/doc[@for="ArrangedElementCollection.Count"]/*' />
431 public int Count { get { return InnerList.Count; } }
432 object ICollection.SyncRoot { get { return InnerList.SyncRoot; } }
433 public bool IsReadOnly { get { return InnerList.IsReadOnly; } }
434 public void CopyTo(Array array, int index) { InnerList.CopyTo(array, index); }
435 bool ICollection.IsSynchronized { get { return InnerList.IsSynchronized; } }
438 #region IEnumerable Members
439 public IEnumerator GetEnumerator() {
440 return InnerList.GetEnumerator();
445 /// Wraps a weak ref object.
446 /// WARNING: Use this class carefully!
447 /// When the weak ref is collected, this object looses its identity. This is bad when the object
448 /// has been added to a collection since Contains(WeakRef(item)) and Remove(WeakRef(item)) would
449 /// not be able to identify the item.
451 internal class WeakRefObject {
453 WeakReference weakHolder;
455 internal WeakRefObject(object obj) {
456 Debug.Assert(obj != null, "Unexpected null object!");
457 weakHolder = new WeakReference(obj);
458 hash = obj.GetHashCode();
461 internal bool IsAlive {
462 get { return weakHolder.IsAlive; }
465 internal object Target {
467 return weakHolder.Target;
471 public override int GetHashCode() {
475 public override bool Equals(object obj) {
476 WeakRefObject other = obj as WeakRefObject;
478 if( other == this ) {
486 if( other.Target != this.Target ) {
487 if( this.Target == null || !this.Target.Equals(other.Target) ) {