// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more 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 _actdHandlers = new Dictionary(); public event EventHandler Activated { add { AddHandler(s_actdEvt, _actdHandlers, value); } remove { RemoveHandler(s_actdEvt, _actdHandlers, value); } } private Dictionary _deacHandlers = new Dictionary(); public event EventHandler Deactivated { add { AddHandler(s_deacEvt, _deacHandlers, value); } remove { RemoveHandler(s_deacEvt, _deacHandlers, value); } } private void AddHandler(EventInfo evt, Dictionary map, object handler) where TEventArgs : EventArgs { var h = GetHandler(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 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(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