Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / WinForms / Managed / System / WinForms / NativeWindow.cs / 4 / 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; ////// /// [ 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/// Provides a low-level encapsulation of a window handle /// and a window procedure. The class automatically manages window class creation and registration. /// ///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; } } ////// /// 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; } } ////// Gets the handle for this window. /// ////// 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; } ////// /// 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(); } } ////// Assigns a handle to this /// 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 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)); } } /// /// /// [ // 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); } } } ////// Creates a window handle for this /// 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); } } ////// /// 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; } } ////// Destroys the /// handle associated with this window. /// ////// 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; } } ////// /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public static NativeWindow FromHandle(IntPtr handle) { if (handle != IntPtr.Zero && handleCount > 0) { return GetWindowFromTable(handle); } return null; } ////// Retrieves the window associated with the specified /// ///. /// /// 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; } ////// /// protected virtual void OnHandleChange() { } ////// Specifies a notification method that is called when the handle for a /// window is changed. /// ////// /// 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 winforms 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(); } ////// /// protected virtual void OnThreadException(Exception e) { } ////// When overridden in a derived class, /// manages an unhandled thread /// exception. /// ////// /// public virtual void ReleaseHandle() { ReleaseHandle(true); } ////// Releases the handle associated with this window. /// ////// 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 should----Bucket = (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 (should----Bucket) { // 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 } } ////// /// protected virtual void WndProc(ref Message m) { DefWndProc(ref m); } ////// Invokes the default window procedure associated with /// this window. /// ////// 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('.'); b.Append(Convert.ToString(AppDomain.CurrentDomain.GetHashCode(), 16)); 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. //------------------------------------------------------------------------------ //// 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; ////// /// [ 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/// Provides a low-level encapsulation of a window handle /// and a window procedure. The class automatically manages window class creation and registration. /// ///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; } } ////// /// 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; } } ////// Gets the handle for this window. /// ////// 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; } ////// /// 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(); } } ////// Assigns a handle to this /// 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 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)); } } /// /// /// [ // 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); } } } ////// Creates a window handle for this /// 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); } } ////// /// 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; } } ////// Destroys the /// handle associated with this window. /// ////// 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; } } ////// /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public static NativeWindow FromHandle(IntPtr handle) { if (handle != IntPtr.Zero && handleCount > 0) { return GetWindowFromTable(handle); } return null; } ////// Retrieves the window associated with the specified /// ///. /// /// 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; } ////// /// protected virtual void OnHandleChange() { } ////// Specifies a notification method that is called when the handle for a /// window is changed. /// ////// /// 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 winforms 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(); } ////// /// protected virtual void OnThreadException(Exception e) { } ////// When overridden in a derived class, /// manages an unhandled thread /// exception. /// ////// /// public virtual void ReleaseHandle() { ReleaseHandle(true); } ////// Releases the handle associated with this window. /// ////// 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 should----Bucket = (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 (should----Bucket) { // 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 } } ////// /// protected virtual void WndProc(ref Message m) { DefWndProc(ref m); } ////// Invokes the default window procedure associated with /// this window. /// ////// 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('.'); b.Append(Convert.ToString(AppDomain.CurrentDomain.GetHashCode(), 16)); 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataException.cs
- GraphicsContainer.cs
- DataGridViewHitTestInfo.cs
- DescendantOverDescendantQuery.cs
- SeekableReadStream.cs
- ASCIIEncoding.cs
- httpstaticobjectscollection.cs
- SqlProviderServices.cs
- XmlAttributeOverrides.cs
- EntryWrittenEventArgs.cs
- Int32Rect.cs
- XmlIgnoreAttribute.cs
- TextControlDesigner.cs
- DriveNotFoundException.cs
- WebPartHeaderCloseVerb.cs
- RenderTargetBitmap.cs
- CheckableControlBaseAdapter.cs
- DoubleLinkList.cs
- PolicyManager.cs
- DiagnosticTraceSchemas.cs
- PresentationAppDomainManager.cs
- TimeSpanMinutesOrInfiniteConverter.cs
- coordinator.cs
- SocketCache.cs
- OperatingSystem.cs
- SiteIdentityPermission.cs
- Domain.cs
- SettingsBase.cs
- XmlSchemaResource.cs
- CqlLexer.cs
- SystemInformation.cs
- PowerStatus.cs
- BitmapEffectState.cs
- BuildResult.cs
- XNodeValidator.cs
- DataGridViewCheckBoxColumn.cs
- ErrorRuntimeConfig.cs
- ComponentEditorPage.cs
- ExpressionPrefixAttribute.cs
- QueuePropertyVariants.cs
- RegistryDataKey.cs
- WebServiceClientProxyGenerator.cs
- ExpressionSelection.cs
- InlinedAggregationOperatorEnumerator.cs
- XmlEntity.cs
- TagNameToTypeMapper.cs
- XmlDataSourceNodeDescriptor.cs
- _DigestClient.cs
- DirectoryInfo.cs
- PolygonHotSpot.cs
- SqlTriggerContext.cs
- UpdatePanelControlTrigger.cs
- DataGridViewTopRowAccessibleObject.cs
- MobileTextWriter.cs
- DynamicUpdateCommand.cs
- Activator.cs
- RectConverter.cs
- OpCodes.cs
- DataGridViewButtonCell.cs
- SqlMethodTransformer.cs
- ComboBoxRenderer.cs
- ColumnHeaderConverter.cs
- Path.cs
- Model3DGroup.cs
- MatrixKeyFrameCollection.cs
- RuntimeCompatibilityAttribute.cs
- TriggerBase.cs
- NamespaceEmitter.cs
- RenamedEventArgs.cs
- MatrixAnimationBase.cs
- SessionIDManager.cs
- BuildResultCache.cs
- MetadataSerializer.cs
- PathGradientBrush.cs
- OdbcConnection.cs
- GetReadStreamResult.cs
- HyperlinkAutomationPeer.cs
- X509Certificate2.cs
- VisemeEventArgs.cs
- MappingModelBuildProvider.cs
- Gdiplus.cs
- InvokePattern.cs
- StyleSelector.cs
- ToolStripItemClickedEventArgs.cs
- Sequence.cs
- RequestCacheManager.cs
- PagesChangedEventArgs.cs
- KnownTypesHelper.cs
- MetadataItemEmitter.cs
- OdbcParameterCollection.cs
- regiisutil.cs
- MenuItem.cs
- TextEditorParagraphs.cs
- DataGridViewBand.cs
- FontFamily.cs
- Crc32Helper.cs
- PassportAuthentication.cs
- XmlSchemaRedefine.cs
- SystemIcmpV4Statistics.cs
- WebPartMenu.cs