NativeWindow.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 / ndp / fx / src / WinForms / Managed / System / WinForms / NativeWindow.cs / 1305376 / NativeWindow.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 */ 
namespace System.Windows.Forms {
    using System.Threading; 
    using System.Configuration.Assemblies;
    using System.Runtime.Remoting;
    using System.Runtime.InteropServices;
    using System.Runtime.ConstrainedExecution; 
    using System.Runtime.CompilerServices;
    using System.Reflection; 
    using System.Security; 
    using System.Security.Permissions;
    using System.Diagnostics; 
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Drawing; 
    using System.Windows.Forms;
    using System.ComponentModel; 
    using Microsoft.Win32; 
    using System.Text;
    using System.Globalization; 
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime.Versioning;

    ///  
    /// 
    ///     
    ///       Provides a low-level encapsulation of a window handle 
    ///       and a window procedure. The class automatically manages window class creation and registration.
    ///     
    /// 
    [
        SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode),
        SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode) 
    ]
    public class NativeWindow : MarshalByRefObject, IWin32Window { 
#if DEBUG 
        private static readonly BooleanSwitch AlwaysUseNormalWndProc = new BooleanSwitch("AlwaysUseNormalWndProc", "Skips checking for the debugger when choosing the debuggable WndProc handler");
        private static readonly TraceSwitch WndProcChoice = new TraceSwitch("WndProcChoice", "Info about choice of WndProc"); 
#else
        private static readonly TraceSwitch WndProcChoice;
#endif
 
        /**
         * Table of prime numbers to use as hash table sizes. Each entry is the 
         * smallest prime number larger than twice the previous entry. 
         */
        private readonly static int[] primes = { 
            11,17,23,29,37,47,59,71,89,107,131,163,197,239,293,353,431,521,631,761,919,
            1103,1327,1597,1931,2333,2801,3371,4049,4861,5839,7013,8419,10103,12143,14591,
            17519,21023,25229,30293,36353,43627,52361,62851,75431,90523, 108631, 130363,
            156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 
            968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287,
            4999559, 5999471, 7199369 
        }; 

        const int InitializedFlags = 0x01; 
        const int DebuggerPresent = 0x02;
        const int UseDebuggableWndProc = 0x04;
        const int LoadConfigSettings = 0x08;
        const int AssemblyIsDebuggable = 0x10; 

        // do we have any active HWNDs? 
        // 
        [ThreadStatic]
        static bool anyHandleCreated; 
        static bool anyHandleCreatedInApp;

        const float hashLoadFactor = .72F;
 
        private static int handleCount;
        private static int hashLoadSize; 
        private static HandleBucket[] hashBuckets; 
        private static IntPtr userDefWindowProc;
        [ThreadStatic] 
        private static byte wndProcFlags = 0;
        [ThreadStatic]
        private static byte userSetProcFlags = 0;
        private static byte userSetProcFlagsForApp; 

        //nned to Store Table of Ids and Handles 
        private static short globalID = 1; 
        private static Dictionary hashForIdHandle;
        private static Dictionary hashForHandleId; 
        private static object internalSyncObject = new object();
        private static object createWindowSyncObject = new object();

#if DEBUG 
        AppDomain               handleCreatedIn = null;
        string                  subclassStatus = "None"; 
#endif 
        IntPtr handle;
        NativeMethods.WndProc windowProc; 
        IntPtr windowProcPtr;
        IntPtr defWindowProc;
        bool suppressedGC;
        bool ownHandle; 
        NativeWindow previousWindow; // doubly linked list of subclasses.
        NativeWindow nextWindow; 
        WeakReference weakThisPtr; 

        static NativeWindow() { 
            EventHandler shutdownHandler = new EventHandler(OnShutdown);
            AppDomain.CurrentDomain.ProcessExit += shutdownHandler;
            AppDomain.CurrentDomain.DomainUnload += shutdownHandler;
 
            // Initialize our static hash of handles.  I have chosen
            // a prime bucket based on a typical number of window handles 
            // needed to start up a decent sized app. 
            int hashSize = primes[4];
            hashBuckets = new HandleBucket[hashSize]; 

            hashLoadSize = (int)(hashLoadFactor * hashSize);
            if (hashLoadSize >= hashSize) {
                hashLoadSize = hashSize - 1; 
            }
 
            //Intilialize the Hashtable for Id... 
            hashForIdHandle = new Dictionary();
            hashForHandleId = new Dictionary(); 
        }

        public NativeWindow() {
            weakThisPtr = new WeakReference(this); 
        }
 
        ///  
        /// 
        ///     Override's the base object's finalize method. 
        /// 
        ~NativeWindow() {
            ForceExitMessageLoop();
        } 

        ///  
        /// This was factored into another function so the finalizer in control that releases the window 
        /// can perform the exact same code without further changes.  If you make changes to the finalizer,
        /// change this method -- try not to change NativeWindow's finalizer. 
        /// 
        internal void ForceExitMessageLoop() {
            IntPtr h;
            bool ownedHandle; 

            // 
            lock (this) { 
                h = handle;
                ownedHandle = ownHandle; 
            }

            if (handle != IntPtr.Zero) {
                //now, before we set handle to zero and finish the finalizer, let's send 
                // a WM_NULL to the window.  Why?  Because if the main ui thread is INSIDE
                // the wndproc for this control during our unsubclass, then we could AV 
                // when control finally reaches us. 
                if (UnsafeNativeMethods.IsWindow(new HandleRef(null, handle))) {
                    int lpdwProcessId;  //unused 
                    int id = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(null, handle), out lpdwProcessId);
                    Application.ThreadContext ctx = Application.ThreadContext.FromId(id);
                    IntPtr threadHandle = (ctx == null ? IntPtr.Zero : ctx.GetHandle());
 
                    if (threadHandle != IntPtr.Zero) {
                        int exitCode = 0; 
                        SafeNativeMethods.GetExitCodeThread(new HandleRef(null, threadHandle), out exitCode); 
                        if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && exitCode == NativeMethods.STATUS_PENDING) {
                            IntPtr result; 
                            if (UnsafeNativeMethods.SendMessageTimeout(new HandleRef(null, handle),
                                NativeMethods.WM_UIUNSUBCLASS, IntPtr.Zero, IntPtr.Zero,
                                UnsafeNativeMethods.SMTO_ABORTIFHUNG, 100, out result) == IntPtr.Zero) {
 
                                //Debug.Fail("unable to ping HWND:" + handle.ToString() + " during finalization");
                            } 
                        } 
                    }
                } 
                if (handle != IntPtr.Zero) {
                    //if the dest thread is gone, it should be safe to unsubclass here.
                    ReleaseHandle(true);
                } 
            }
 
            if (h != IntPtr.Zero && ownedHandle) { 
                // If we owned the handle, post a
                // WM_CLOSE to get rid of it. 
                //
                UnsafeNativeMethods.PostMessage(new HandleRef(this, h), NativeMethods.WM_CLOSE, 0, 0);
            }
        } 

        ///  
        ///     Indicates whether a window handle was created & is being tracked. 
        /// 
        internal static bool AnyHandleCreated { 
            get {
                return anyHandleCreated;
            }
        } 

        ///  
        ///  
        ///    
        ///       Gets the handle for this window. 
        ///    
        /// 
        public IntPtr Handle {
            [System.Runtime.TargetedPatchingOptOutAttribute("Performance critical to inline across NGen image boundaries")] 
            get {
#if DEBUG 
                Debug.Assert(handle == IntPtr.Zero || (handleCreatedIn != null && handleCreatedIn == AppDomain.CurrentDomain), 
                             "Attempt to access a handle created in a different domain");
 
                // We cannot assert this here.  Consider the case where someone has created an HWND on a thread but
                // never started a message pump.  When the thread is cleaned up by the OS USER will destroy all window
                // handles. This happens long before we finalize, so when finalization does come along and we
                // try to check to see if we should PostMessage(WM_CLOSE, this assert would fire. 
                //
                // Debug.Assert(handle == IntPtr.Zero || UnsafeNativeMethods.IsWindow(new HandleRef(this, handle)), 
                //             "Attempt to access a non-valid handle "); 
#endif
                return handle; 
            }
        }

        ///  
        ///     This returns the previous NativeWindow in the chain of subclasses.
        ///     Generally it returns null, but if someone has subclassed a control 
        ///     through the use of a NativeWindow class, this will return the 
        ///     previous NativeWindow subclass.
        /// 
        ///     This should be public, but it is way too late for that.
        /// 
        internal NativeWindow PreviousWindow {
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 
            get {
                return previousWindow; 
            } 
        }
 
        /// 
        ///     Helper method that returns a static value for the
        ///     unmanaged DefWindowProc call.
        ///  
        internal static IntPtr UserDefindowProc {
            get { 
                return userDefWindowProc; 
            }
        } 

        private static int WndProcFlags {
            get {
                // upcast for easy bit masking... 
                //
                int intWndProcFlags = wndProcFlags; 
 
                // Check to see if a debugger is installed.  If there is, then use
                // DebuggableCallback instead; this callback has no try/catch around it 
                // so exceptions go to the debugger.
                //
                if (intWndProcFlags == 0) {
                    Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Init wndProcFlags"); 
                    Debug.Indent();
 
                    if (userSetProcFlags != 0) { 
                        intWndProcFlags = userSetProcFlags;
                    } 
                    else if (userSetProcFlagsForApp != 0) {
                        intWndProcFlags = userSetProcFlagsForApp;
                    }
                    else if (!Application.CustomThreadExceptionHandlerAttached) { 
                        if (Debugger.IsAttached) {
                            Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debugger is attached, using debuggable WndProc"); 
                            intWndProcFlags |= UseDebuggableWndProc; 
                        }
                        else { 
                            intWndProcFlags = AdjustWndProcFlagsFromRegistry(intWndProcFlags);
                            Debug.WriteLineIf(WndProcChoice.TraceVerbose, "After reg check 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture));
                            if ((intWndProcFlags & DebuggerPresent) != 0) {
                                Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debugger present"); 

                                intWndProcFlags = AdjustWndProcFlagsFromMetadata(intWndProcFlags); 
 
                                if ((intWndProcFlags & AssemblyIsDebuggable) != 0) {
                                    if ((intWndProcFlags & LoadConfigSettings) != 0) { 
                                        intWndProcFlags = AdjustWndProcFlagsFromConfig(intWndProcFlags);
                                        Debug.WriteLineIf(WndProcChoice.TraceVerbose, "After config check 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture));
                                    }
                                    else { 
                                        intWndProcFlags |= UseDebuggableWndProc;
                                    } 
                                } 
                            }
 
                            Debug.WriteLineIf(WndProcChoice.TraceVerbose, "After config & debugger check 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture));
                        }
                    }
 
#if DEBUG
                    if (AlwaysUseNormalWndProc.Enabled) { 
                        Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Stripping debuggablewndproc due to AlwaysUseNormalWndProc switch"); 
                        intWndProcFlags &= ~UseDebuggableWndProc;
                    } 
#endif
                    intWndProcFlags |= InitializedFlags;
                    Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Final 0x" + intWndProcFlags.ToString("X", CultureInfo.InvariantCulture));
                    wndProcFlags = (byte)intWndProcFlags; 
                    Debug.Unindent();
                } 
 
                return intWndProcFlags;
            } 
        }

        internal static bool WndProcShouldBeDebuggable {
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 
            get {
                return (WndProcFlags & UseDebuggableWndProc) != 0; 
            } 
        }
 
        /// 
        ///     Inserts an entry into this hashtable.
        /// 
        private static void AddWindowToTable(IntPtr handle, NativeWindow window) { 

            Debug.Assert(handle != IntPtr.Zero, "Should never insert a zero handle into the hash"); 
 
            lock (internalSyncObject) {
 
                if (handleCount >= hashLoadSize) {
                    ExpandTable();
                }
 
                // set a flag that this thread is tracking an HWND
                // 
                anyHandleCreated = true; 
                // ...same for the application
                anyHandleCreatedInApp = true; 

                uint seed;
                uint incr;
                // Assume we only have one thread writing concurrently.  Modify 
                // buckets to contain new data, as long as we insert in the right order.
                uint hashcode = InitHash(handle, hashBuckets.Length, out seed, out incr); 
 
                int ntry = 0;
                int emptySlotNumber = -1; // We use the empty slot number to cache the first empty slot. We chose to reuse slots 
                // create by remove that have the collision bit set over using up new slots.

                GCHandle root = GCHandle.Alloc(window, GCHandleType.Weak);
 
                do {
                    int bucketNumber = (int)(seed % (uint)hashBuckets.Length); 
 
                    if (emptySlotNumber == -1 && (hashBuckets[bucketNumber].handle == new IntPtr(-1)) && (hashBuckets[bucketNumber].hash_coll < 0))
                        emptySlotNumber = bucketNumber; 

                    //We need to check if the collision bit is set because we have the possibility where the first
                    //item in the hash-chain has been deleted.
                    if ((hashBuckets[bucketNumber].handle == IntPtr.Zero) || 
                        (hashBuckets[bucketNumber].handle == new IntPtr(-1) && ((hashBuckets[bucketNumber].hash_coll & unchecked(0x80000000)) == 0))) {
 
                        if (emptySlotNumber != -1) { // Reuse slot 
                            bucketNumber = emptySlotNumber;
                        } 

                        // Always set the hash_coll last because there may be readers
                        // reading the table right now on other threads.
                        hashBuckets[bucketNumber].window = root; 
                        hashBuckets[bucketNumber].handle = handle;
#if DEBUG 
                        hashBuckets[bucketNumber].owner = window.ToString(); 
#endif
                        hashBuckets[bucketNumber].hash_coll |= (int)hashcode; 
                        handleCount++;
                        return;
                    }
 
                    // If there is an existing window in this slot, reuse it.  Be sure to hook up the previous and next
                    // window pointers so we can get back to the right window. 
                    // 
                    if (((hashBuckets[bucketNumber].hash_coll & 0x7FFFFFFF) == hashcode) && handle == hashBuckets[bucketNumber].handle) {
                        GCHandle prevWindow = hashBuckets[bucketNumber].window; 
                        if (prevWindow.IsAllocated) {
                            window.previousWindow = ((NativeWindow)prevWindow.Target);
                            Debug.Assert(window.previousWindow.nextWindow == null, "Last window in chain should have null next ptr");
                            window.previousWindow.nextWindow = window; 
                            prevWindow.Free();
                        } 
                        hashBuckets[bucketNumber].window = root; 
#if DEBUG
                        string ownerString = string.Empty; 
                        NativeWindow w = window;
                        while(w != null) {
                            ownerString += ("->" + w.ToString());
                            w = w.previousWindow; 
                        }
                        hashBuckets[bucketNumber].owner = ownerString; 
#endif 
                        return;
                    } 

                    if (emptySlotNumber == -1) {// We don't need to set the collision bit here since we already have an empty slot
                        hashBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
                    } 

                    seed += incr; 
 
                } while (++ntry < hashBuckets.Length);
 
                if (emptySlotNumber != -1) {
                    // Always set the hash_coll last because there may be readers
                    // reading the table right now on other threads.
                    hashBuckets[emptySlotNumber].window = root; 
                    hashBuckets[emptySlotNumber].handle = handle;
#if DEBUG 
                        hashBuckets[emptySlotNumber].owner = window.ToString(); 
#endif
                    hashBuckets[emptySlotNumber].hash_coll |= (int)hashcode; 
                    handleCount++;
                    return;
                }
            } 

            // If you see this assert, make sure load factor & count are reasonable. 
            // Then verify that our double hash function (h2, described at top of file) 
            // meets the requirements described above. You should never see this assert.
            Debug.Fail("native window hash table insert failed!  Load factor too high, or our double hashing function is incorrect."); 
        }

        /// 
        ///     Inserts an entry into this ID hashtable. 
        /// 
        internal static void AddWindowToIDTable(object wrapper, IntPtr handle) { 
            NativeWindow.hashForIdHandle[NativeWindow.globalID] = handle; 
            NativeWindow.hashForHandleId[handle] = NativeWindow.globalID;
            UnsafeNativeMethods.SetWindowLong(new HandleRef(wrapper, handle), NativeMethods.GWL_ID, new HandleRef(wrapper, (IntPtr)globalID)); 
            globalID++;

        }
 
        // disable inlining optimization - VSW 454165
        // This method introduces a dependency on System.Configuration.dll 
        // due to its usage of the type WindowsFormsSection 
        //
        [MethodImplAttribute(MethodImplOptions.NoInlining)] 
        private static int AdjustWndProcFlagsFromConfig(int wndProcFlags) {
            if (WindowsFormsSection.GetSection().JitDebugging) {
                wndProcFlags |= UseDebuggableWndProc;
            } 
            return wndProcFlags;
        } 
 
        private static int AdjustWndProcFlagsFromRegistry(int wndProcFlags) {
            // This is the enum used to define the meaning of the debug flag... 
            //

            /*
            // 
            // We split the value of DbgJITDebugLaunchSetting between the value for whether or not to ask the user and between a
            // mask of places to ask. The places to ask are specified in the UnhandledExceptionLocation enum in excep.h. 
            // 
            enum DebuggerLaunchSetting
            { 
                DLS_ASK_USER          = 0x00000000,
                DLS_TERMINATE_APP     = 0x00000001,
                DLS_ATTACH_DEBUGGER   = 0x00000002,
                DLS_QUESTION_MASK     = 0x0000000F, 
                DLS_ASK_WHEN_SERVICE  = 0x00000010,
                DLS_MODIFIER_MASK     = 0x000000F0, 
                DLS_LOCATION_MASK     = 0xFFFFFF00, 
                DLS_LOCATION_SHIFT    = 8 // Shift right 8 bits to get a UnhandledExceptionLocation value from the location part.
            }; 
            */

            new RegistryPermission(PermissionState.Unrestricted).Assert();
            try { 

                Debug.Assert(wndProcFlags == 0x00, "Re-entrancy into IsDebuggerInstalled()"); 
 
                RegistryKey debugKey = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\.NETFramework");
                if (debugKey == null) { 
                    Debug.WriteLineIf(WndProcChoice.TraceVerbose, ".NETFramework key not found");
                    return wndProcFlags;
                }
                try { 
                    object value = debugKey.GetValue("DbgJITDebugLaunchSetting");
                    if (value != null) { 
                        Debug.WriteLineIf(WndProcChoice.TraceVerbose, "DbgJITDebugLaunchSetting value found, debugger is installed"); 
                        int dbgJit = 0;
                        try { 
                            dbgJit = (int)value;
                        }
                        catch (InvalidCastException) {
                            // If the value isn't a DWORD, then we will 
                            // continue to use the non-debuggable wndproc
                            // 
                            dbgJit = 1; 
                        }
 
                        // From the enum above, 0x01 == "Terminate App"... for
                        // anything else, we should flag that the debugger
                        // will catch unhandled exceptions
                        // 
                        if (dbgJit != 1) {
                            wndProcFlags |= DebuggerPresent; 
                            wndProcFlags |= LoadConfigSettings; 
                        }
                    } 
                    else if (debugKey.GetValue("DbgManagedDebugger") != null) {
                        //if there is a debugger installed, check the config files to decide
                        //whether to allow JIT debugging.
                        wndProcFlags |= DebuggerPresent; 
                        wndProcFlags |= LoadConfigSettings;
                    } 
                } 
                finally {
                    debugKey.Close(); 
                }
            }
            finally {
                System.Security.CodeAccessPermission.RevertAssert(); 
            }
 
            return wndProcFlags; 
        }
 
        static int AdjustWndProcFlagsFromMetadata(int wndProcFlags) {
            if ((wndProcFlags & DebuggerPresent) != 0) {
                Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debugger present, checking for debuggable entry assembly");
                Assembly entry = Assembly.GetEntryAssembly(); 
                if (entry != null) {
                    if (Attribute.IsDefined(entry, typeof(DebuggableAttribute))) { 
                        Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Debuggable attribute on assembly"); 
                        Attribute[] attr = Attribute.GetCustomAttributes(entry, typeof(DebuggableAttribute));
                        if (attr.Length > 0) { 
                            DebuggableAttribute dbg = (DebuggableAttribute)attr[0];
                            if (dbg.IsJITTrackingEnabled) {
                                // Only if the assembly is really setup for debugging
                                // does it really make sense to enable the jitDebugging 
                                //
                                Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Entry assembly is debuggable"); 
                                wndProcFlags |= AssemblyIsDebuggable; 
                            }
                        } 
                    }
                }
            }
            return wndProcFlags; 
        }
 
        ///  
        /// 
        ///     
        ///       Assigns a handle to this
        ///       window.
        ///    
        ///  
        public void AssignHandle(IntPtr handle) {
            AssignHandle(handle, true); 
        } 

        // 



 
        internal void AssignHandle(IntPtr handle, bool assignUniqueID) {
            // 
            lock (this) { 
                CheckReleased();
                Debug.Assert(handle != IntPtr.Zero, "handle is 0"); 
#if DEBUG
                handleCreatedIn = AppDomain.CurrentDomain;
#endif
                this.handle = handle; 

                if (userDefWindowProc == IntPtr.Zero) { 
                    string defproc = (Marshal.SystemDefaultCharSize == 1 ? "DefWindowProcA" : "DefWindowProcW"); 

                    userDefWindowProc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null, UnsafeNativeMethods.GetModuleHandle("user32.dll")), defproc); 
                    if (userDefWindowProc == IntPtr.Zero) {
                        throw new Win32Exception();
                    }
                } 

                defWindowProc = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); 
                Debug.Assert(defWindowProc != IntPtr.Zero, "defWindowProc is 0"); 

 
                if (WndProcShouldBeDebuggable) {
                    Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Using debuggable wndproc");
                    windowProc = new NativeMethods.WndProc(this.DebuggableCallback);
                } 
                else {
                    Debug.WriteLineIf(WndProcChoice.TraceVerbose, "Using normal wndproc"); 
                    windowProc = new NativeMethods.WndProc(this.Callback); 
                }
 
                AddWindowToTable(handle, this);

                UnsafeNativeMethods.SetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC, windowProc);
                windowProcPtr = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC); 
                Debug.Assert(defWindowProc != windowProcPtr, "Uh oh! Subclassed ourselves!!!");
                if (assignUniqueID && 
                    (unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_STYLE))) & NativeMethods.WS_CHILD) != 0 && 
                     unchecked((int)((long)UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_ID))) == 0) {
                    UnsafeNativeMethods.SetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_ID, new HandleRef(this, handle)); 
                }


                if (suppressedGC) { 
                    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
                    try { 
                        GC.ReRegisterForFinalize(this); 
                    }
                    finally { 
                        CodeAccessPermission.RevertAssert();
                    }
                    suppressedGC = false;
                } 

                OnHandleChange(); 
            } 
        }
 
        /// 
        /// 
        ///     Window message callback method. Control arrives here when a window
        ///     message is sent to this Window. This method packages the window message 
        ///     in a Message object and invokes the wndProc() method. A WM_NCDESTROY
        ///     message automatically causes the releaseHandle() method to be called. 
        ///  
        /// 
        private IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { 

            // Note: if you change this code be sure to change the
            // corresponding code in DebuggableCallback below!
 
            Message m = Message.Create(hWnd, msg, wparam, lparam);
 
            try { 
                if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
                    WndProc(ref m); 
                }
                else {
                    DefWndProc(ref m);
                } 
            }
            catch (Exception e) { 
                OnThreadException(e); 
            }
            finally { 
                if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
                if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
            }
 
            return m.Result;
        } 
 
        /// 
        ///  
        ///     Raises an exception if the window handle is not zero.
        /// 
        /// 
        private void CheckReleased() { 
            if (handle != IntPtr.Zero) {
                throw new InvalidOperationException(SR.GetString(SR.HandleAlreadyExists)); 
            } 
        }
 
        /// 
        /// 
        ///    
        ///       Creates a window handle for this 
        ///       window.
        ///     
        ///  
        [
            // The call to UnsafeNativeMethods.CreateWindowEx is actually an interop call... 
            System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke")
        ]
        public virtual void CreateHandle(CreateParams cp) {
 
            Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "CreateAnyWindow Demanded");
            IntSecurity.CreateAnyWindow.Demand(); 
 
            if ((cp.Style & NativeMethods.WS_CHILD) != NativeMethods.WS_CHILD
                || cp.Parent == IntPtr.Zero) { 

                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "TopLevelWindow Demanded");
                IntSecurity.TopLevelWindow.Demand();
            } 

            // 
            lock (this) { 
                CheckReleased();
                WindowClass windowClass = WindowClass.Create(cp.ClassName, cp.ClassStyle); 
                lock (createWindowSyncObject) {

                    // VSWhidbey 528778
                    // Why are we checking the handle again after calling CheckReleased()?  It turns 
                    // out the CLR will sometimes pump messages while we're waiting on the lock.  If
                    // a message comes through (say a WM_ACTIVATE for the parent) which causes the 
                    // handle to be created, we can try to create the handle twice for the same 
                    // NativeWindow object. See bug for more details.
 
                    if (this.handle != IntPtr.Zero) {
                        return;
                    }
                    windowClass.targetWindow = this; 
                    IntPtr modHandle = UnsafeNativeMethods.GetModuleHandle(null);
 
                    IntPtr createResult = IntPtr.Zero; 
                    int lastWin32Error = 0;
 
                    // Win98 apparently doesn't believe in returning E_OUTOFMEMORY.  They'd much
                    // rather just AV.  So we catch this and then we re-throw an out of memory error.
                    //
                    try { 

                        //(bug 109840) 
                        //CreateWindowEx() is throwing because we're passing the WindowText arg with a string of length  > 32767. 
                        //It looks like the Windows call (CreateWindowEx) will only work
                        //for string lengths no greater than the max length of a 16 bit int (32767). 

                        //We need to check the length of the string we're passing into CreateWindowEx().
                        //If it exceeds the max, we should take the substring....
 
                        if (cp.Caption != null && cp.Caption.Length > Int16.MaxValue) {
                            cp.Caption = cp.Caption.Substring(0, Int16.MaxValue); 
                        } 

                        createResult = UnsafeNativeMethods.CreateWindowEx(cp.ExStyle, windowClass.windowClassName, 
                                                                          cp.Caption, cp.Style, cp.X, cp.Y, cp.Width, cp.Height, new HandleRef(cp, cp.Parent), NativeMethods.NullHandleRef,
                                                                          new HandleRef(null, modHandle), cp.Param);

                        lastWin32Error = Marshal.GetLastWin32Error(); 
                    }
                    catch (NullReferenceException e) { 
                        throw new OutOfMemoryException(SR.GetString(SR.ErrorCreatingHandle), e); 
                    }
 
                    windowClass.targetWindow = null;

                    Debug.WriteLineIf(CoreSwitches.PerfTrack.Enabled, "Handle created of type '" + cp.ClassName + "' with caption '" + cp.Caption + "' from NativeWindow of type '" + GetType().FullName + "'");
 
                    if (createResult == IntPtr.Zero) {
                        throw new Win32Exception(lastWin32Error, SR.GetString(SR.ErrorCreatingHandle)); 
                    } 
                    ownHandle = true;
                    System.Internal.HandleCollector.Add(createResult, NativeMethods.CommonHandles.Window); 
                }
            }
        }
 
        /// 
        ///  
        ///     Window message callback method. Control arrives here when a window 
        ///     message is sent to this Window. This method packages the window message
        ///     in a Message object and invokes the wndProc() method. A WM_NCDESTROY 
        ///     message automatically causes the releaseHandle() method to be called.
        /// 
        /// 
        private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { 

            // Note: if you change this code be sure to change the 
            // corresponding code in Callback above! 

            Message m = Message.Create(hWnd, msg, wparam, lparam); 

            try {
                if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
                    WndProc(ref m); 
                }
                else { 
                    DefWndProc(ref m); 
                }
            } 
            finally {
                if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
                if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
            } 

            return m.Result; 
        } 

        ///  
        /// 
        ///     Invokes the default window procedure associated with this Window. It is
        ///     an error to call this method when the Handle property is zero.
        ///  
        public void DefWndProc(ref Message m) {
            if (previousWindow == null) { 
                if (defWindowProc == IntPtr.Zero) { 

#if DEBUG 
                    Debug.Fail("Can't find a default window procedure for message " + m.ToString() + " on class " + GetType().Name + " subclass status: " + subclassStatus);
#endif

                    // At this point, there isn't much we can do.  There's a 
                    // small chance the following line will allow the rest of
                    // the program to run, but don't get your hopes up. 
                    m.Result = UnsafeNativeMethods.DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam); 
                    return;
                } 
                m.Result = UnsafeNativeMethods.CallWindowProc(defWindowProc, m.HWnd, m.Msg, m.WParam, m.LParam);
            }
            else {
                Debug.Assert(previousWindow != this, "Looping in our linked list"); 
                m.Result = previousWindow.Callback(m.HWnd, m.Msg, m.WParam, m.LParam);
            } 
        } 

        ///  
        /// 
        ///    
        ///       Destroys the
        ///       handle associated with this window. 
        ///    
        ///  
        public virtual void DestroyHandle() { 
            //
            lock (this) { 
                if (handle != IntPtr.Zero) {
                    if (!UnsafeNativeMethods.DestroyWindow(new HandleRef(this, handle))) {
                        UnSubclass();
                        //then post a close and let it do whatever it needs to do on its own. 
                        UnsafeNativeMethods.PostMessage(new HandleRef(this, handle), NativeMethods.WM_CLOSE, 0, 0);
                    } 
                    handle = IntPtr.Zero; 
                    ownHandle = false;
                } 

                // Now that we have disposed, there is no need to finalize us any more.  So
                // Mark to the garbage collector that we no longer need finalization.
                // 
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
                try { 
                    GC.SuppressFinalize(this); 
                }
                finally { 
                    CodeAccessPermission.RevertAssert();
                }
                suppressedGC = true;
            } 
        }
 
        ///  
        ///     Increases the bucket count of this hashtable. This method is called from
        ///     the Insert method when the actual load factor of the hashtable reaches 
        ///     the upper limit specified when the hashtable was constructed. The number
        ///     of buckets in the hashtable is increased to the smallest prime number
        ///     that is larger than twice the current number of buckets, and the entries
        ///     in the hashtable are redistributed into the new buckets using the cached 
        ///     hashcodes.
        ///  
        private static void ExpandTable() { 
            // Allocate new Array
            int oldhashsize = hashBuckets.Length; 

            int hashsize = GetPrime(1 + oldhashsize * 2);

            // Don't replace any internal state until we've finished adding to the 
            // new bucket[].  This serves two purposes: 1) Allow concurrent readers
            // to see valid hashtable contents at all times and 2) Protect against 
            // an OutOfMemoryException while allocating this new bucket[]. 
            HandleBucket[] newBuckets = new HandleBucket[hashsize];
 
            // rehash table into new buckets
            int nb;
            for (nb = 0; nb < oldhashsize; nb++) {
                HandleBucket oldb = hashBuckets[nb]; 
                if ((oldb.handle != IntPtr.Zero) && (oldb.handle != new IntPtr(-1))) {
 
                    // Now re-fit this entry into the table 
                    //
                    uint seed = (uint)oldb.hash_coll & 0x7FFFFFFF; 
                    uint incr = (uint)(1 + (((seed >> 5) + 1) % ((uint)newBuckets.Length - 1)));

                    do {
                        int bucketNumber = (int)(seed % (uint)newBuckets.Length); 

                        if ((newBuckets[bucketNumber].handle == IntPtr.Zero) || (newBuckets[bucketNumber].handle == new IntPtr(-1))) { 
                            newBuckets[bucketNumber].window = oldb.window; 
                            newBuckets[bucketNumber].handle = oldb.handle;
                            newBuckets[bucketNumber].hash_coll |= oldb.hash_coll & 0x7FFFFFFF; 
                            break;
                        }
                        newBuckets[bucketNumber].hash_coll |= unchecked((int)0x80000000);
                        seed += incr; 
                    } while (true);
                } 
            } 

            // New bucket[] is good to go - replace buckets and other internal state. 
            hashBuckets = newBuckets;

            hashLoadSize = (int)(hashLoadFactor * hashsize);
            if (hashLoadSize >= hashsize) { 
                hashLoadSize = hashsize - 1;
            } 
        } 

        ///  
        /// 
        ///    
        ///       Retrieves the window associated with the specified
        ///    . 
        ///    
        ///  
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 
        public static NativeWindow FromHandle(IntPtr handle) {
            if (handle != IntPtr.Zero && handleCount > 0) { 
                return GetWindowFromTable(handle);
            }
            return null;
        } 

        ///  
        ///     Calculates a prime number of at least minSize using a static table, and 
        ///     if we overflow it, we calculate directly.
        ///  
        private static int GetPrime(int minSize) {
            if (minSize < 0) {
                Debug.Fail("NativeWindow hashtable capacity overflow");
                throw new OutOfMemoryException(); 
            }
            for (int i = 0; i < primes.Length; i++) { 
                int size = primes[i]; 
                if (size >= minSize) return size;
            } 
            //outside of our predefined table.
            //compute the hard way.
            for (int j = ((minSize - 2) | 1); j < Int32.MaxValue; j += 2) {
                bool prime = true; 

                if ((j & 1) != 0) { 
                    int target = (int)Math.Sqrt(j); 
                    for (int divisor = 3; divisor < target; divisor += 2) {
                        if ((j % divisor) == 0) { 
                            prime = false;
                            break;
                        }
                    } 
                    if (prime) return j;
                } 
                else { 
                    if (j == 2) {
                        return j; 
                    }
                }
            }
            return minSize; 
        }
 
        ///  
        ///     Returns the native window for the given handle, or null if
        ///     the handle is not in our hash table. 
        /// 
        private static NativeWindow GetWindowFromTable(IntPtr handle) {

            Debug.Assert(handle != IntPtr.Zero, "Zero handles cannot be stored in the table"); 

            // Take a snapshot of buckets, in case another thread does a resize 
            HandleBucket[] buckets = hashBuckets; 
            uint seed;
            uint incr; 
            int ntry = 0;
            uint hashcode = InitHash(handle, buckets.Length, out seed, out incr);

            HandleBucket b; 
            do {
                int bucketNumber = (int)(seed % (uint)buckets.Length); 
                b = buckets[bucketNumber]; 
                if (b.handle == IntPtr.Zero) {
                    return null; 
                }
                if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && handle == b.handle) {
                    if (b.window.IsAllocated) {
                        return (NativeWindow)b.window.Target; 
                    }
                } 
                seed += incr; 
            }
            while (b.hash_coll < 0 && ++ntry < buckets.Length); 
            return null;
        }

        internal IntPtr GetHandleFromID(short id) { 
            IntPtr handle;
            if (NativeWindow.hashForIdHandle == null || !NativeWindow.hashForIdHandle.TryGetValue(id, out handle)) { 
                handle = IntPtr.Zero; 
            }
 
            return handle;
        }

        ///  
        ///     Computes the hash function:  H(key, i) = h1(key) + i*h2(key, hashSize).
        ///     The out parameter 'seed' is h1(key), while the out parameter 
        ///     'incr' is h2(key, hashSize).  Callers of this function should 
        ///     add 'incr' each time through a loop.
        ///  
        private static uint InitHash(IntPtr handle, int hashsize, out uint seed, out uint incr) {
            // Hashcode must be positive.  Also, we must not use the sign bit, since
            // that is used for the collision bit.
            uint hashcode = ((uint)handle.GetHashCode()) & 0x7FFFFFFF; 
            seed = (uint)hashcode;
            // Restriction: incr MUST be between 1 and hashsize - 1, inclusive for 
            // the modular arithmetic to work correctly.  This guarantees you'll 
            // visit every bucket in the table exactly once within hashsize
            // iterations.  Violate this and it'll cause obscure bugs forever. 
            // If you change this calculation for h2(key), update putEntry too!
            incr = (uint)(1 + (((seed >> 5) + 1) % ((uint)hashsize - 1)));
            return hashcode;
        } 

        ///  
        ///  
        ///    
        ///       Specifies a notification method that is called when the handle for a 
        ///       window is changed.
        ///    
        /// 
        protected virtual void OnHandleChange() { 
        }
 
        ///  
        /// 
        ///     On class load, we connect an event to Application to let us know when 
        ///     the process or domain terminates.  When this happens, we attempt to
        ///     clear our window class cache.  We cannot destroy windows (because we don't
        ///     have access to their thread), and we cannot unregister window classes
        ///     (because the classes are in use by the windows we can't destroy).  Instead, 
        ///     we move the class and window procs to DefWndProc
        ///  
        [PrePrepareMethod] 
        private static void OnShutdown(object sender, EventArgs e) {
 
            // If we still have windows allocated, we must sling them to userDefWindowProc
            // or else they will AV if they get a message after the managed code has been
            // removed.  In debug builds, we assert and give the "ToString" of the native
            // window. In retail we just detatch the window proc and let it go.  Note that 
            // we cannot call DestroyWindow because this API will fail if called from
            // an incorrect thread. 
            // 
            if (handleCount > 0) {
 
                Debug.Assert(userDefWindowProc != IntPtr.Zero, "We have active windows but no user window proc?");

                lock (internalSyncObject) {
                    for (int i = 0; i < hashBuckets.Length; i++) { 
                        HandleBucket b = hashBuckets[i];
                        if (b.handle != IntPtr.Zero && b.handle != new IntPtr(-1)) { 
                            HandleRef href = new HandleRef(b, b.handle); 
                            UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(null, userDefWindowProc));
                            UnsafeNativeMethods.SetClassLong(href, NativeMethods.GCL_WNDPROC, userDefWindowProc); 
                            UnsafeNativeMethods.PostMessage(href, NativeMethods.WM_CLOSE, 0, 0);

                            // Fish out the Window object, if it is valid, and NULL the handle pointer.  This
                            // way the rest of [....] won't think the handle is still valid here. 
                            if (b.window.IsAllocated) {
                                NativeWindow w = (NativeWindow)b.window.Target; 
                                if (w != null) { 
                                    w.handle = IntPtr.Zero;
                                } 
                            }

#if DEBUG && FINALIZATION_WATCH
                            Debug.Fail("Window did not clean itself up: " + b.owner); 
#endif
 
                            b.window.Free(); 
                        }
                        hashBuckets[i].handle = IntPtr.Zero; 
                        hashBuckets[i].hash_coll = 0;
                    }

                    handleCount = 0; 
                }
            } 
 
            WindowClass.DisposeCache();
        } 

        /// 
        /// 
        ///     
        ///       When overridden in a derived class,
        ///       manages an unhandled thread 
        ///       exception. 
        ///    
        ///  
        protected virtual void OnThreadException(Exception e) {
        }

        ///  
        /// 
        ///     
        ///       Releases the handle associated with this window. 
        ///    
        ///  
        public virtual void ReleaseHandle() {
            ReleaseHandle(true);
        }
 
        /// 
        ///     Releases the handle associated with this window.  If handleValid 
        ///     is true, this will unsubclass the window as well.  HandleValid 
        ///     should be false if we are releasing in response to a
        ///     WM_DESTROY.  Unsubclassing during this message can cause problems 
        ///     with XP's theme manager and it's not needed anyway.
        /// 
        private void ReleaseHandle(bool handleValid) {
            if (handle != IntPtr.Zero) { 
                //
                lock (this) { 
                    if (handle != IntPtr.Zero) { 
                        if (handleValid) {
                            UnSubclass(); 
                        }

                        RemoveWindowFromTable(handle, this);
 
                        if (ownHandle) {
                            System.Internal.HandleCollector.Remove(handle, NativeMethods.CommonHandles.Window); 
                            ownHandle = false; 
                        }
 
                        handle = IntPtr.Zero;
                        //if not finalizing already.
                        if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
                            OnHandleChange(); 

                            // Now that we have disposed, there is no need to finalize us any more.  So 
                            // Mark to the garbage collector that we no longer need finalization. 
                            //
                            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); 
                            try {
                                GC.SuppressFinalize(this);
                            }
                            finally { 
                                CodeAccessPermission.RevertAssert();
                            } 
                            suppressedGC = true; 
                        }
                    } 
                }
            }
        }
 
        /// 
        ///     Removes an entry from this hashtable. If an entry with the specified 
        ///     key exists in the hashtable, it is removed. 
        /// 
        private static void RemoveWindowFromTable(IntPtr handle, NativeWindow window) { 

            Debug.Assert(handle != IntPtr.Zero, "Incorrect handle");

            lock (internalSyncObject) { 

                uint seed; 
                uint incr; 
                // Assuming only one concurrent writer, write directly into buckets.
                uint hashcode = InitHash(handle, hashBuckets.Length, out seed, out incr); 
                int ntry = 0;
                NativeWindow prevWindow = window.PreviousWindow;
                HandleBucket b;
 
                int bn; // bucketNumber
                do { 
                    bn = (int)(seed % (uint)hashBuckets.Length);  // bucketNumber 
                    b = hashBuckets[bn];
                    if (((b.hash_coll & 0x7FFFFFFF) == hashcode) && handle == b.handle) { 

                        bool shouldRemoveBucket = (window.nextWindow == null);
                        bool shouldReplaceBucket = IsRootWindowInListWithChildren(window);
 
                        // We need to fixup the link pointers of window here.
                        // 
                        if (window.previousWindow != null) { 
                            window.previousWindow.nextWindow = window.nextWindow;
                        } 
                        if (window.nextWindow != null) {
                            window.nextWindow.defWindowProc = window.defWindowProc;
                            window.nextWindow.previousWindow = window.previousWindow;
                        } 

                        window.nextWindow = null; 
                        window.previousWindow = null; 

                        if (shouldReplaceBucket) { 
                            // Free the existing GC handle
                            if (hashBuckets[bn].window.IsAllocated) {
                                hashBuckets[bn].window.Free();
                            } 

                            hashBuckets[bn].window = GCHandle.Alloc(prevWindow, GCHandleType.Weak); 
                        } 
                        else if (shouldRemoveBucket) {
 
                            // Clear hash_coll field, then key, then value
                            hashBuckets[bn].hash_coll &= unchecked((int)0x80000000);
                            if (hashBuckets[bn].hash_coll != 0) {
                                hashBuckets[bn].handle = new IntPtr(-1); 
                            }
                            else { 
                                hashBuckets[bn].handle = IntPtr.Zero; 
                            }
 
                            if (hashBuckets[bn].window.IsAllocated) {
                                hashBuckets[bn].window.Free();
                            }
 
                            Debug.Assert(handleCount > 0, "Underflow on handle count");
                            handleCount--; 
                        } 
                        return;
                    } 
                    seed += incr;
                } while (hashBuckets[bn].hash_coll < 0 && ++ntry < hashBuckets.Length);
            }
        } 

 
        ///  
        ///   Determines if the given window is the first member of the linked list
        ///  
        private static bool IsRootWindowInListWithChildren(NativeWindow window) {
            // This seems backwards, but it isn't.  When a new subclass comes in,
            // it's previousWindow field is set to the previous subclass.  Therefore,
            // the top of the subclass chain has nextWindow == null and previousWindow 
            // == the first child subclass.
            return ((window.PreviousWindow != null) && (window.nextWindow == null)); 
        } 

        ///  
        ///   Determines if the given window is the first member of the linked list
        ///   and has no children
        /// 
 
        /* No one is calling this private method, so it is safe to comment it out.
        private static bool IsRootWindowInListWithNoChildren(NativeWindow window) 
        { 
            return ((window.PreviousWindow == null) && (window.nextWindow == null));
        } 
        */


        ///  
        ///     Inserts an entry into this ID hashtable.
        ///  
        internal static void RemoveWindowFromIDTable(IntPtr handle) { 
            short id = (short)NativeWindow.hashForHandleId[handle];
            NativeWindow.hashForHandleId.Remove(handle); 
            NativeWindow.hashForIdHandle.Remove(id);
        }

        ///  
        ///     This method can be used to modify the exception handling behavior of
        ///     NativeWindow.  By default, NativeWindow will detect if an application 
        ///     is running under a debugger, or is running on a machine with a debugger 
        ///     installed.  In this case, an unhandled exception in the NativeWindow's
        ///     WndProc method will remain unhandled so the debugger can trap it.  If 
        ///     there is no debugger installed NativeWindow will trap the exception
        ///     and route it to the Application class's unhandled exception filter.
        ///
        ///     You can control this behavior via a config file, or directly through 
        ///     code using this method.  Setting the unhandled exception mode does
        ///     not change the behavior of any NativeWindow objects that are currently 
        ///     connected to window handles; it only affects new handle connections. 
        ///
        ///     When threadScope is false, the application exception mode is set. The 
        ///     application exception mode is used for all threads that have the Automatic mode.
        ///     Setting the application exception mode does not affect the setting of the current thread.
        ///
        ///     When threadScope is true, the thread exception mode is set. The thread 
        ///     exception mode overrides the application exception mode if it's not Automatic.
        ///  
        internal static void SetUnhandledExceptionModeInternal(UnhandledExceptionMode mode, bool threadScope) { 

            if (!threadScope && anyHandleCreatedInApp) { 
                throw new InvalidOperationException(SR.GetString(SR.ApplicationCannotChangeApplicationExceptionMode));
            }
            if (threadScope && anyHandleCreated) {
                throw new InvalidOperationException(SR.GetString(SR.ApplicationCannotChangeThreadExceptionMode)); 
            }
 
            switch (mode) { 
                case UnhandledExceptionMode.Automatic:
                    if (threadScope) { 
                        userSetProcFlags = 0;
                    }
                    else {
                        userSetProcFlagsForApp = 0; 
                    }
                    break; 
                case UnhandledExceptionMode.ThrowException: 
                    if (threadScope) {
                        userSetProcFlags = UseDebuggableWndProc | InitializedFlags; 
                    }
                    else {
                        userSetProcFlagsForApp = UseDebuggableWndProc | InitializedFlags;
                    } 
                    break;
                case UnhandledExceptionMode.CatchException: 
                    if (threadScope) { 
                        userSetProcFlags = InitializedFlags;
                    } 
                    else {
                        userSetProcFlagsForApp = InitializedFlags;
                    }
                    break; 
                default:
                    throw new InvalidEnumArgumentException("mode", (int)mode, typeof(UnhandledExceptionMode)); 
            } 
        }
 
        /// 
        ///     Unsubclassing is a tricky business.  We need to account for
        ///     some border cases:
        /// 
        ///     1) User has done multiple subclasses but has un-subclassed out of order.
        ///     2) User has done multiple subclasses but now our defWindowProc points to 
        ///        a NativeWindow that has GC'd 
        ///     3) User releasing this handle but this NativeWindow is not the current
        ///        window proc. 
        /// 
        private void UnSubclass() {
            bool finalizing = (!weakThisPtr.IsAlive || weakThisPtr.Target == null);
            HandleRef href = new HandleRef(this, handle); 

            // Don't touch if the current window proc is not ours. 
            // 
            IntPtr currentWinPrc = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, handle), NativeMethods.GWL_WNDPROC);
            if (windowProcPtr == currentWinPrc) { 
                if (previousWindow == null) {

#if DEBUG
                subclassStatus = "Unsubclassing back to native defWindowProc"; 
#endif
 
                    // If the defWindowProc points to a native window proc, previousWindow will 
                    // be null.  In this case, it is completely safe to assign defWindowProc
                    // to the current wndproc. 
                    //
                    UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, defWindowProc));
                }
                else { 
                    if (finalizing) {
 
#if DEBUG 
                        subclassStatus = "Setting back to userDefWindowProc -- next chain is managed";
#endif 

                        // Here, we are finalizing and defWindowProc is pointing to a managed object.  We must assume
                        // that the object defWindowProc is pointing to is also finalizing.  Why?  Because we're
                        // holding a ref to it, and it is holding a ref to us.  The only way this cycle will 
                        // finalize is if no one else is hanging onto it.  So, we re-assign the window proc to
                        // userDefWindowProc. 
                        UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, userDefWindowProc)); 
                    }
                    else { 

#if DEBUG
                        subclassStatus = "Setting back to next managed subclass object";
#endif 

                        // Here we are not finalizing so we use the windowProc for our previous window.  This may 
                        // DIFFER from the value we are currently storing in defWindowProc because someone may 
                        // have re-subclassed.
                        UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, previousWindow.windowProc); 
                    }
                }
            }
            else { 
                // cutting the subclass chain anyway, even if we're not the last one in the chain
                // if the whole chain is all managed NativeWindow it doesnt matter, 
                // if the chain is not, then someone has been dirty and didn't clean up properly, too bad for them... 

                //We will cut off the chain if we cannot unsubclass. 
                //If we find previouswindow pointing to us, then we can let RemoveWindowFromTable reassign the
                //defwndproc pointers properly when this guy gets removed (thereby unsubclassing ourselves)

                if (nextWindow == null || nextWindow.defWindowProc != windowProcPtr) { 
                    // we didn't find it... let's unhook anyway and cut the chain... this prevents crashes
                    UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, userDefWindowProc)); 
                } 
#if DEBUG
                subclassStatus = "FORCE unsubclass -- we do not own the subclass"; 
#endif
            }
        }
 
        /// 
        ///  
        ///     
        ///       Invokes the default window procedure associated with
        ///       this window. 
        ///    
        /// 

        protected virtual void WndProc(ref Message m) { 
            DefWndProc(ref m);
        } 
 
        /// 
        ///     A struct that contains a single bucket for our handle / GCHandle hash table. 
        ///     The hash table algorithm we use here was stolen selfishly from the framework's
        ///     Hashtable class.  We don't use Hashtable directly, however, because of boxing
        ///     concerns.  It's algorithm is perfect for our needs, however:  Multiple
        ///     reader, single writer without the need for locks and constant lookup time. 
        ///
        ///     Differences between this implementation and Hashtable: 
        /// 
        ///     Keys are IntPtrs; their hash code is their value.  Collision is still
        ///     marked with the high bit. 
        ///
        ///     Reclaimed buckets store -1 in their handle, not the hash table reference.
        /// 
        private struct HandleBucket { 
            public IntPtr handle; // Win32 window handle
            public GCHandle window; // a weak GC handle to the NativeWindow class 
            public int hash_coll;   // Store hash code; sign bit means there was a collision. 
#if DEBUG
            public string owner;    // owner of this handle 
#endif
        }

        ///  
        /// 
        ///     WindowClass encapsulates a window class. 
        ///  
        /// 
        [ 
        System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
        ]
        private class WindowClass {
            internal static WindowClass cache; 

            internal WindowClass next; 
            internal string className; 
            internal int classStyle;
            internal string windowClassName; 
            internal int hashCode;
            internal IntPtr defWindowProc;
            internal NativeMethods.WndProc windowProc;
            internal bool registered; 
            internal NativeWindow targetWindow;
 
            private static object wcInternalSyncObject = new object(); 
            private static int domainQualifier = 0;
 
            internal WindowClass(string className, int classStyle) {
                this.className = className;
                this.classStyle = classStyle;
                RegisterClass(); 
            }
 
            public IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { 
                Debug.Assert(hWnd != IntPtr.Zero, "Windows called us with an HWND of 0");
                UnsafeNativeMethods.SetWindowLong(new HandleRef(null, hWnd), NativeMethods.GWL_WNDPROC, new HandleRef(this, defWindowProc)); 
                targetWindow.AssignHandle(hWnd);
                return targetWindow.Callback(hWnd, msg, wparam, lparam);
            }
 
            /// 
            ///  
            ///     Retrieves a WindowClass object for use.  This will create a new 
            ///     object if there is no such class/style available, or retrun a
            ///     cached object if one exists. 
            /// 
            internal static WindowClass Create(string className, int classStyle) {
                lock (wcInternalSyncObject) {
                    WindowClass wc = cache; 
                    if (className == null) {
                        while (wc != null && (wc.className != null || 
                                              wc.classStyle != classStyle)) wc = wc.next; 
                    }
                    else { 
                        while (wc != null &&
                               !className.Equals(wc.className)) {
                            wc = wc.next;
                        } 
                    }
                    if (wc == null) { 
                        wc = new WindowClass(className, classStyle); 
                        wc.next = cache;
                        cache = wc; 
                    }
                    else {
                        if (!wc.registered) {
                            wc.RegisterClass(); 
                        }
                    } 
                    return wc; 
                }
            } 

            /// 
            /// 
            ///     Disposes our window class cache.  This doesn't free anything 
            ///     from the actual cache; it merely attempts to unregister
            ///     the classes of everything in the cache.  This allows the unused 
            ///     classes to be unrooted. They can later be re-rooted and reused. 
            /// 
            internal static void DisposeCache() { 
                lock (wcInternalSyncObject) {
                    WindowClass wc = cache;

                    while (wc != null) { 
                        wc.UnregisterClass();
                        wc = wc.next; 
                    } 
                }
            } 

            /// 
            ///     Fabricates a full class name from a partial.
            ///  
            private string GetFullClassName(string className) {
                StringBuilder b = new StringBuilder(50); 
                b.Append(Application.WindowsFormsVersion); 
                b.Append('.');
                b.Append(className); 
                b.Append(".app.");
                b.Append(domainQualifier);
                b.Append('.');
                String appDomain = Convert.ToString(AppDomain.CurrentDomain.GetHashCode(), 16); 
                b.Append(VersioningHelper.MakeVersionSafeName(appDomain, ResourceScope.Process, ResourceScope.AppDomain));
                return b.ToString(); 
            } 

            ///  
            /// 
            ///     Once the classname and style bits have been set, this can
            ///     be called to register the class.
            ///  
            private void RegisterClass() {
                NativeMethods.WNDCLASS_D wndclass = new NativeMethods.WNDCLASS_D(); 
 
                if (userDefWindowProc == IntPtr.Zero) {
                    string defproc = (Marshal.SystemDefaultCharSize == 1 ? "DefWindowProcA" : "DefWindowProcW"); 

                    userDefWindowProc = UnsafeNativeMethods.GetProcAddress(new HandleRef(null, UnsafeNativeMethods.GetModuleHandle("user32.dll")), defproc);
                    if (userDefWindowProc == IntPtr.Zero) {
                        throw new Win32Exception(); 
                    }
                } 
 
                string localClassName = className;
 
                if (localClassName == null) {

                    // If we don't use a hollow brush here, Windows will "pre paint" us with COLOR_WINDOW which
                    // creates a little bit if flicker.  This happens even though we are overriding wm_erasebackgnd. 
                    // Make this hollow to avoid all flicker.
                    // 
                    wndclass.hbrBackground = UnsafeNativeMethods.GetStockObject(NativeMethods.HOLLOW_BRUSH); //(IntPtr)(NativeMethods.COLOR_WINDOW + 1); 
                    wndclass.style = classStyle;
 
                    defWindowProc = userDefWindowProc;
                    localClassName = "Window." + Convert.ToString(classStyle, 16);
                    hashCode = 0;
                } 
                else {
                    NativeMethods.WNDCLASS_I wcls = new NativeMethods.WNDCLASS_I(); 
                    bool ok = UnsafeNativeMethods.GetClassInfo(NativeMethods.NullHandleRef, className, wcls); 
                    int error = Marshal.GetLastWin32Error();
                    if (!ok) { 
                        throw new Win32Exception(error, SR.GetString(SR.InvalidWndClsName));
                    }
                    wndclass.style = wcls.style;
                    wndclass.cbClsExtra = wcls.cbClsExtra; 
                    wndclass.cbWndExtra = wcls.cbWndExtra;
                    wndclass.hIcon = wcls.hIcon; 
                    wndclass.hCursor = wcls.hCursor; 
                    wndclass.hbrBackground = wcls.hbrBackground;
                    wndclass.lpszMenuName = Marshal.PtrToStringAuto(wcls.lpszMenuName); 
                    localClassName = className;
                    defWindowProc = wcls.lpfnWndProc;
                    hashCode = className.GetHashCode();
                } 

                // Our static data is different for different app domains, so we include the app domain in with 
                // our window class name.  This way our static table always matches what Win32 thinks. 
                //
                windowClassName = GetFullClassName(localClassName); 
                windowProc = new NativeMethods.WndProc(this.Callback);
                wndclass.lpfnWndProc = windowProc;
                wndclass.hInstance = UnsafeNativeMethods.GetModuleHandle(null);
                wndclass.lpszClassName = windowClassName; 

                short atom = UnsafeNativeMethods.RegisterClass(wndclass); 
                if (atom == 0) { 

                    int err = Marshal.GetLastWin32Error(); 
                    if (err == NativeMethods.ERROR_CLASS_ALREADY_EXISTS) {
                        // Check to see if the window class window
                        // proc points to DefWndProc.  If it does, then
                        // this is a class from a rudely-terminated app domain 
                        // and we can safely reuse it.  If not, we've got
                        // to throw. 
                        NativeMethods.WNDCLASS_I wcls = new NativeMethods.WNDCLASS_I(); 
                        bool ok = UnsafeNativeMethods.GetClassInfo(new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)), windowClassName, wcls);
                        if (ok && wcls.lpfnWndProc == NativeWindow.UserDefindowProc) { 

                            // We can just reuse this class because we have marked it
                            // as being a nop in another domain.  All we need to do is call SetClassLong.
                            // Only one problem:  SetClassLong takes an HWND, which we don't have.  That leaves 
                            // us with some tricky business. First, try this the easy way and see
                            // if we can simply unregister and re-register the class.  This might 
                            // work because the other domain shutdown would have posted WM_CLOSE to all 
                            // the windows of the class.
                            if (UnsafeNativeMethods.UnregisterClass(windowClassName, new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)))) { 
                                atom = UnsafeNativeMethods.RegisterClass(wndclass);
                                // If this fails, we will always raise the first err above.  No sense exposing our twiddling.
                            }
                            else { 
                                // This is a little harder.  We cannot reuse the class because it is
                                // already in use.  We must create a new class.  We bump our domain qualifier 
                                // here to account for this, so we only do this expensive search once for the 
                                // domain.
                                do { 
                                    domainQualifier++;
                                    windowClassName = GetFullClassName(localClassName);
                                    wndclass.lpszClassName = windowClassName;
                                    atom = UnsafeNativeMethods.RegisterClass(wndclass); 
                                } while (atom == 0 && Marshal.GetLastWin32Error() == NativeMethods.ERROR_CLASS_ALREADY_EXISTS);
                            } 
                        } 
                    }
 
                    if (atom == 0) {
                        windowProc = null;
                        throw new Win32Exception(err);
                    } 
                }
                registered = true; 
            } 

            ///  
            /// 
            ///     Unregisters this window class.  Unregistration is not a
            ///     last resort; the window class may be re-registered through
            ///     a call to registerClass. 
            /// 
            private void UnregisterClass() { 
                if (registered && UnsafeNativeMethods.UnregisterClass(windowClassName, new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)))) { 
                    windowProc = null;
                    registered = false; 
                }
            }
        }
    } 

    ///  
    ///     Determines the exception mode of NativeWindow's WndProc method.  Pass 
    ///     a value of this enum into SetUnhandledExceptionMode to control how
    ///     new NativeWindow objects handle exceptions. 
    /// 
    public enum UnhandledExceptionMode {
        Automatic,
        ThrowException, 
        CatchException
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

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