diff options
Diffstat (limited to 'Rx/NET/Source/System.Reactive.PlatformServices/Reactive/Internal/PhoneShellThunks.cs')
-rw-r--r-- | Rx/NET/Source/System.Reactive.PlatformServices/Reactive/Internal/PhoneShellThunks.cs | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/Rx/NET/Source/System.Reactive.PlatformServices/Reactive/Internal/PhoneShellThunks.cs b/Rx/NET/Source/System.Reactive.PlatformServices/Reactive/Internal/PhoneShellThunks.cs new file mode 100644 index 0000000..3ce1b22 --- /dev/null +++ b/Rx/NET/Source/System.Reactive.PlatformServices/Reactive/Internal/PhoneShellThunks.cs @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if WINDOWSPHONE7 +#if !DEBUG_NO_AGENT_SUPPORT +// +// The Windows Phone Marketplace Test Kit disallows usage of types +// in the Microsoft.Phone.Shell namespace, determined by static code +// analysis, for background agents. +// +// However, with a null check for PhoneApplicationService.Current, +// we can safely use this the lifecycle events for dormant state +// transitions. In a background agent this property will be null; +// trying to create an instance of PhoneApplicationService throws +// a ComException and is required to set the Current property. +// +// In order to access the PhoneApplicationService functionality for +// non-background agent assemblies, we build a late bound wrapper +// around the APIs. +// +// See appplat\src\Frameworks\Microsoft\Phone\PhoneApplicationService.cs +// for implementation details of the class we use. +// +namespace System.Reactive.PlatformServices.Phone.Shell +{ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + using System.Reflection; + using Microsoft.Phone; + + class PhoneApplicationService + { + private static readonly object s_gate = new object(); + internal static readonly Assembly s_phshAsm; + private static readonly Type s_pasType; + private static readonly PropertyInfo s_curProp; + private static readonly EventInfo s_actdEvt; + private static readonly EventInfo s_deacEvt; + + private readonly object _target; + + static PhoneApplicationService() + { + s_phshAsm = typeof(BackgroundAgent).Assembly; // Has to be in Microsoft.Phone.dll. + s_pasType = s_phshAsm.GetType("Microsoft.Phone.Shell.PhoneApplicationService"); + s_curProp = s_pasType.GetProperty("Current", BindingFlags.Public | BindingFlags.Static); + s_actdEvt = s_curProp.PropertyType.GetEvent("Activated", BindingFlags.Public | BindingFlags.Instance); + s_deacEvt = s_curProp.PropertyType.GetEvent("Deactivated", BindingFlags.Public | BindingFlags.Instance); + } + + private PhoneApplicationService(object target) + { + _target = target; + } + + private static PhoneApplicationService s_current; + + public static PhoneApplicationService Current + { + get + { + lock (s_gate) + { + if (s_current == null) + { + var current = s_curProp.GetValue(null, null); + if (current == null) + return null; + + // + // Current provides a singleton. The constructor + // of PhoneApplicationService guarantees this, + // throwing an InvalidOperationException if more + // than one instance is created. + // + s_current = new PhoneApplicationService(current); + } + } + + return s_current; + } + } + + private Dictionary<object, object> _actdHandlers = new Dictionary<object, object>(); + + public event EventHandler<ActivatedEventArgs> Activated + { + add + { + AddHandler<ActivatedEventArgs>(s_actdEvt, _actdHandlers, value); + } + + remove + { + RemoveHandler(s_actdEvt, _actdHandlers, value); + } + } + + private Dictionary<object, object> _deacHandlers = new Dictionary<object, object>(); + + public event EventHandler<DeactivatedEventArgs> Deactivated + { + add + { + AddHandler<DeactivatedEventArgs>(s_deacEvt, _deacHandlers, value); + } + + remove + { + RemoveHandler(s_deacEvt, _deacHandlers, value); + } + } + + private void AddHandler<TEventArgs>(EventInfo evt, Dictionary<object, object> map, object handler) + where TEventArgs : EventArgs + { + var h = GetHandler<TEventArgs>(evt, handler); + var add = evt.GetAddMethod(); + + lock (s_gate) + { + map.Add(handler, h); + add.Invoke(_target, new object[] { h }); + } + } + + private void RemoveHandler(EventInfo evt, Dictionary<object, object> map, object handler) + { + var rem = evt.GetRemoveMethod(); + + lock (s_gate) + { + var h = default(object); + if (map.TryGetValue(handler, out h)) + { + // + // We assume only one handler will be attached to + // the event, hence we shouldn't worry about having + // multiple delegate instances with the same target + // being attached. This guarantee is made by the + // reference counting in HostLifecycleService. + // + // The use of a dictionary allows for reuse with + // multiple distinct handlers going forward. + // + map.Remove(handler); + rem.Invoke(_target, new object[] { h }); + } + } + } + + private static object GetHandler<TEventArgsThunk>(EventInfo evt, object call) + where TEventArgsThunk : EventArgs + { + var ht = evt.EventHandlerType; + var hp = ht.GetMethod("Invoke").GetParameters(); + + var po = Expression.Parameter(hp[0].ParameterType, hp[0].Name); + var pe = Expression.Parameter(hp[1].ParameterType, hp[1].Name); + + var h = Expression.Lambda( + ht, + Expression.Invoke( + Expression.Constant(call), + po, + Expression.New( + typeof(TEventArgsThunk).GetConstructor(new[] { typeof(object) }), + pe + ) + ), + po, + pe + ); + + return h.Compile(); + } + } + + class ActivatedEventArgs : EventArgs + { + private static readonly Type s_aeaType; + private static readonly PropertyInfo s_aipProp; + + private readonly object _target; + + static ActivatedEventArgs() + { + s_aeaType = PhoneApplicationService.s_phshAsm.GetType("Microsoft.Phone.Shell.ActivatedEventArgs"); + s_aipProp = s_aeaType.GetProperty("IsApplicationInstancePreserved", BindingFlags.Public | BindingFlags.Instance); + } + + public ActivatedEventArgs(object target) + { + _target = target; + } + + public bool IsApplicationInstancePreserved + { + get + { + return (bool)s_aipProp.GetValue(_target, null); + } + } + } + + class DeactivatedEventArgs : EventArgs + { + public DeactivatedEventArgs(object target) + { + } + } +} +#endif +#endif
\ No newline at end of file |