hwndwrapper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Shared / MS / Win32 / hwndwrapper.cs / 1305600 / hwndwrapper.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, 2004
//
//  File: HwndWrapper.cs 
//-----------------------------------------------------------------------------
using System; 
using System.Security; 
using System.Security.Permissions;
using System.Collections.Generic; 
using System.Threading;
using System.Windows.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics; 
using MS.Internal;
using MS.Internal.Interop; 
using System.Globalization; // CultureInfo.InvariantCulture 

#if WINDOWS_BASE 
    using MS.Internal.WindowsBase;
#elif PRESENTATION_CORE
    using MS.Internal.PresentationCore;
#elif PRESENTATIONFRAMEWORK 
    using MS.Internal.PresentationFramework;
#elif DRT 
    using MS.Internal.Drt; 
#else
#error Attempt to use FriendAccessAllowedAttribute from an unknown assembly. 
using MS.Internal.YourAssemblyName;
#endif

// Disable pragma warnings to enable PREsharp pragmas 
#pragma warning disable 1634, 1691
 
namespace MS.Win32 
{
    [FriendAccessAllowed] 
    internal class HwndWrapper : DispatcherObject, IDisposable
    {
        ///
        ///    SecurityCritical: uses UnsafeNativeMethods RegisterWindowMessage 
        ///    SecurityTreatAsSafe: This is safe to call
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        static HwndWrapper()
        { 
            s_msgGCMemory = UnsafeNativeMethods.RegisterWindowMessage("HwndWrapper.GetGCMemMessage");
        }

        /// 
        ///    SecurityCritical: uses UnsafeNativeMethods GetModuleHandle
        ///                      elevates to call HwndSubclass Dispose 
        /// 
        [SecurityCritical]
        public HwndWrapper( 
            int classStyle,
            int style,
            int exStyle,
            int x, 
            int y,
            int width, 
            int height, 
            string name,
            IntPtr parent, 
            HwndWrapperHook[] hooks)
        {

            _ownerThreadID = new SecurityCriticalDataForSet(Thread.CurrentThread.ManagedThreadId); 

 
            // First, add the set of hooks.  This allows the hooks to receive the 
            // messages sent to the window very early in the process.
            if(hooks != null) 
            {
                for(int i = 0, iEnd = hooks.Length; i < iEnd; i++)
                {
                    if(null != hooks[i]) 
                        AddHook(hooks[i]);
                } 
            } 

 
            _wndProc = new SecurityCriticalData(new HwndWrapperHook(WndProc));

            // We create the HwndSubclass object so that we can use its
            // window proc directly.  We will not be "subclassing" the 
            // window we create.
            HwndSubclass hwndSubclass = new HwndSubclass(_wndProc.Value); 
 
            // Register a unique window class for this instance.
            NativeMethods.WNDCLASSEX_D wc_d = new NativeMethods.WNDCLASSEX_D(); 

            IntPtr hNullBrush = UnsafeNativeMethods.CriticalGetStockObject(NativeMethods.NULL_BRUSH);

            if (hNullBrush == IntPtr.Zero) 
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); 
            } 

            IntPtr hInstance = UnsafeNativeMethods.GetModuleHandle( null ); 

            // We need to keep the Delegate object alive through the call to CreateWindowEx().
            // Subclass.WndProc will install a better delegate (to the same function) when it
            // processes the first message. 
            // But this first delegate needs be held alive until then.
            NativeMethods.WndProc initialWndProc = new NativeMethods.WndProc(hwndSubclass.SubclassWndProc); 
 
            // The class name is a concat of AppName, ThreadName, and RandomNumber.
            // Register will fail if the string gets over 255 in length. 
            // So limit each part to a reasonable amount.
            string appName;
            if(null != AppDomain.CurrentDomain.FriendlyName && 128 <= AppDomain.CurrentDomain.FriendlyName.Length)
                appName = AppDomain.CurrentDomain.FriendlyName.Substring(0, 128); 
            else
                appName = AppDomain.CurrentDomain.FriendlyName; 
 
            string threadName;
            if(null != Thread.CurrentThread.Name && 64 <= Thread.CurrentThread.Name.Length) 
                threadName = Thread.CurrentThread.Name.Substring(0, 64);
            else
                threadName = Thread.CurrentThread.Name;
 
            // Create a suitable unique class name.
            _classAtom = 0; 
            string randomName = Guid.NewGuid().ToString(); 
            string className = String.Format(CultureInfo.InvariantCulture, "HwndWrapper[{0};{1};{2}]", appName, threadName, randomName);
 
            wc_d.cbSize        = Marshal.SizeOf(typeof(NativeMethods.WNDCLASSEX_D));
            wc_d.style         = classStyle;
            wc_d.lpfnWndProc   = initialWndProc;
            wc_d.cbClsExtra    = 0; 
            wc_d.cbWndExtra    = 0;
            wc_d.hInstance     = hInstance; 
            wc_d.hIcon         = IntPtr.Zero; 
            wc_d.hCursor       = IntPtr.Zero;
            wc_d.hbrBackground = hNullBrush; 
            wc_d.lpszMenuName  = "";
            wc_d.lpszClassName = className;
            wc_d.hIconSm       = IntPtr.Zero;
 
            // Register the unique class for this instance.
            // Note we use a GUID in the name so we are confident that 
            // the class name should be unique.  And RegisterClassEx won't 
            // fail (for that reason).
            _classAtom = UnsafeNativeMethods.RegisterClassEx(wc_d); 

            // call CreateWindow
            _isInCreateWindow = true;
            try { 
                _handle = new SecurityCriticalDataClass(UnsafeNativeMethods.CreateWindowEx(exStyle,
                                                         className, 
                                                         name, 
                                                         style,
                                                         x, 
                                                         y,
                                                         width,
                                                         height,
                                                         new HandleRef(null,parent), 
                                                         new HandleRef(null,IntPtr.Zero),
                                                         new HandleRef(null,IntPtr.Zero), 
                                                         null)); 
            }
            finally 
            {
                _isInCreateWindow = false;
                if(_handle == null || _handle.Value == IntPtr.Zero)
                { 
                    new UIPermission(UIPermissionWindow.AllWindows).Assert(); //BlessedAssert to call Dispose
                    try 
                    { 
                        // Because the HwndSubclass is pinned, but the HWND creation failed,
                        // we need to manually clean it up. 
                        hwndSubclass.Dispose();
                    }
                    finally
                    { 
                        CodeAccessPermission.RevertAssert();
                    } 
                } 
            }
            GC.KeepAlive(initialWndProc); 
        }


        ~HwndWrapper() 
        {
            Dispose(/*disposing = */ false, 
                    /*isHwndBeingDestroyed = */ false); 
        }
 
        public virtual void Dispose()
        {
            //             VerifyAccess();
 
            Dispose(/*disposing = */ true,
                    /*isHwndBeingDestroyed = */ false); 
            GC.SuppressFinalize(this); 
        }
 
        // internal Dispose(bool, bool)
        /// 
        ///  TreatAsSafe:  we demand when constructed, disposing considered safe
        ///  Critical: Elevates by calling an UnsafeNativeMethod 
        ///
        [SecurityTreatAsSafe, SecurityCritical] 
        private void Dispose(bool disposing, bool isHwndBeingDestroyed) 
        {
            if (_isDisposed) 
            {
                // protect against re-entrancy:  Calling DestroyWindow here will send
                // a WM_NCDESTROY -- WndProc may catch this and call Dispose again.
                return; 
            }
 
            if(disposing) 
            {
                // diposing == false means we're being called from the finalizer 
                // and can't follow any reference types that may themselves be
                // finalizable - thus don't call the Disposed callback.

                // Notify listeners that we are being disposed. 
                if(Disposed != null)
                { 
                    Disposed(this, EventArgs.Empty); 
                }
            } 

            // We are now considered disposed.
            _isDisposed = true;
 

            if (isHwndBeingDestroyed) 
            { 
                // The window is in the process of being destroyed.  We can't call UnregisterClass yet
                // so we'll ask the Dispatcher to do it later when the window is gone. 
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, (DispatcherOperationCallback)UnregisterClass, _classAtom);
            }
            else if (_handle != null && _handle.Value != IntPtr.Zero)
            { 
                // The window isn't in the process of being destroyed and it hasn't been destroyed yet
                // (we know this since we're listening for WM_NCDESTROY).  Since we're being disposed 
                // we destroy it now. 

                if(Thread.CurrentThread.ManagedThreadId == _ownerThreadID.Value) 
                {
                    // We are the owner thread, we can safely destroy the window and unregister
                    // the class
                    DestroyWindow(new DestroyWindowArgs(_handle, _classAtom)); 
                }
                else 
                { 
                    // Post a DispatcherOperation to ask the owner thread to destroy the window for us.
                    Dispatcher.BeginInvoke( 
                        DispatcherPriority.Normal,
                        (DispatcherOperationCallback)DestroyWindow,
                        new DestroyWindowArgs(_handle, _classAtom));
                } 
            }
 
 
            _classAtom = 0;
            _handle = null; 
        }

        /// 
        ///     Critical: Returns the handle of the window 
        /// 
        public IntPtr Handle { 
            [SecurityCritical] 
            get
            { 
                // This could be called from other threads, so snap the member.
                SecurityCriticalDataClass handle = _handle;

                if (handle != null) 
                {
                    return handle.Value; 
                } 
                else
                { 
                    return IntPtr.Zero;
                }
            }
        } 

        public event EventHandler Disposed; 
 
        /// 
        ///     Critical: Used to add hooks to the system which can be used to listen to window messages 
        /// 
        [SecurityCritical]
        public void AddHook(HwndWrapperHook hook)
        { 
            //VerifyAccess();
            if(_hooks == null) 
            { 
                _hooks = new SecurityCriticalDataClass(new WeakReferenceList());
            } 

            _hooks.Value.Insert(0, hook);
        }
 
        /// 
        ///     Critical: Used to add hooks to the system which can be used to listen to window messages 
        ///  
        [SecurityCritical]
        internal void AddHookLast(HwndWrapperHook hook) 
        {
            if(_hooks == null)
            {
                _hooks = new SecurityCriticalDataClass(new WeakReferenceList()); 
            }
            _hooks.Value.Add(hook); 
        } 

        ///  
        ///     Critical: This code acceses critical value hooks
        /// 
 	    [SecurityCritical]
        public void RemoveHook(HwndWrapperHook hook) 
        {
            //VerifyAccess(); 
            if (_hooks != null) 
            {
                _hooks.Value.Remove(hook); 
            }
        }

        ///  
        ///     Critical: Calls the hooks and can be used to send spurious input to the system
        ///  
        [SecurityCritical] 
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        { 
            // The default result for messages we handle is 0.
            IntPtr result = IntPtr.Zero;
            WindowMessage message = (WindowMessage)msg;
 
            // Call all of the hooks
            if(_hooks != null) 
            { 
                foreach(HwndWrapperHook hook in _hooks.Value)
                { 
                    result = hook(hwnd, msg, wParam, lParam, ref handled);

                    CheckForCreateWindowFailure(result, handled);
 
                    if(handled)
                    { 
                        break; 
                    }
                } 
            }

            if (message == WindowMessage.WM_NCDESTROY)
            { 
                Dispose(/*disposing = */ true,
                        /*isHwndBeingDestroyed = */ true); 
                GC.SuppressFinalize(this); 

                // We want the default window proc to process this message as 
                // well, so we mark it as unhandled.
                handled = false;
            }
            else if (message == s_msgGCMemory) 
            {
                // This is a special message we respond to by forcing a GC Collect.  This 
                // is used by test apps and such. 
                IntPtr lHeap = (IntPtr)GC.GetTotalMemory((wParam == new IntPtr(1) )? true : false);
                result =  lHeap; 
                handled = true;
            }

            CheckForCreateWindowFailure(result, true); 

            // return our result 
            return result; 
        }
 
        private void CheckForCreateWindowFailure( IntPtr result, bool handled )
        {
            if( ! _isInCreateWindow )
                return; 

            if( IntPtr.Zero != result ) 
            { 
                System.Diagnostics.Debug.WriteLine("Non-zero WndProc result=" + result);
                if( handled ) 
                {
                    if( System.Diagnostics.Debugger.IsAttached )
                        System.Diagnostics.Debugger.Break();
                    else 
                        throw new InvalidOperationException();
                } 
            } 
        }
 

        /// 
        /// Destroys the window with the given handle and class atom and unregisters its window class
        ///  
        /// A DestrowWindowParams instance
        ///  
        ///     Critical: Destroys a Window and calls a critical method 
        ///     Partial Trust scenarios can execute this method.  It takes an object so that it
        ///     can be called by a DispatcherOperationCallback and avoid a DynamicInvoke, which 
        ///     requires ReflectionPermission.
        /// 
        [SecurityCritical]
        internal static object DestroyWindow(object args) 
        {
            SecurityCriticalDataClass handle = ((DestroyWindowArgs)args).Handle; 
            ushort classAtom = ((DestroyWindowArgs)args).ClassAtom; 

            Invariant.Assert(handle != null && handle.Value != IntPtr.Zero, 
               "Attempting to destroy an invalid hwnd");

            UnsafeNativeMethods.DestroyWindow(new HandleRef(null, handle.Value));
 
            UnregisterClass((object)classAtom);
 
            return null; 
        }
 
        /// 
        /// Unregisters the window class represented by classAtom
        /// 
        /// A ushort representing the class atom 
        /// 
        ///     Critical: Unregisters the window class and calls a critical method 
        ///     Partial Trust scenarios can execute this method.  It takes an object so that it 
        ///     can be called by a DispatcherOperationCallback and avoid a DynamicInvoke, which
        ///     requires ReflectionPermission. 
        /// 
        [SecurityCritical]
        internal static object UnregisterClass(object arg)
        { 
            ushort classAtom = (ushort)arg;
 
            if (classAtom != 0) 
            {
                IntPtr hInstance = UnsafeNativeMethods.GetModuleHandle(null); 
                UnsafeNativeMethods.UnregisterClass(
                                new IntPtr(classAtom), //* this function is defined as taking a type lpClassName - but this can be an atom. 2 Low Bytes are the atom*/
                                hInstance);
            } 

            return null; 
        } 

        // This is used only so that DestroyWindow can take a single object parameter 
        // in order for it to be called by a DispatcherOperationCallback
        internal class DestroyWindowArgs
        {
            public DestroyWindowArgs(SecurityCriticalDataClass handle, ushort classAtom) 
            {
                _handle = handle; 
                _classAtom = classAtom; 
            }
 
            public SecurityCriticalDataClass Handle
            {
                get
                { 
                    return _handle;
                } 
            } 

            public ushort ClassAtom 
            {
                get
                {
                    return _classAtom; 
                }
            } 
 
            private SecurityCriticalDataClass _handle;
            private ushort _classAtom; 
        }


        private SecurityCriticalDataClass _handle; 
        private UInt16 _classAtom;
        private SecurityCriticalDataClass _hooks; 
        private SecurityCriticalDataForSet _ownerThreadID; 
        private SecurityCriticalData _wndProc;
        private bool _isDisposed; 

        private bool _isInCreateWindow = false;     // debugging variable (temporary)

        // Message to cause a dispose.  We need this to ensure we destroy the window on the right thread. 
        /// 
        ///     Critical: This is initialized under an elevation 
        ///  
        [SecurityCritical]
        private static WindowMessage s_msgGCMemory; 
    } // class RawWindow
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK