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