// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // // Microsoft using System.Security; using System.Collections; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Collections.Specialized; using System.Runtime.CompilerServices; using System.Windows.Input; namespace System.Runtime.InteropServices.WindowsRuntime { // Local definition of Windows.UI.Xaml.Interop.INotifyCollectionChangedEventArgs [ComImport] [Guid("4cf68d33-e3f2-4964-b85e-945b4f7e2f21")] [WindowsRuntimeImport] internal interface INotifyCollectionChangedEventArgs { NotifyCollectionChangedAction Action { get; } IList NewItems { get; } IList OldItems { get; } int NewStartingIndex { get; } int OldStartingIndex { get; } } // Local definition of Windows.UI.Xaml.Data.IPropertyChangedEventArgs [ComImport] [Guid("4f33a9a0-5cf4-47a4-b16f-d7faaf17457e")] [WindowsRuntimeImport] internal interface IPropertyChangedEventArgs { string PropertyName { get; } } // Local definition of Windows.UI.Xaml.Interop.INotifyCollectionChanged [ComImport] [Guid("28b167d5-1a31-465b-9b25-d5c3ae686c40")] [WindowsRuntimeImport] internal interface INotifyCollectionChanged_WinRT { EventRegistrationToken add_CollectionChanged(NotifyCollectionChangedEventHandler value); void remove_CollectionChanged(EventRegistrationToken token); } // Local definition of Windows.UI.Xaml.Data.INotifyPropertyChanged [ComImport] [Guid("cf75d69c-f2f4-486b-b302-bb4c09baebfa")] [WindowsRuntimeImport] internal interface INotifyPropertyChanged_WinRT { EventRegistrationToken add_PropertyChanged(PropertyChangedEventHandler value); void remove_PropertyChanged(EventRegistrationToken token); } // Local definition of Windows.UI.Xaml.Input.ICommand [ComImport] [Guid("e5af3542-ca67-4081-995b-709dd13792df")] [WindowsRuntimeImport] internal interface ICommand_WinRT { EventRegistrationToken add_CanExecuteChanged(EventHandler value); void remove_CanExecuteChanged(EventRegistrationToken token); bool CanExecute(object parameter); void Execute(object parameter); } // Local definition of Windows.UI.Xaml.Interop.NotifyCollectionChangedEventHandler [Guid("ca10b37c-f382-4591-8557-5e24965279b0")] [WindowsRuntimeImport] internal delegate void NotifyCollectionChangedEventHandler_WinRT(object sender, NotifyCollectionChangedEventArgs e); // Local definition of Windows.UI.Xaml.Data.PropertyChangedEventHandler [Guid("50f19c16-0a22-4d8e-a089-1ea9951657d2")] [WindowsRuntimeImport] internal delegate void PropertyChangedEventHandler_WinRT(object sender, PropertyChangedEventArgs e); internal static class NotifyCollectionChangedEventArgsMarshaler { // Extracts properties from a managed NotifyCollectionChangedEventArgs and passes them to // a VM-implemented helper that creates a WinRT NotifyCollectionChangedEventArgs instance. // This method is called from IL stubs and needs to have its token stabilized. [SecurityCritical] static internal IntPtr ConvertToNative(NotifyCollectionChangedEventArgs managedArgs) { if (managedArgs == null) return IntPtr.Zero; return System.StubHelpers.EventArgsMarshaler.CreateNativeNCCEventArgsInstance( (int)managedArgs.Action, managedArgs.NewItems, managedArgs.OldItems, managedArgs.NewStartingIndex, managedArgs.OldStartingIndex); } // Extracts properties from a WinRT NotifyCollectionChangedEventArgs and creates a new // managed NotifyCollectionChangedEventArgs instance. // This method is called from IL stubs and needs to have its token stabilized. [SecurityCritical] static internal NotifyCollectionChangedEventArgs ConvertToManaged(IntPtr nativeArgsIP) { if (nativeArgsIP == IntPtr.Zero) return null; object obj = System.StubHelpers.InterfaceMarshaler.ConvertToManagedWithoutUnboxing(nativeArgsIP); INotifyCollectionChangedEventArgs nativeArgs = (INotifyCollectionChangedEventArgs)obj; return new NotifyCollectionChangedEventArgs( nativeArgs.Action, nativeArgs.NewItems, nativeArgs.OldItems, nativeArgs.NewStartingIndex, nativeArgs.OldStartingIndex); } } internal static class PropertyChangedEventArgsMarshaler { // Extracts PropertyName from a managed PropertyChangedEventArgs and passes them to // a VM-implemented helper that creates a WinRT PropertyChangedEventArgs instance. // This method is called from IL stubs and needs to have its token stabilized. [SecurityCritical] static internal IntPtr ConvertToNative(PropertyChangedEventArgs managedArgs) { if (managedArgs == null) return IntPtr.Zero; return System.StubHelpers.EventArgsMarshaler.CreateNativePCEventArgsInstance(managedArgs.PropertyName); } // Extracts properties from a WinRT PropertyChangedEventArgs and creates a new // managed PropertyChangedEventArgs instance. // This method is called from IL stubs and needs to have its token stabilized. [SecurityCritical] static internal PropertyChangedEventArgs ConvertToManaged(IntPtr nativeArgsIP) { if (nativeArgsIP == IntPtr.Zero) return null; object obj = System.StubHelpers.InterfaceMarshaler.ConvertToManagedWithoutUnboxing(nativeArgsIP); IPropertyChangedEventArgs nativeArgs = (IPropertyChangedEventArgs)obj; return new PropertyChangedEventArgs(nativeArgs.PropertyName); } } // This is a set of stub methods implementing the support for the managed INotifyCollectionChanged // interface on WinRT objects that support the WinRT INotifyCollectionChanged. Used by the interop // mashaling infrastructure. internal sealed class NotifyCollectionChangedToManagedAdapter { private NotifyCollectionChangedToManagedAdapter() { Contract.Assert(false, "This class is never instantiated"); } internal event NotifyCollectionChangedEventHandler CollectionChanged { // void CollectionChanged.add(NotifyCollectionChangedEventHandler) [SecurityCritical] add { INotifyCollectionChanged_WinRT _this = JitHelpers.UnsafeCast(this); // call the WinRT eventing support in mscorlib to subscribe the event Func addMethod = new Func(_this.add_CollectionChanged); Action removeMethod = new Action(_this.remove_CollectionChanged); WindowsRuntimeMarshal.AddEventHandler(addMethod, removeMethod, value); } // void CollectionChanged.remove(NotifyCollectionChangedEventHandler) [SecurityCritical] remove { INotifyCollectionChanged_WinRT _this = JitHelpers.UnsafeCast(this); // call the WinRT eventing support in mscorlib to unsubscribe the event Action removeMethod = new Action(_this.remove_CollectionChanged); WindowsRuntimeMarshal.RemoveEventHandler(removeMethod, value); } } } // This is a set of stub methods implementing the support for the WinRT INotifyCollectionChanged // interface on managed objects that support the managed INotifyCollectionChanged. Used by the interop // mashaling infrastructure. internal sealed class NotifyCollectionChangedToWinRTAdapter { private NotifyCollectionChangedToWinRTAdapter() { Contract.Assert(false, "This class is never instantiated"); } // An instance field typed as EventRegistrationTokenTable is injected into managed classed by the compiler when compiling for /t:winmdobj. // Since here the class can be an arbitrary implementation of INotifyCollectionChanged, we have to keep the EventRegistrationTokenTable's // separately, associated with the implementations using ConditionalWeakTable. private static ConditionalWeakTable> m_weakTable = new ConditionalWeakTable>(); // EventRegistrationToken CollectionChanged.add(NotifyCollectionChangedEventHandler value) [SecurityCritical] internal EventRegistrationToken add_CollectionChanged(NotifyCollectionChangedEventHandler value) { INotifyCollectionChanged _this = JitHelpers.UnsafeCast(this); EventRegistrationTokenTable table = m_weakTable.GetOrCreateValue(_this); EventRegistrationToken token = table.AddEventHandler(value); _this.CollectionChanged += value; return token; } // void CollectionChanged.remove(EventRegistrationToken token) [SecurityCritical] internal void remove_CollectionChanged(EventRegistrationToken token) { INotifyCollectionChanged _this = JitHelpers.UnsafeCast(this); EventRegistrationTokenTable table = m_weakTable.GetOrCreateValue(_this); NotifyCollectionChangedEventHandler handler = table.ExtractHandler(token); if (handler != null) { _this.CollectionChanged -= handler; } } } // This is a set of stub methods implementing the support for the managed INotifyPropertyChanged // interface on WinRT objects that support the WinRT INotifyPropertyChanged. Used by the interop // mashaling infrastructure. internal sealed class NotifyPropertyChangedToManagedAdapter { private NotifyPropertyChangedToManagedAdapter() { Contract.Assert(false, "This class is never instantiated"); } internal event PropertyChangedEventHandler PropertyChanged { // void PropertyChanged.add(PropertyChangedEventHandler) [SecurityCritical] add { INotifyPropertyChanged_WinRT _this = JitHelpers.UnsafeCast(this); // call the WinRT eventing support in mscorlib to subscribe the event Func addMethod = new Func(_this.add_PropertyChanged); Action removeMethod = new Action(_this.remove_PropertyChanged); WindowsRuntimeMarshal.AddEventHandler(addMethod, removeMethod, value); } // void PropertyChanged.remove(PropertyChangedEventHandler) [SecurityCritical] remove { INotifyPropertyChanged_WinRT _this = JitHelpers.UnsafeCast(this); // call the WinRT eventing support in mscorlib to unsubscribe the event Action removeMethod = new Action(_this.remove_PropertyChanged); WindowsRuntimeMarshal.RemoveEventHandler(removeMethod, value); } } } // This is a set of stub methods implementing the support for the WinRT INotifyPropertyChanged // interface on managed objects that support the managed INotifyPropertyChanged. Used by the interop // mashaling infrastructure. internal sealed class NotifyPropertyChangedToWinRTAdapter { private NotifyPropertyChangedToWinRTAdapter() { Contract.Assert(false, "This class is never instantiated"); } // An instance field typed as EventRegistrationTokenTable is injected into managed classed by the compiler when compiling for /t:winmdobj. // Since here the class can be an arbitrary implementation of INotifyCollectionChanged, we have to keep the EventRegistrationTokenTable's // separately, associated with the implementations using ConditionalWeakTable. private static ConditionalWeakTable> m_weakTable = new ConditionalWeakTable>(); // EventRegistrationToken PropertyChanged.add(PropertyChangedEventHandler value) [SecurityCritical] internal EventRegistrationToken add_PropertyChanged(PropertyChangedEventHandler value) { INotifyPropertyChanged _this = JitHelpers.UnsafeCast(this); EventRegistrationTokenTable table = m_weakTable.GetOrCreateValue(_this); EventRegistrationToken token = table.AddEventHandler(value); _this.PropertyChanged += value; return token; } // void PropertyChanged.remove(EventRegistrationToken token) [SecurityCritical] internal void remove_PropertyChanged(EventRegistrationToken token) { INotifyPropertyChanged _this = JitHelpers.UnsafeCast(this); EventRegistrationTokenTable table = m_weakTable.GetOrCreateValue(_this); PropertyChangedEventHandler handler = table.ExtractHandler(token); if (handler != null) { _this.PropertyChanged -= handler; } } } // This is a set of stub methods implementing the support for the managed ICommand // interface on WinRT objects that support the WinRT ICommand_WinRT. // Used by the interop mashaling infrastructure. // Instances of this are really RCWs of ICommand_WinRT (not ICommandToManagedAdapter or any ICommand). [SecurityCritical] internal sealed class ICommandToManagedAdapter /*: System.Windows.Input.ICommand*/ { private static ConditionalWeakTable> m_weakTable = new ConditionalWeakTable>(); private ICommandToManagedAdapter() { Contract.Assert(false, "This class is never instantiated"); } private event EventHandler CanExecuteChanged { // void CanExecuteChanged.add(EventHandler) add { ICommand_WinRT _this = JitHelpers.UnsafeCast(this); // call the WinRT eventing support in mscorlib to subscribe the event Func, EventRegistrationToken> addMethod = new Func, EventRegistrationToken>(_this.add_CanExecuteChanged); Action removeMethod = new Action(_this.remove_CanExecuteChanged); // value is of type System.EventHandler, but ICommand_WinRT (and thus WindowsRuntimeMarshal.AddEventHandler) // expects an instance of EventHandler. So we get/create a wrapper of value here. EventHandler handler_WinRT = m_weakTable.GetValue(value, ICommandAdapterHelpers.CreateWrapperHandler); WindowsRuntimeMarshal.AddEventHandler>(addMethod, removeMethod, handler_WinRT); } // void CanExecuteChanged.remove(EventHandler) remove { ICommand_WinRT _this = JitHelpers.UnsafeCast(this); // call the WinRT eventing support in mscorlib to unsubscribe the event Action removeMethod = new Action(_this.remove_CanExecuteChanged); // value is of type System.EventHandler, but ICommand_WinRT (and thus WindowsRuntimeMarshal.RemoveEventHandler) // expects an instance of EventHandler. So we get/create a wrapper of value here. // Also we do a value check rather than an instance check to ensure that different instances of the same delegates are treated equal. EventHandler handler_WinRT = ICommandAdapterHelpers.GetValueFromEquivalentKey(m_weakTable , value, ICommandAdapterHelpers.CreateWrapperHandler); WindowsRuntimeMarshal.RemoveEventHandler>(removeMethod, handler_WinRT); } } private bool CanExecute(object parameter) { ICommand_WinRT _this = JitHelpers.UnsafeCast(this); return _this.CanExecute(parameter); } private void Execute(object parameter) { ICommand_WinRT _this = JitHelpers.UnsafeCast(this); _this.Execute(parameter); } } // This is a set of stub methods implementing the support for the WinRT ICommand_WinRT // interface on managed objects that support the managed ICommand interface. // Used by the interop mashaling infrastructure. // Instances of this are really CCWs of ICommand (not ICommandToWinRTAdapter or any ICommand_WinRT). [SecurityCritical] internal sealed class ICommandToWinRTAdapter /*: ICommand_WinRT*/ { private ICommandToWinRTAdapter() { Contract.Assert(false, "This class is never instantiated"); } // An instance field typed as EventRegistrationTokenTable is injected into managed classed by the compiler when compiling for /t:winmdobj. // Since here the class can be an arbitrary implementation of ICommand, we have to keep the EventRegistrationTokenTable's // separately, associated with the implementations using ConditionalWeakTable. private static ConditionalWeakTable> m_weakTable = new ConditionalWeakTable>(); // EventRegistrationToken PropertyChanged.add(EventHandler value) private EventRegistrationToken add_CanExecuteChanged(EventHandler value) { ICommand _this = JitHelpers.UnsafeCast(this); EventRegistrationTokenTable table = m_weakTable.GetOrCreateValue(_this); EventHandler handler = ICommandAdapterHelpers.CreateWrapperHandler(value); EventRegistrationToken token = table.AddEventHandler(handler); _this.CanExecuteChanged += handler; return token; } // void PropertyChanged.remove(EventRegistrationToken token) private void remove_CanExecuteChanged(EventRegistrationToken token) { ICommand _this = JitHelpers.UnsafeCast(this); EventRegistrationTokenTable table = m_weakTable.GetOrCreateValue(_this); EventHandler handler = table.ExtractHandler(token); if (handler != null) { _this.CanExecuteChanged -= handler; } } private bool CanExecute(object parameter) { ICommand _this = JitHelpers.UnsafeCast(this); return _this.CanExecute(parameter); } private void Execute(object parameter) { ICommand _this = JitHelpers.UnsafeCast(this); _this.Execute(parameter); } } // A couple of ICommand adapter helpers need to be transparent, and so are in their own type internal static class ICommandAdapterHelpers { internal static EventHandler CreateWrapperHandler(EventHandler handler) { // Check whether it is a round-tripping case i.e. the sender is of the type eventArgs, // If so we use it else we pass EventArgs.Empty return (object sender, object e) => { EventArgs eventArgs = e as EventArgs; handler(sender, (eventArgs == null ? System.EventArgs.Empty : eventArgs)); }; } internal static EventHandler CreateWrapperHandler(EventHandler handler) { return (object sender, EventArgs e) => handler(sender, e); } internal static EventHandler GetValueFromEquivalentKey( ConditionalWeakTable> table, EventHandler key, ConditionalWeakTable>.CreateValueCallback callback) { EventHandler value; // Find the key in the table using a value check rather than an instance check. EventHandler existingKey = table.FindEquivalentKeyUnsafe(key, out value); if (existingKey == null) { value = callback(key); table.Add(key, value); } return value; } } }