2 // Value.cs: represents the unmanaged Value structure from runtime.cpp
5 // Moonlight List (moonlight-list@lists.ximian.com)
7 // Copyright 2007 Novell, Inc.
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System
.Windows
.Data
;
31 using System
.Windows
.Input
;
32 using System
.Windows
.Markup
;
33 using System
.Windows
.Media
;
34 using System
.Windows
.Media3D
;
35 using System
.Windows
.Documents
;
36 using System
.Windows
.Media
.Animation
;
37 using System
.Runtime
.InteropServices
;
38 using System
.Reflection
;
42 internal struct UnmanagedFontFamily
{
46 internal struct UnmanagedFontWeight
{
47 public FontWeightKind weight
;
50 internal struct UnmanagedFontStyle
{
51 public FontStyleKind style
;
54 internal struct UnmanagedFontStretch
{
55 public FontStretchKind stretch
;
58 internal struct UnmanagedFontSource
{
62 internal struct UnmanagedStreamCallbacks
{
64 public IntPtr CanSeek
;
65 public IntPtr CanRead
;
67 public IntPtr Position
;
74 [StructLayout(LayoutKind
.Sequential
)]
75 internal struct UnmanagedPropertyPath
{
76 public IntPtr pathString
;
77 public IntPtr expandedPathString
;
78 public IntPtr property
;
81 internal struct UnmanagedColor
{
87 public Color
ToColor ()
89 return Color
.FromArgb ((byte)(255 * a
), (byte)(255 * r
), (byte)(255 * g
), (byte)(255 * b
));
93 internal struct UnmanagedUri
{
94 public bool isAbsolute
;
102 public IntPtr _params
;
104 public IntPtr fragment
;
105 public IntPtr originalString
;
107 public unsafe static IntPtr
FromUri (Uri managed_uri
)
109 IntPtr uri
= Marshal
.AllocHGlobal (sizeof (UnmanagedUri
));
111 UnmanagedUri
*uuri
= (UnmanagedUri
*)uri
;
112 uuri
->scheme
= IntPtr
.Zero
;
113 uuri
->user
= IntPtr
.Zero
;
114 uuri
->auth
= IntPtr
.Zero
;
115 uuri
->passwd
= IntPtr
.Zero
;
116 uuri
->host
= IntPtr
.Zero
;
117 uuri
->path
= IntPtr
.Zero
;
118 uuri
->_params
= IntPtr
.Zero
;
119 uuri
->query
= IntPtr
.Zero
;
120 uuri
->fragment
= IntPtr
.Zero
;
121 uuri
->originalString
= IntPtr
.Zero
;
123 NativeMethods
.uri_parse (uri
, managed_uri
.OriginalString
, false);
125 uuri
->isAbsolute
= managed_uri
.IsAbsoluteUri
;
131 [StructLayout(LayoutKind
.Sequential
)]
132 internal struct ManagedTypeInfo
{
133 public IntPtr assembly_name
;
134 public IntPtr full_name
;
137 [StructLayout(LayoutKind
.Explicit
)]
138 internal struct ValUnion
{
139 [FieldOffset(0)] public float f
;
140 [FieldOffset(0)] public double d
;
141 [FieldOffset(0)] public long i64
;
142 [FieldOffset(0)] public ulong ui64
;
143 [FieldOffset(0)] public int i32
;
144 [FieldOffset(0)] public uint ui32
;
145 [FieldOffset(0)] public IntPtr p
;
148 internal struct Value
{
149 // Note: Keep these flags in sync with the native version
150 const int NullFlag
= 1;
157 get { return (bitfield & NullFlag) == NullFlag; }
160 bitfield
|= NullFlag
;
162 bitfield
&= ~NullFlag
;
166 public static Value Empty
{
167 get { return new Value (); }
170 static bool slow_codepath_error_shown
= false;
172 public static unsafe object ToObject (Type type
, Value
* value)
174 if (value == null || value->IsNull
) {
181 case Kind
.DEPENDENCYPROPERTY
:
182 return DependencyProperty
.Lookup (value->u
.p
);
185 return value->u
.i32
!= 0;
194 return value->u
.ui64
;
200 return new TimeSpan (value->u
.i64
);
203 // marshall back to the .NET type that we simply serialised as int for unmanaged usage
204 int i32
= value->u
.i32
;
205 if (type
== typeof (System
.Windows
.Input
.Cursor
))
206 return Cursors
.FromEnum ((CursorType
)i32
);
207 else if (type
== typeof (TextDecorationCollection
))
208 return (i32
== (int) TextDecorationKind
.Underline
) ? TextDecorations
.Underline
: null;
209 else if (type
!= null && type
.IsEnum
)
210 return Enum
.ToObject (type
, i32
);
215 return (char) value->u
.ui32
;
218 return NativeDependencyObjectHelper
.FromIntPtr (value->u
.p
);
221 IntPtr managed_object
= value->u
.p
;
222 GCHandle handle
= GCHandle
.FromIntPtr (managed_object
);
223 return handle
.Target
;
226 string str
= Marshal
.PtrToStringAuto (value->u
.p
);
230 // marshall back to the .NET type that we simply serialised as 'string' for unmanaged usage
231 if (type
== typeof (System
.Windows
.Markup
.XmlLanguage
))
232 return XmlLanguage
.GetLanguage (str
);
238 UnmanagedUri
*uri
= (UnmanagedUri
*)value->u
.p
;
239 return uri
->originalString
== IntPtr
.Zero
240 ? new Uri("", UriKind
.Relative
)
241 : new Uri (Marshal
.PtrToStringAuto (uri
->originalString
),
242 uri
->isAbsolute
? UriKind
.Absolute
: UriKind
.Relative
);
245 case Kind
.XMLLANGUAGE
: {
246 string str
= Marshal
.PtrToStringAuto (value->u
.p
);
247 return XmlLanguage
.GetLanguage (str
);
250 case Kind
.FONTFAMILY
: {
251 UnmanagedFontFamily
*family
= (UnmanagedFontFamily
*)value->u
.p
;
252 return new FontFamily (family
== null ? null : Marshal
.PtrToStringAuto (family
->source
));
255 case Kind
.FONTSTRETCH
: {
256 UnmanagedFontStretch
*stretch
= (UnmanagedFontStretch
*)value->u
.p
;
257 return new FontStretch (stretch
== null ? FontStretchKind
.Normal
: stretch
->stretch
);
260 case Kind
.FONTSTYLE
: {
261 UnmanagedFontStyle
*style
= (UnmanagedFontStyle
*)value->u
.p
;
262 return new FontStyle (style
== null ? FontStyleKind
.Normal
: style
->style
);
265 case Kind
.FONTWEIGHT
: {
266 UnmanagedFontWeight
*weight
= (UnmanagedFontWeight
*)value->u
.p
;
267 return new FontWeight (weight
== null ? FontWeightKind
.Normal
: weight
->weight
);
270 case Kind
.FONTSOURCE
: {
271 UnmanagedFontSource
*source
= (UnmanagedFontSource
*) value->u
.p
;
272 ManagedStreamCallbacks callbacks
;
273 StreamWrapper wrapper
;
275 callbacks
= (ManagedStreamCallbacks
) Marshal
.PtrToStructure (source
->stream
, typeof (ManagedStreamCallbacks
));
277 wrapper
= (StreamWrapper
) GCHandle
.FromIntPtr (callbacks
.handle
).Target
;
279 return new FontSource (wrapper
.stream
);
282 case Kind
.PROPERTYPATH
: {
283 UnmanagedPropertyPath
*propertypath
= (UnmanagedPropertyPath
*) value->u
.p
;
284 if (propertypath
== null)
285 return new PropertyPath (null);
286 if (propertypath
->property
!= IntPtr
.Zero
)
288 return new PropertyPath (Marshal
.PtrToStringAuto (propertypath
->pathString
));
292 Point
*point
= (Point
*)value->u
.p
;
293 return (point
== null) ? new Point (0,0) : *point
;
297 Rect
*rect
= (Rect
*)value->u
.p
;
298 return (rect
== null) ? new Rect (0,0,0,0) : *rect
;
302 Size
*size
= (Size
*)value->u
.p
;
303 return (size
== null) ? new Size (0,0) : *size
;
306 case Kind
.CORNERRADIUS
: {
307 CornerRadius
*corner
= (CornerRadius
*)value->u
.p
;
308 return (corner
== null) ? new CornerRadius (0) : *corner
;
311 case Kind
.THICKNESS
: {
312 Thickness
*thickness
= (Thickness
*)value->u
.p
;
313 return (thickness
== null) ? new Thickness (0) : *thickness
;
317 UnmanagedColor
*color
= (UnmanagedColor
*)value->u
.p
;
320 return color
->ToColor ();
324 case Kind
.UNMANAGEDMATRIX
: {
325 return new Matrix (value->u
.p
);
329 case Kind
.UNMANAGEDMATRIX3D
: {
330 return new Matrix3D (value->u
.p
);
333 case Kind
.DURATION
: {
334 Duration
* duration
= (Duration
*)value->u
.p
;
335 return (duration
== null) ? Duration
.Automatic
: *duration
;
339 KeyTime
* keytime
= (KeyTime
*)value->u
.p
;
340 return (keytime
== null) ? KeyTime
.FromTimeSpan (TimeSpan
.Zero
) : *keytime
;
343 case Kind
.GRIDLENGTH
: {
344 GridLength
* gridlength
= (GridLength
*)value->u
.p
;
345 return (gridlength
== null) ? new GridLength () : *gridlength
;
348 case Kind
.REPEATBEHAVIOR
: {
349 RepeatBehavior
*repeat
= (RepeatBehavior
*)value->u
.p
;
350 return (repeat
== null) ? new RepeatBehavior () : *repeat
;
353 case Kind
.MEDIAATTRIBUTE_COLLECTION
: {
354 MediaAttributeCollection attrs
= (MediaAttributeCollection
) NativeDependencyObjectHelper
.Lookup (value->k
, value->u
.p
);
355 return attrs
.AsDictionary ();
358 case Kind
.MANAGEDTYPEINFO
: {
359 ManagedTypeInfo
*type_info
= (ManagedTypeInfo
*) value->u
.p
;
361 if (type_info
== null)
364 string assembly_name
= Marshal
.PtrToStringAuto (type_info
->assembly_name
);
365 string full_name
= Marshal
.PtrToStringAuto (type_info
->full_name
);
367 Assembly asm
= Application
.GetAssembly (assembly_name
);
369 return asm
.GetType (full_name
);
375 if (!slow_codepath_error_shown
){
376 Report
.Warning ("DependencyObject type testing now using a very slow code path");
377 slow_codepath_error_shown
= true;
380 if (NativeMethods
.type_is_dependency_object (value->k
)){
381 // Old fast test: if (value->k > Kind.DEPENDENCY_OBJECT){
383 if (value->u
.p
== IntPtr
.Zero
)
386 return NativeDependencyObjectHelper
.Lookup (value->k
, value->u
.p
);
389 throw new Exception (String
.Format ("Do not know how to convert {0} {1}", value->k
, (int) value->k
));
392 public static unsafe object ToObject (Type type
, IntPtr
value)
394 return ToObject (type
, (Value
*) value);
397 public static Value
FromObject (object v
)
399 return FromObject (v
, false);
403 // How do we support "null" values, should the caller take care of that?
405 public static Value
FromObject (object v
, bool box_value_types
)
407 Value
value = new Value ();
410 // get rid of this case right away.
411 if (box_value_types
&& v
.GetType().IsValueType
) {
412 //Console.WriteLine ("Boxing a value of type {0}:", v.GetType());
414 GCHandle handle
= GCHandle
.Alloc (v
);
415 value.k
= Kind
.MANAGED
;
416 value.u
.p
= GCHandle
.ToIntPtr (handle
);
420 if (v
is IEasingFunction
&& !(v
is EasingFunctionBase
))
421 v
= new EasingFunctionWrapper (v
as IEasingFunction
);
423 if (v
is INativeDependencyObjectWrapper
) {
424 INativeDependencyObjectWrapper dov
= (INativeDependencyObjectWrapper
) v
;
426 if (dov
.NativeHandle
== IntPtr
.Zero
)
427 throw new Exception (String
.Format (
428 "Object {0} has not set its native property", dov
.GetType()));
430 NativeMethods
.event_object_ref (dov
.NativeHandle
);
432 value.k
= dov
.GetKind ();
433 value.u
.p
= dov
.NativeHandle
;
435 } else if (v
is DependencyProperty
) {
436 value.k
= Kind
.DEPENDENCYPROPERTY
;
437 value.u
.p
= ((DependencyProperty
)v
).Native
;
439 else if (v
is int || (v
.GetType ().IsEnum
&& Enum
.GetUnderlyingType (v
.GetType()) == typeof(int))) {
440 value.k
= Kind
.INT32
;
441 value.u
.i32
= (int) v
;
443 else if (v
is bool) {
445 value.u
.i32
= ((bool) v
) ? 1 : 0;
447 else if (v
is double) {
448 value.k
= Kind
.DOUBLE
;
449 value.u
.d
= (double) v
;
451 else if (v
is float) {
452 value.k
= Kind
.FLOAT
;
453 value.u
.f
= (float) v
;
455 else if (v
is long) {
456 value.k
= Kind
.INT64
;
457 value.u
.i64
= (long) v
;
459 else if (v
is TimeSpan
) {
460 TimeSpan ts
= (TimeSpan
) v
;
461 value.k
= Kind
.TIMESPAN
;
462 value.u
.i64
= ts
.Ticks
;
464 else if (v
is ulong) {
465 value.k
= Kind
.UINT64
;
466 value.u
.ui64
= (ulong) v
;
468 else if (v
is uint) {
469 value.k
= Kind
.UINT32
;
470 value.u
.ui32
= (uint) v
;
472 else if (v
is char) {
474 value.u
.ui32
= (uint) (char) v
;
476 else if (v
is string) {
477 value.k
= Kind
.STRING
;
479 value.u
.p
= StringToIntPtr ((string) v
);
481 else if (v
is Rect
) {
482 Rect rect
= (Rect
) v
;
484 value.u
.p
= Marshal
.AllocHGlobal (sizeof (Rect
));
485 Marshal
.StructureToPtr (rect
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
487 else if (v
is Size
) {
488 Size size
= (Size
) v
;
490 value.u
.p
= Marshal
.AllocHGlobal (sizeof (Size
));
491 Marshal
.StructureToPtr (size
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
493 else if (v
is CornerRadius
) {
494 CornerRadius corner
= (CornerRadius
) v
;
495 value.k
= Kind
.CORNERRADIUS
;
496 value.u
.p
= Marshal
.AllocHGlobal (sizeof (CornerRadius
));
497 Marshal
.StructureToPtr (corner
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
499 else if (v
is Point
) {
500 Point pnt
= (Point
) v
;
501 value.k
= Kind
.POINT
;
502 value.u
.p
= Marshal
.AllocHGlobal (sizeof (Point
));
503 Marshal
.StructureToPtr (pnt
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
505 else if (v
is Thickness
) {
506 Thickness thickness
= (Thickness
)v
;
507 value.k
= Kind
.THICKNESS
;
508 value.u
.p
= Marshal
.AllocHGlobal (sizeof (Thickness
));
509 Marshal
.StructureToPtr (thickness
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
511 else if (v
is Color
) {
513 value.k
= Kind
.COLOR
;
514 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedColor
));
515 UnmanagedColor
* color
= (UnmanagedColor
*) value.u
.p
;
516 color
->r
= c
.R
/ 255.0f
;
517 color
->g
= c
.G
/ 255.0f
;
518 color
->b
= c
.B
/ 255.0f
;
519 color
->a
= c
.A
/ 255.0f
;
521 else if (v
is Matrix
) {
522 // hack around the fact that managed Matrix is a struct while unmanaged Matrix is a DO
523 // i.e. the unmanaged and managed structure layouts ARE NOT equal
524 return FromObject (new UnmanagedMatrix ((Matrix
) v
), box_value_types
);
526 else if (v
is Duration
) {
527 Duration d
= (Duration
) v
;
528 value.k
= Kind
.DURATION
;
529 value.u
.p
= Marshal
.AllocHGlobal (sizeof (Duration
));
530 Marshal
.StructureToPtr (d
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
532 else if (v
is KeyTime
) {
533 KeyTime k
= (KeyTime
) v
;
534 value.k
= Kind
.KEYTIME
;
535 value.u
.p
= Marshal
.AllocHGlobal (sizeof (KeyTime
));
536 Marshal
.StructureToPtr (k
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
538 else if (v
is RepeatBehavior
) {
539 RepeatBehavior d
= (RepeatBehavior
) v
;
540 value.k
= Kind
.REPEATBEHAVIOR
;
541 value.u
.p
= Marshal
.AllocHGlobal (sizeof (RepeatBehavior
));
542 Marshal
.StructureToPtr (d
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
544 else if (v
is FontFamily
) {
545 FontFamily family
= (FontFamily
) v
;
546 value.k
= Kind
.FONTFAMILY
;
547 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedFontFamily
));
548 Marshal
.StructureToPtr (family
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
551 else if (v
is FontSource
) {
552 FontSource source
= (FontSource
) v
;
554 value.k
= Kind
.FONTSOURCE
;
556 if (source
.wrapper
!= null) {
557 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedFontSource
));
558 UnmanagedFontSource
*ufs
= (UnmanagedFontSource
*) value.u
.p
;
559 ManagedStreamCallbacks callbacks
= source
.wrapper
.GetCallbacks ();
560 ufs
->stream
= Marshal
.AllocHGlobal (sizeof (UnmanagedStreamCallbacks
));
561 Marshal
.StructureToPtr (callbacks
, ufs
->stream
, false);
567 else if (v
is PropertyPath
) {
568 PropertyPath propertypath
= (PropertyPath
) v
;
569 value.k
= Kind
.PROPERTYPATH
;
570 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedPropertyPath
));
572 UnmanagedPropertyPath
*upp
= (UnmanagedPropertyPath
*) value.u
.p
;
573 upp
->property
= propertypath
.NativeDP
;
574 if (upp
->property
== IntPtr
.Zero
)
575 upp
->pathString
= StringToIntPtr (propertypath
.Path
);
577 upp
->pathString
= IntPtr
.Zero
;
578 upp
->expandedPathString
= IntPtr
.Zero
;
584 value.u
.p
= UnmanagedUri
.FromUri (uri
);
586 else if (v
is XmlLanguage
) {
587 XmlLanguage lang
= (XmlLanguage
) v
;
589 value.k
= Kind
.STRING
;
591 value.u
.p
= StringToIntPtr (lang
.IetfLanguageTag
);
593 else if (v
is Cursor
) {
594 Cursor c
= (Cursor
) v
;
596 value.k
= Kind
.INT32
;
598 value.u
.i32
= (int)c
.cursor
;
600 else if (v
is GridLength
) {
601 GridLength gl
= (GridLength
) v
;
602 value.k
= Kind
.GRIDLENGTH
;
603 value.u
.p
= Marshal
.AllocHGlobal (sizeof (GridLength
));
604 Marshal
.StructureToPtr (gl
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
606 else if (v
is FontStretch
) {
607 FontStretch stretch
= (FontStretch
) v
;
608 value.k
= Kind
.FONTSTRETCH
;
609 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedFontStretch
));
610 Marshal
.StructureToPtr (stretch
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
612 else if (v
is FontStyle
) {
613 FontStyle style
= (FontStyle
) v
;
614 value.k
= Kind
.FONTSTYLE
;
615 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedFontStyle
));
616 Marshal
.StructureToPtr (style
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
618 else if (v
is FontWeight
) {
619 FontWeight weight
= (FontWeight
) v
;
620 value.k
= Kind
.FONTWEIGHT
;
621 value.u
.p
= Marshal
.AllocHGlobal (sizeof (UnmanagedFontWeight
));
622 Marshal
.StructureToPtr (weight
, value.u
.p
, false); // Unmanaged and managed structure layout is equal.
624 else if (v
is PixelFormat
) {
625 // FIXME we need to make an unmanaged PixelFormat struct and
626 // marshal it like the FontStretch/Style/Weight structs above.
627 value.k
= Kind
.INT32
;
628 value.u
.i32
= v
.GetHashCode ();
630 else if (v
is TextDecorationCollection
) {
631 value.k
= Kind
.INT32
;
632 value.u
.i32
= (int) (v
as TextDecorationCollection
).Kind
;
634 else if (v
is Type
) {
636 ManagedTypeInfo mti
= new ManagedTypeInfo ();
638 mti
.assembly_name
= StringToIntPtr (t
.Assembly
.GetName ().Name
);
639 mti
.full_name
= StringToIntPtr (t
.FullName
);
641 value.k
= Kind
.MANAGEDTYPEINFO
;
642 value.u
.p
= Marshal
.AllocHGlobal (sizeof (ManagedTypeInfo
));
643 Marshal
.StructureToPtr (mti
, value.u
.p
, false);
646 //Console.WriteLine ("Do not know how to encode {0} yet, boxing it", v.GetType ());
648 // TODO: We probably need to marshal types that can animate as the
649 // corresponding type (Point, Double, Color, etc).
650 // TODO: We need to store the GCHandle somewhere so that we can free it,
651 // or register a callback on the surface for the unmanaged code to call.
652 GCHandle handle
= GCHandle
.Alloc (v
);
653 value.k
= Kind
.MANAGED
;
654 value.u
.p
= GCHandle
.ToIntPtr (handle
);
660 public static IntPtr
StringToIntPtr (string str
)
665 byte [] bytes
= System
.Text
.Encoding
.UTF8
.GetBytes (str
);
666 IntPtr result
= Marshal
.AllocHGlobal (bytes
.Length
+ 1);
667 Marshal
.Copy (bytes
, 0, result
, bytes
.Length
);
668 Marshal
.WriteByte (result
, bytes
.Length
, 0);