1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Diagnostics; 7 using System.Runtime.InteropServices; 8 using System.Runtime.InteropServices.WindowsRuntime; 9 10 namespace System.Runtime.InteropServices.WindowsRuntime 11 { 12 [System.Runtime.CompilerServices.DependencyReductionRootAttribute] 13 [McgInternalTypeAttribute] 14 public class PropertyValueImpl : BoxedValue, IPropertyValue 15 { PropertyValueImpl(object val, int type)16 internal PropertyValueImpl(object val, int type) : base(val, type) 17 { 18 } 19 get_Type()20 public PropertyType get_Type() 21 { 22 return (PropertyType)m_type; 23 } 24 25 public bool IsNumericScalar 26 { 27 get 28 { 29 return IsNumericScalarImpl((PropertyType)m_type, m_data); 30 } 31 } 32 GetUInt8()33 public byte GetUInt8() 34 { 35 return CoerceScalarValue<byte>(PropertyType.UInt8); 36 } 37 GetInt16()38 public short GetInt16() 39 { 40 return CoerceScalarValue<short>(PropertyType.Int16); 41 } 42 GetUInt16()43 public ushort GetUInt16() 44 { 45 return CoerceScalarValue<ushort>(PropertyType.UInt16); 46 } 47 GetInt32()48 public int GetInt32() 49 { 50 return CoerceScalarValue<int>(PropertyType.Int32); 51 } 52 GetUInt32()53 public uint GetUInt32() 54 { 55 return CoerceScalarValue<uint>(PropertyType.UInt32); 56 } 57 GetInt64()58 public long GetInt64() 59 { 60 return CoerceScalarValue<long>(PropertyType.Int64); 61 } 62 GetUInt64()63 public ulong GetUInt64() 64 { 65 return CoerceScalarValue<ulong>(PropertyType.UInt64); 66 } 67 GetSingle()68 public float GetSingle() 69 { 70 return CoerceScalarValue<float>(PropertyType.Single); 71 } 72 GetDouble()73 public double GetDouble() 74 { 75 return CoerceScalarValue<double>(PropertyType.Double); 76 } 77 GetChar16()78 public char GetChar16() 79 { 80 CheckType(PropertyType.Char16); 81 return (char)m_data; 82 } 83 GetBoolean()84 public bool GetBoolean() 85 { 86 CheckType(PropertyType.Boolean); 87 return (bool)m_data; 88 } 89 GetString()90 public string GetString() 91 { 92 return CoerceScalarValue<string>(PropertyType.String); 93 } 94 GetInspectable()95 public object GetInspectable() 96 { 97 CheckType(PropertyType.Inspectable); 98 return m_data; 99 } 100 GetGuid()101 public System.Guid GetGuid() 102 { 103 return CoerceScalarValue<System.Guid>(PropertyType.Guid); 104 } 105 GetDateTime()106 public System.DateTimeOffset GetDateTime() 107 { 108 CheckType(PropertyType.DateTime); 109 return (System.DateTimeOffset)m_data; 110 } 111 GetTimeSpan()112 public System.TimeSpan GetTimeSpan() 113 { 114 CheckType(PropertyType.TimeSpan); 115 return (System.TimeSpan)m_data; 116 } 117 GetPoint()118 public global::Windows.Foundation.Point GetPoint() 119 { 120 CheckType(PropertyType.Point); 121 return (global::Windows.Foundation.Point)m_data; 122 } 123 GetSize()124 public global::Windows.Foundation.Size GetSize() 125 { 126 CheckType(PropertyType.Size); 127 return (global::Windows.Foundation.Size)m_data; 128 } 129 GetRect()130 public global::Windows.Foundation.Rect GetRect() 131 { 132 CheckType(PropertyType.Rect); 133 return (global::Windows.Foundation.Rect)m_data; 134 } 135 GetUInt8Array(out byte[] array)136 public void GetUInt8Array(out byte[] array) 137 { 138 array = CoerceArrayValue<byte>(PropertyType.UInt8Array); 139 } 140 GetInt16Array(out short[] array)141 public void GetInt16Array(out short[] array) 142 { 143 array = CoerceArrayValue<short>(PropertyType.Int16Array); 144 } 145 GetUInt16Array(out ushort[] array)146 public void GetUInt16Array(out ushort[] array) 147 { 148 array = CoerceArrayValue<ushort>(PropertyType.UInt16Array); 149 } 150 GetInt32Array(out int[] array)151 public void GetInt32Array(out int[] array) 152 { 153 array = CoerceArrayValue<int>(PropertyType.Int32Array); 154 } 155 GetUInt32Array(out uint[] array)156 public void GetUInt32Array(out uint[] array) 157 { 158 array = CoerceArrayValue<uint>(PropertyType.UInt32Array); 159 } 160 GetInt64Array(out long[] array)161 public void GetInt64Array(out long[] array) 162 { 163 array = CoerceArrayValue<long>(PropertyType.Int64Array); 164 } 165 GetUInt64Array(out ulong[] array)166 public void GetUInt64Array(out ulong[] array) 167 { 168 array = CoerceArrayValue<ulong>(PropertyType.UInt64Array); 169 } 170 GetSingleArray(out float[] array)171 public void GetSingleArray(out float[] array) 172 { 173 array = CoerceArrayValue<float>(PropertyType.SingleArray); 174 } 175 GetDoubleArray(out double[] array)176 public void GetDoubleArray(out double[] array) 177 { 178 array = CoerceArrayValue<double>(PropertyType.DoubleArray); 179 } 180 GetChar16Array(out char[] array)181 public void GetChar16Array(out char[] array) 182 { 183 CheckType(PropertyType.Char16Array); 184 array = (char[])m_data; 185 } 186 GetBooleanArray(out bool[] array)187 public void GetBooleanArray(out bool[] array) 188 { 189 CheckType(PropertyType.BooleanArray); 190 array = (bool[])m_data; 191 } 192 GetStringArray(out string[] array)193 public void GetStringArray(out string[] array) 194 { 195 array = CoerceArrayValue<string>(PropertyType.StringArray); 196 } 197 GetInspectableArray(out object[] array)198 public void GetInspectableArray(out object[] array) 199 { 200 CheckType(PropertyType.InspectableArray); 201 array = (object[])m_data; 202 } 203 GetGuidArray(out System.Guid[] array)204 public void GetGuidArray(out System.Guid[] array) 205 { 206 array = CoerceArrayValue<System.Guid>(PropertyType.GuidArray); 207 } 208 GetDateTimeArray(out System.DateTimeOffset[] array)209 public void GetDateTimeArray(out System.DateTimeOffset[] array) 210 { 211 CheckType(PropertyType.DateTimeArray); 212 array = (System.DateTimeOffset[])m_data; 213 } 214 GetTimeSpanArray(out System.TimeSpan[] array)215 public void GetTimeSpanArray(out System.TimeSpan[] array) 216 { 217 CheckType(PropertyType.TimeSpanArray); 218 array = (System.TimeSpan[])m_data; 219 } 220 GetPointArray(out global::Windows.Foundation.Point[] array)221 public void GetPointArray(out global::Windows.Foundation.Point[] array) 222 { 223 CheckType(PropertyType.PointArray); 224 array = (global::Windows.Foundation.Point[])m_data; 225 } 226 GetSizeArray(out global::Windows.Foundation.Size[] array)227 public void GetSizeArray(out global::Windows.Foundation.Size[] array) 228 { 229 CheckType(PropertyType.SizeArray); 230 array = (global::Windows.Foundation.Size[])m_data; 231 } 232 GetRectArray(out global::Windows.Foundation.Rect[] array)233 public void GetRectArray(out global::Windows.Foundation.Rect[] array) 234 { 235 CheckType(PropertyType.RectArray); 236 array = (global::Windows.Foundation.Rect[])m_data; 237 } 238 CoerceArrayValue(PropertyType unboxType)239 private T[] CoerceArrayValue<T>(PropertyType unboxType) 240 { 241 // If we contain the type being looked for directly, then take the fast-path 242 if (m_type == (int)unboxType) 243 { 244 return (T[])m_data; 245 } 246 247 // Make sure we have an array to begin with 248 System.Array dataArray = m_data as System.Array; 249 250 if (dataArray == null) 251 { 252 throw CreateExceptionForInvalidCast((PropertyType)m_type, unboxType); 253 } 254 255 // Array types are 1024 larger than their equivilent scalar counterpart 256 if ((m_type <= 1024) || ((int)unboxType <= 1024)) 257 { 258 throw CreateExceptionForInvalidCast((PropertyType)m_type, unboxType); 259 } 260 261 PropertyType scalarType = (PropertyType)(m_type - 1024); 262 PropertyType unboxTypeT = unboxType - 1024; 263 264 // If we do not have the correct array type, then we need to convert the array element-by-element 265 // to a new array of the requested type 266 T[] coercedArray = new T[dataArray.Length]; 267 268 for (int i = 0; i < dataArray.Length; ++i) 269 { 270 coercedArray[i] = (T)CoerceScalarValue(scalarType, dataArray.GetValue(i), unboxTypeT); 271 } 272 273 return coercedArray; 274 } 275 276 [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] CoerceScalarValue(PropertyType unboxType)277 private T CoerceScalarValue<T>(PropertyType unboxType) 278 { 279 object result = m_data; 280 281 // If we are just a boxed version of the requested type, then take the fast path out 282 if (m_type != (int)unboxType) 283 { 284 result = CoerceScalarValue((PropertyType)m_type, result, unboxType); 285 } 286 287 return (T)result; 288 } 289 CoerceScalarValue(PropertyType type, object value, PropertyType unboxType)290 static private object CoerceScalarValue(PropertyType type, object value, PropertyType unboxType) 291 { 292 // If the property type is neither one of the coercable numeric types nor IInspectable, we 293 // should not attempt coersion, even if the underlying value is technically convertable 294 if ((type == PropertyType.Guid) && (unboxType == PropertyType.String)) 295 { 296 // String <--> Guid is allowed 297 return ((System.Guid)value).ToString(); 298 } 299 else if ((type == PropertyType.String) && (unboxType == PropertyType.Guid)) 300 { 301 System.Guid result; 302 303 if (System.Guid.TryParse((string)value, out result)) 304 { 305 return result; 306 } 307 } 308 else if (type == PropertyType.Inspectable) 309 { 310 // If the property type is IInspectable, and we have a nested IPropertyValue, then we need 311 // to pass along the request to coerce the value. 312 IPropertyValue ipv = value as IPropertyValue; 313 314 if (ipv != null) 315 { 316 object result = ReferenceUtility.GetWellKnownScalar(ipv, unboxType); 317 318 if (result != null) 319 { 320 return result; 321 } 322 323 Debug.Assert( 324 false, 325 "T in coersion function wasn't understood as a type that can be coerced - make sure that CoerceScalarValue and NumericScalarTypes are in sync" 326 ); 327 } 328 } 329 else if (type == PropertyType.Boolean || type == PropertyType.Char16) 330 { 331 throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.TYPE_E_TYPEMISMATCH); 332 } 333 334 // 335 // Let Convert handle all possible conversions - this include 336 // 1. string - which desktop code accidentally allowed 337 // 2. object (IInspectable) 338 // 339 try 340 { 341 switch (unboxType) 342 { 343 case PropertyType.UInt8: 344 return System.Convert.ToByte(value); 345 346 case PropertyType.Int16: 347 return System.Convert.ToInt16(value); 348 349 case PropertyType.UInt16: 350 return System.Convert.ToUInt16(value); 351 352 case PropertyType.Int32: 353 return System.Convert.ToInt32(value); 354 355 case PropertyType.UInt32: 356 return System.Convert.ToUInt32(value); 357 358 case PropertyType.Int64: 359 return System.Convert.ToInt64(value); 360 361 case PropertyType.UInt64: 362 return System.Convert.ToUInt64(value); 363 364 case PropertyType.Single: 365 return System.Convert.ToSingle(value); 366 367 case PropertyType.Double: 368 return System.Convert.ToDouble(value); 369 370 default: 371 break; 372 } 373 } 374 catch (System.FormatException) 375 { 376 throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.TYPE_E_TYPEMISMATCH); 377 } 378 catch (System.InvalidCastException) 379 { 380 throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.TYPE_E_TYPEMISMATCH); 381 } 382 catch (System.OverflowException) 383 { 384 throw CreateExceptionForInvalidCoersion(type, value, unboxType, Interop.COM.DISP_E_OVERFLOW); 385 } 386 387 throw CreateExceptionForInvalidCast(type, unboxType); 388 } 389 IsNumericScalarImpl(PropertyType type, object data)390 private static bool IsNumericScalarImpl(PropertyType type, object data) 391 { 392 switch (type) 393 { 394 case PropertyType.UInt8: 395 case PropertyType.Int16: 396 case PropertyType.UInt16: 397 case PropertyType.Int32: 398 case PropertyType.UInt32: 399 case PropertyType.Int64: 400 case PropertyType.UInt64: 401 case PropertyType.Single: 402 case PropertyType.Double: 403 return true; 404 405 default: 406 return McgMarshal.IsEnum(data); 407 } 408 } 409 CheckType(PropertyType unboxType)410 private void CheckType(PropertyType unboxType) 411 { 412 if (this.get_Type() != unboxType) 413 { 414 throw CreateExceptionForInvalidCast(this.get_Type(), unboxType); 415 } 416 } 417 CreateExceptionForInvalidCast( PropertyType type, PropertyType unboxType)418 private static System.InvalidCastException CreateExceptionForInvalidCast( 419 PropertyType type, 420 PropertyType unboxType) 421 { 422 System.InvalidCastException ex = new System.InvalidCastException(SR.Format(SR.PropertyValue_InvalidCast, type, unboxType)); 423 McgMarshal.SetExceptionErrorCode(ex, Interop.COM.TYPE_E_TYPEMISMATCH); 424 return ex; 425 } 426 CreateExceptionForInvalidCoersion( PropertyType type, object value, PropertyType unboxType, int hr)427 private static System.InvalidCastException CreateExceptionForInvalidCoersion( 428 PropertyType type, 429 object value, 430 PropertyType unboxType, 431 int hr) 432 { 433 InvalidCastException ex = new InvalidCastException(SR.Format(SR.PropertyValue_InvalidCoersion, type, value, unboxType)); 434 McgMarshal.SetExceptionErrorCode(ex, hr); 435 return ex; 436 } 437 } 438 439 internal class ReferenceUtility 440 { GetWellKnownScalar(IPropertyValue ipv, PropertyType type)441 internal static object GetWellKnownScalar(IPropertyValue ipv, PropertyType type) 442 { 443 switch (type) 444 { 445 case PropertyType.UInt8: 446 return ipv.GetUInt8(); 447 448 case PropertyType.Int16: 449 return ipv.GetInt16(); 450 451 case PropertyType.UInt16: 452 return ipv.GetUInt16(); 453 454 case PropertyType.Int32: 455 return ipv.GetInt32(); 456 457 case PropertyType.UInt32: 458 return ipv.GetUInt32(); 459 460 case PropertyType.Int64: 461 return ipv.GetInt64(); 462 463 case PropertyType.UInt64: 464 return ipv.GetUInt64(); 465 466 case PropertyType.Single: 467 return ipv.GetSingle(); 468 469 case PropertyType.Double: 470 return ipv.GetDouble(); 471 } 472 473 Debug.Assert(false); 474 return null; 475 } 476 } 477 } 478