File: Core\CSharp\System\Windows\Media\EventProxy.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
//------------------------------------------------------------------------------
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, 2003
//
//  File:       eventproxy.cs
//------------------------------------------------------------------------------
#pragma warning disable 1634, 1691 // Allow suppression of certain presharp messages
 
using System.Windows.Media;
using System;
using MS.Internal;
using MS.Win32;
using System.Reflection;
using System.Collections;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using MS.Internal.PresentationCore;
 
// TODOTODO Make our own namespace for internal stuff.
namespace System.Windows.Media
{
    #region EventProxyDescriptor
    [StructLayout(LayoutKind.Sequential)]
    internal struct EventProxyDescriptor
    {
        internal delegate void Dispose(
            ref EventProxyDescriptor pEPD
            );
 
        internal delegate int RaiseEvent(
            ref EventProxyDescriptor pEPD,
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] buffer,
            uint cb
            );
 
        internal Dispose pfnDispose;
        internal RaiseEvent pfnRaiseEvent;
 
        ///<SecurityNote>
        ///     Critical: calls GCHandle.get_Target which LinkDemands
        ///     TreatAsSafe: can't pass in an arbitrary class, only EventProxyDescriptor.  Also, it's OK to dispose this.
        ///</SecurityNote> 
        [SecurityCritical, SecurityTreatAsSafe]
        internal static void StaticDispose(ref EventProxyDescriptor pEPD)
        {
            Debug.Assert(((IntPtr)pEPD.m_handle) != IntPtr.Zero, "If this asserts fires: Why is it firing? It might be legal in future.");
            EventProxyWrapper epw = (EventProxyWrapper)(pEPD.m_handle.Target);
            ((System.Runtime.InteropServices.GCHandle)(pEPD.m_handle)).Free();
        }
 
        internal System.Runtime.InteropServices.GCHandle m_handle;
    }
    #endregion
 
    #region EventProxyStaticPtrs
    /// <summary>
    /// We need to keep the delegates alive.
    /// </summary>
    internal static class EventProxyStaticPtrs
    {
        static EventProxyStaticPtrs()
        {
            EventProxyStaticPtrs.pfnDispose = new EventProxyDescriptor.Dispose(EventProxyDescriptor.StaticDispose);
            EventProxyStaticPtrs.pfnRaiseEvent = new EventProxyDescriptor.RaiseEvent(EventProxyWrapper.RaiseEvent);
 
        }
 
        internal static EventProxyDescriptor.Dispose pfnDispose;
        internal static EventProxyDescriptor.RaiseEvent pfnRaiseEvent;
    }
    #endregion
 
    #region EventProxyWrapper
    /// <summary>
    /// Event proxy wrapper will relay events from unmanaged code to managed code
    /// </summary>
    internal class EventProxyWrapper
    {
        private WeakReference target;
 
        #region Constructor
 
        private EventProxyWrapper(IInvokable invokable)
        {
            target = new WeakReference(invokable);
        }
 
        #endregion
 
        #region Verify
 
        private void Verify()
        {
            if (target == null)
            {
                throw new System.ObjectDisposedException("EventProxyWrapper");
            }
        }
 
        #endregion
 
        #region Public methods
 
        ///<SecurityNote>
        ///     Critical: calls Marshal.GetHRForException which LinkDemands
        ///     TreatAsSafe: ok to return an hresult in partial trust
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe] 
        public int RaiseEvent(byte[] buffer, uint cb)
        {
#pragma warning disable 6500
            try
            {
                Verify();
                IInvokable invokable = (IInvokable)target.Target;
                if (invokable != null)
                {
                    invokable.RaiseEvent(buffer, (int)cb);
                }
                else
                {
                    // return E_HANDLE to notify that object is no longer alive
 
                    return NativeMethods.E_HANDLE;
                }
            }
            catch (Exception e)
            {
                return Marshal.GetHRForException(e);
            }
#pragma warning restore 6500
 
            return NativeMethods.S_OK;
        }
 
        #endregion
 
        #region Delegate Implemetations
        /// <SecurityNote>
        ///     Critical: This code calls into handle to get Target which has a link demand
        ///     TreatAsSafe: EventProxyWrapper is safe to expose
        /// </SecurityNote>
        [SecurityCritical,SecurityTreatAsSafe]
        internal static EventProxyWrapper FromEPD(ref EventProxyDescriptor epd)
        {
            Debug.Assert(((IntPtr)epd.m_handle) != IntPtr.Zero, "Stream is disposed.");
            System.Runtime.InteropServices.GCHandle handle = (System.Runtime.InteropServices.GCHandle)(epd.m_handle);
            return (EventProxyWrapper)(handle.Target);
        }
 
        internal static int RaiseEvent(ref EventProxyDescriptor pEPD, byte[] buffer, uint cb)
        {
            EventProxyWrapper target = EventProxyWrapper.FromEPD(ref pEPD);
            if (target != null)
            {
                return target.RaiseEvent(buffer, cb);
            }
            else
            {
                return NativeMethods.E_HANDLE;
            }
        }
 
        #endregion
 
        #region Static Create Method(s)
 
        /// <SecurityNote>
        ///     Critical: This code hooks up event sinks for unmanaged events
        ///               It also calls into a native method via MILCreateEventProxy
        /// </SecurityNote>
        [SecurityCritical]
        internal static SafeMILHandle CreateEventProxyWrapper(IInvokable invokable)
        {
            if (invokable == null)
            {
                throw new System.ArgumentNullException("invokable");
            }
 
            SafeMILHandle eventProxy = null;
 
            EventProxyWrapper epw = new EventProxyWrapper(invokable);
            EventProxyDescriptor epd = new EventProxyDescriptor();
 
            epd.pfnDispose = EventProxyStaticPtrs.pfnDispose;
            epd.pfnRaiseEvent = EventProxyStaticPtrs.pfnRaiseEvent;
 
            epd.m_handle = System.Runtime.InteropServices.GCHandle.Alloc(epw, System.Runtime.InteropServices.GCHandleType.Normal);
 
            HRESULT.Check(MILCreateEventProxy(ref epd, out eventProxy));
 
            return eventProxy;
        }
 
        #endregion
 
        /// <SecurityNote>
        ///     Critical: Elevates to unmanaged code permission
        /// </SecurityNote>
        [SuppressUnmanagedCodeSecurity]
        [SecurityCritical]
        [DllImport(DllImport.MilCore)]
        private extern static int /* HRESULT */ MILCreateEventProxy(ref EventProxyDescriptor pEPD, out SafeMILHandle ppEventProxy);
    }
    #endregion
}