File: winforms\Managed\System\WinForms\NativeWindow.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="NativeWindow.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 */
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;
 
    /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Provides a low-level encapsulation of a window handle
    ///       and a window procedure. The class automatically manages window class creation and registration.
    ///    </para>
    /// </devdoc>
    [
        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<short, IntPtr> hashForIdHandle;
        private static Dictionary<IntPtr, short> 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;
        private DpiAwarenessContext windowDpiAwarenessContext = DpiHelper.EnableDpiChangedHighDpiImprovements ? 
                                                                  CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() :
                                                                  DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
 
        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<short, IntPtr>();
            hashForHandleId = new Dictionary<IntPtr, short>();
        }
 
        public NativeWindow() {
            weakThisPtr = new WeakReference(this);
        }
 
        /// <summary>
        /// Cache window DpiContext awareness information that helps to create handle with right context at the later time.
        /// </summary>
        internal DpiAwarenessContext DpiAwarenessContext {
            get {
                return windowDpiAwarenessContext;
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.Finalize"]/*' />
        /// <devdoc>
        ///     Override's the base object's finalize method.
        /// </devdoc>
        ~NativeWindow() {
            ForceExitMessageLoop();
        }
 
        /// <summary>
        /// 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.
        /// </summary>
        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);
            }
        }
 
        /// <devdoc>
        ///     Indicates whether a window handle was created & is being tracked.
        /// </devdoc>
        internal static bool AnyHandleCreated {
            get {
                return anyHandleCreated;
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.Handle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the handle for this window.
        ///    </para>
        /// </devdoc>
        public IntPtr Handle {
            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;
            }
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        internal NativeWindow PreviousWindow {
            [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
            get {
                return previousWindow;
            }
        }
 
        /// <devdoc>
        ///     Helper method that returns a static value for the
        ///     unmanaged DefWindowProc call.
        /// </devdoc>
        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;
            }
        }
 
        /// <devdoc>
        ///     Inserts an entry into this hashtable.
        /// </devdoc>
        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) {
                            if (prevWindow.Target != null) {
                                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.");
        }
 
        /// <devdoc>
        ///     Inserts an entry into this ID hashtable.
        /// </devdoc>
        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;
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.AssignHandle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Assigns a handle to this
        ///       window.
        ///    </para>
        /// </devdoc>
        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;
 
                // Synchronizing the handle DPiAwarenessContext with control DpiAwarenessContext. They can be out of sync in case control has a parent set before the handle was created.
                if (DpiHelper.EnableDpiChangedHighDpiImprovements && windowDpiAwarenessContext != DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) {
                    var handleDpiContext = CommonUnsafeNativeMethods.TryGetDpiAwarenessContextForWindow(this.handle);
                    if (handleDpiContext!= DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && !CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(windowDpiAwarenessContext, handleDpiContext)) {
                        windowDpiAwarenessContext = handleDpiContext;
                    }
                }
 
                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();
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.Callback"]/*' />
        /// <devdoc>
        ///     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.
        /// </devdoc>
        /// <internalonly/>
        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;
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.CheckReleased"]/*' />
        /// <devdoc>
        ///     Raises an exception if the window handle is not zero.
        /// </devdoc>
        /// <internalonly/>
        private void CheckReleased() {
            if (handle != IntPtr.Zero) {
                throw new InvalidOperationException(SR.GetString(SR.HandleAlreadyExists));
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.CreateHandle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Creates a window handle for this
        ///       window.
        ///    </para>
        /// </devdoc>
        [
            // 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 createResult = IntPtr.Zero;
                    int lastWin32Error = 0;
 
                    // Parking window dpi awarness context need to match with dpi awarenss context of control being 
                    // parented to this parkign window. Otherwise, reparenting of control will fail.
                    using (DpiHelper.EnterDpiAwarenessScope(this.windowDpiAwarenessContext)) {
                        IntPtr modHandle = UnsafeNativeMethods.GetModuleHandle(null);
 
                        // 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);
                }
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.DebuggableCallback"]/*' />
        /// <devdoc>
        ///     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.
        /// </devdoc>
        /// <internalonly/>
        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;
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.DefWndProc"]/*' />
        /// <devdoc>
        ///     Invokes the default window procedure associated with this Window. It is
        ///     an error to call this method when the Handle property is zero.
        /// </devdoc>
        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);
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.DestroyHandle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Destroys the
        ///       handle associated with this window.
        ///    </para>
        /// </devdoc>
        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;
            }
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        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;
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.FromHandle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Retrieves the window associated with the specified
        ///    <paramref name="handle"/>.
        ///    </para>
        /// </devdoc>
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
        public static NativeWindow FromHandle(IntPtr handle) {
            if (handle != IntPtr.Zero && handleCount > 0) {
                return GetWindowFromTable(handle);
            }
            return null;
        }
 
        /// <devdoc>
        ///     Calculates a prime number of at least minSize using a static table, and
        ///     if we overflow it, we calculate directly.
        /// </devdoc>
        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;
        }
 
        /// <devdoc>
        ///     Returns the native window for the given handle, or null if 
        ///     the handle is not in our hash table.
        /// </devdoc>
        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;
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        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;
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.OnHandleChange"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Specifies a notification method that is called when the handle for a
        ///       window is changed.
        ///    </para>
        /// </devdoc>
        protected virtual void OnHandleChange() {
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.OnShutdown"]/*' />
        /// <devdoc>
        ///     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
        /// </devdoc>
        [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 Microsoft 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();
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.OnThreadException"]/*' />
        /// <devdoc>
        ///    <para>
        ///       When overridden in a derived class,
        ///       manages an unhandled thread
        ///       exception.
        ///    </para>
        /// </devdoc>
        protected virtual void OnThreadException(Exception e) {
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.ReleaseHandle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Releases the handle associated with this window.
        ///    </para>
        /// </devdoc>
        public virtual void ReleaseHandle() {
            ReleaseHandle(true);
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        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;
                        }
                    }
                }
            }
        }
 
        /// <devdoc>
        ///     Removes an entry from this hashtable. If an entry with the specified
        ///     key exists in the hashtable, it is removed.
        /// </devdoc>
        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);
            }
        }
 
 
        /// <devdoc>
        ///   Determines if the given window is the first member of the linked list
        /// </devdoc>
        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));
        }
 
        /// <devdoc>
        ///   Determines if the given window is the first member of the linked list
        ///   and has no children
        /// </devdoc>
 
        /* 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));     
        }       
        */
 
 
        /// <devdoc>
        ///     Inserts an entry into this ID hashtable.
        /// </devdoc>
        internal static void RemoveWindowFromIDTable(IntPtr handle) {
            short id = (short)NativeWindow.hashForHandleId[handle];
            NativeWindow.hashForHandleId.Remove(handle);
            NativeWindow.hashForIdHandle.Remove(id);
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        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));
            }
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        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
            }
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.WndProc"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Invokes the default window procedure associated with
        ///       this window.
        ///    </para>
        /// </devdoc>
 
        protected virtual void WndProc(ref Message m) {
            DefWndProc(ref m);
        }
 
        /// <devdoc>
        ///     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.
        /// </devdoc>
        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
        }
 
        /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.WindowClass"]/*' />
        /// <devdoc>
        ///     WindowClass encapsulates a window class.
        /// </devdoc>
        /// <internalonly/>
        [
        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);
            }
 
            /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.WindowClass.Create"]/*' />
            /// <devdoc>
            ///     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.
            /// </devdoc>
            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;
                }
            }
 
            /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.WindowClass.DisposeCache"]/*' />
            /// <devdoc>
            ///     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.
            /// </devdoc>
            internal static void DisposeCache() {
                lock (wcInternalSyncObject) {
                    WindowClass wc = cache;
 
                    while (wc != null) {
                        wc.UnregisterClass();
                        wc = wc.next;
                    }
                }
            }
 
            /// <devdoc>
            ///     Fabricates a full class name from a partial.
            /// </devdoc>
            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();
            }
 
            /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.WindowClass.RegisterClass"]/*' />
            /// <devdoc>
            ///     Once the classname and style bits have been set, this can
            ///     be called to register the class.
            /// </devdoc>
            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;
            }
 
            /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.WindowClass.UnregisterClass"]/*' />
            /// <devdoc>
            ///     Unregisters this window class.  Unregistration is not a
            ///     last resort; the window class may be re-registered through
            ///     a call to registerClass.
            /// </devdoc>
            private void UnregisterClass() {
                if (registered && UnsafeNativeMethods.UnregisterClass(windowClassName, new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)))) {
                    windowProc = null;
                    registered = false;
                }
            }
        }
    }
 
    /// <devdoc>
    ///     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.  
    /// </devdoc>
    public enum UnhandledExceptionMode {
        Automatic,
        ThrowException,
        CatchException
    }
}