1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 // 7 // <OWNER>GPaperin</OWNER> 8 // <OWNER>Microsoft</OWNER> 9 10 using System; 11 using System.Security; 12 using System.Reflection; 13 using System.Collections; 14 using System.Collections.Generic; 15 using System.Collections.ObjectModel; 16 using System.Diagnostics.Contracts; 17 using System.Runtime.InteropServices; 18 using System.Runtime.CompilerServices; 19 20 namespace System.Runtime.InteropServices.WindowsRuntime 21 { 22 // This is a set of stub methods implementing the support for the IVector`1 interface on managed 23 // objects that implement IList`1. Used by the interop mashaling infrastructure. 24 // 25 // The methods on this class must be written VERY carefully to avoid introducing security holes. 26 // That's because they are invoked with special "this"! The "this" object 27 // for all of these methods are not ListToVectorAdapter objects. Rather, they are of type 28 // IList<T>. No actual ListToVectorAdapter object is ever instantiated. Thus, you will 29 // see a lot of expressions that cast "this" to "IList<T>". 30 internal sealed class ListToVectorAdapter 31 { ListToVectorAdapter()32 private ListToVectorAdapter() 33 { 34 Contract.Assert(false, "This class is never instantiated"); 35 } 36 37 // T GetAt(uint index) 38 [SecurityCritical] GetAt(uint index)39 internal T GetAt<T>(uint index) 40 { 41 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 42 EnsureIndexInt32(index, _this.Count); 43 44 try 45 { 46 return _this[(Int32)index]; 47 } 48 catch (ArgumentOutOfRangeException ex) 49 { 50 throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); 51 } 52 } 53 54 // uint Size { get } 55 [SecurityCritical] Size()56 internal uint Size<T>() 57 { 58 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 59 return (uint)_this.Count; 60 } 61 62 // IVectorView<T> GetView() 63 [SecurityCritical] GetView()64 internal IReadOnlyList<T> GetView<T>() 65 { 66 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 67 Contract.Assert(_this != null); 68 69 // Note: This list is not really read-only - you could QI for a modifiable 70 // list. We gain some perf by doing this. We believe this is acceptable. 71 IReadOnlyList<T> roList = _this as IReadOnlyList<T>; 72 if (roList == null) 73 { 74 roList = new ReadOnlyCollection<T>(_this); 75 } 76 return roList; 77 } 78 79 // bool IndexOf(T value, out uint index) 80 [SecurityCritical] IndexOf(T value, out uint index)81 internal bool IndexOf<T>(T value, out uint index) 82 { 83 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 84 int ind = _this.IndexOf(value); 85 86 if (-1 == ind) 87 { 88 index = 0; 89 return false; 90 } 91 92 index = (uint)ind; 93 return true; 94 } 95 96 // void SetAt(uint index, T value) 97 [SecurityCritical] SetAt(uint index, T value)98 internal void SetAt<T>(uint index, T value) 99 { 100 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 101 EnsureIndexInt32(index, _this.Count); 102 103 try 104 { 105 _this[(int)index] = value; 106 } 107 catch (ArgumentOutOfRangeException ex) 108 { 109 throw WindowsRuntimeMarshal.GetExceptionForHR(__HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange"); 110 } 111 } 112 113 // void InsertAt(uint index, T value) 114 [SecurityCritical] InsertAt(uint index, T value)115 internal void InsertAt<T>(uint index, T value) 116 { 117 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 118 119 // Inserting at an index one past the end of the list is equivalent to appending 120 // so we need to ensure that we're within (0, count + 1). 121 EnsureIndexInt32(index, _this.Count + 1); 122 123 try 124 { 125 _this.Insert((int)index, value); 126 } 127 catch (ArgumentOutOfRangeException ex) 128 { 129 // Change error code to match what WinRT expects 130 ex.SetErrorCode(__HResults.E_BOUNDS); 131 throw; 132 } 133 } 134 135 // void RemoveAt(uint index) 136 [SecurityCritical] RemoveAt(uint index)137 internal void RemoveAt<T>(uint index) 138 { 139 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 140 EnsureIndexInt32(index, _this.Count); 141 142 try 143 { 144 _this.RemoveAt((Int32)index); 145 } 146 catch (ArgumentOutOfRangeException ex) 147 { 148 // Change error code to match what WinRT expects 149 ex.SetErrorCode(__HResults.E_BOUNDS); 150 throw; 151 } 152 } 153 154 // void Append(T value) 155 [SecurityCritical] Append(T value)156 internal void Append<T>(T value) 157 { 158 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 159 _this.Add(value); 160 } 161 162 // void RemoveAtEnd() 163 [SecurityCritical] RemoveAtEnd()164 internal void RemoveAtEnd<T>() 165 { 166 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 167 if (_this.Count == 0) 168 { 169 Exception e = new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CannotRemoveLastFromEmptyCollection")); 170 e.SetErrorCode(__HResults.E_BOUNDS); 171 throw e; 172 } 173 174 uint size = (uint)_this.Count; 175 RemoveAt<T>(size - 1); 176 } 177 178 // void Clear() 179 [SecurityCritical] Clear()180 internal void Clear<T>() 181 { 182 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 183 _this.Clear(); 184 } 185 186 // uint GetMany(uint startIndex, T[] items) 187 [SecurityCritical] GetMany(uint startIndex, T[] items)188 internal uint GetMany<T>(uint startIndex, T[] items) 189 { 190 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 191 return GetManyHelper<T>(_this, startIndex, items); 192 } 193 194 // void ReplaceAll(T[] items) 195 [SecurityCritical] ReplaceAll(T[] items)196 internal void ReplaceAll<T>(T[] items) 197 { 198 IList<T> _this = JitHelpers.UnsafeCast<IList<T>>(this); 199 _this.Clear(); 200 201 if (items != null) 202 { 203 foreach (T item in items) 204 { 205 _this.Add(item); 206 } 207 } 208 } 209 210 // Helpers: 211 EnsureIndexInt32(uint index, int listCapacity)212 private static void EnsureIndexInt32(uint index, int listCapacity) 213 { 214 // We use '<=' and not '<' becasue Int32.MaxValue == index would imply 215 // that Size > Int32.MaxValue: 216 if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity) 217 { 218 Exception e = new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue")); 219 e.SetErrorCode(__HResults.E_BOUNDS); 220 throw e; 221 } 222 } 223 GetManyHelper(IList<T> sourceList, uint startIndex, T[] items)224 private static uint GetManyHelper<T>(IList<T> sourceList, uint startIndex, T[] items) 225 { 226 // Calling GetMany with a start index equal to the size of the list should always 227 // return 0 elements, regardless of the input item size 228 if (startIndex == sourceList.Count) 229 { 230 return 0; 231 } 232 233 EnsureIndexInt32(startIndex, sourceList.Count); 234 235 if (items == null) 236 { 237 return 0; 238 } 239 240 uint itemCount = Math.Min((uint)items.Length, (uint)sourceList.Count - startIndex); 241 for (uint i = 0; i < itemCount; ++i) 242 { 243 items[i] = sourceList[(int)(i + startIndex)]; 244 } 245 246 if (typeof(T) == typeof(string)) 247 { 248 string[] stringItems = items as string[]; 249 250 // Fill in rest of the array with String.Empty to avoid marshaling failure 251 for (uint i = itemCount; i < items.Length; ++i) 252 stringItems[i] = String.Empty; 253 } 254 255 return itemCount; 256 } 257 } 258 } 259