Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Shared / MS / Win32 / HwndSubclass.cs / 1 / HwndSubclass.cs
using System; using System.Runtime.InteropServices; using System.Security.Permissions; using MS.Internal; using MS.Utility; using System.Windows; using System.Windows.Threading; using System.Security; // CAS using System.Threading; // Thread // The SecurityHelper class differs between assemblies and could not actually be // shared, so it is duplicated across namespaces to prevent name collision. #if WINDOWS_BASE using MS.Internal.WindowsBase; #elif PRESENTATION_CORE using MS.Internal.PresentationCore; #elif PRESENTATIONFRAMEWORK using MS.Internal.PresentationFramework; #elif DRT using MS.Internal.Drt; #else #error Attempt to use a class (duplicated across multiple namespaces) from an unknown assembly. #endif namespace MS.Win32 { ////// The HwndSubclass class provides a managed way to subclass an existing /// HWND. This class inserts itself into the WNDPROC chain for the /// window, and will call a specified delegate to process the window /// messages that arrive. The delegate has a slightly different /// signature than a WNDPROC to be more specific about whether the /// message was handled or not. If the message was not handled by the /// delegate, this class passes the message on down the WNDPROC chain. /// /// To use this class properly, simply: /// 1) Create an instance of the HwndSubclass class and pass the delegate /// to the constructor. /// 2) Call Attach(HWND) to subclass an existing window. /// 3) Call Detach(false) to unsubclass the window when you are done. /// /// You can also just call RequestDetach() to send a message to the /// window that will cause the HwndSubclass to detach itself. This is /// important if you are on a different thread, as the HwndSubclass class /// is not thread safe and will be operated on by the thread that owns /// the window. /// ///Not available to partial trust callers [FriendAccessAllowed] // Built into Base, also used by Framework internal class HwndSubclass : IDisposable { ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] static HwndSubclass() { DetachMessage = UnsafeNativeMethods.RegisterWindowMessage("HwndSubclass.DetachMessage"); if (DetachMessage == 0) { throw new System.ComponentModel.Win32Exception(); } // Go find the address of DefWindowProc. IntPtr hModuleUser32 = UnsafeNativeMethods.GetModuleHandle(ExternDll.User32); IntPtr address = UnsafeNativeMethods.GetProcAddress(new HandleRef(null,hModuleUser32), "DefWindowProcW"); DefWndProc = address; } ////// This HwndSubclass constructor binds the HwndSubclass object to the /// specified delegate. This delegate will be called to process /// the messages that are sent or posted to the window. /// /// /// The delegate that will be called to process the messages that /// are sent or posted to the window. /// ////// Nothing. /// ////// Critical: This code creates an object that is not allowed in partial trust /// [SecurityCritical] internal HwndSubclass(HwndWrapperHook hook) { if(hook == null) { throw new ArgumentNullException("hook"); } _bond = Bond.Unattached; _hook = new WeakReference(hook); // Allocate a GC handle so that we won't be collected, even if all // references to us get released. This is because a component outside // of the managed code (ie. the window we are subclassing) still holds // a reference to us - just not a reference that the GC recognizes. _gcHandle = GCHandle.Alloc(this); } // This is LIVE OBJECT because it has a GCHandle. The only time LIVE OBJECTS // are destroyed is during Shutdown. But Shutdown cleanup is handled through // the ManagedWndProcTracker and hence no work needs to happen here. PLEASE // NOTE that reintroducing any cleanup logic in here will conflict with the cleanup // logic in ManagedWndProcTracker and hence must be avoided. If this instance // has been disposed its GCHandle is released at the time and hence this object // is available for GC thereafter. Even in that case since all the cleanup has been // done during dispose there is no further cleanup required. // ///// /// Critical - It calls the critical DisposeImpl. // /// // [SecurityCritical] // ~HwndSubclass() // { // // In Shutdown, the finalizer is called on LIVE OBJECTS. // // // // So this method can be called. (even though we are pinned) // // If it is, we're shutting down so it's OK to force unhooking // // the subclass. // DisposeImpl(true); // } ////// Critical - It calls the critical DisposeImpl. /// PublicOK - Demands for AllWindows permission. /// [SecurityCritical] public virtual void Dispose() { SecurityHelper.DemandUIWindowPermission(); DisposeImpl(false); } ////// Critical - Calls the critical UnhookWindowProc. /// [SecurityCritical] private bool DisposeImpl(bool forceUnhook) { _hook = null; return UnhookWindowProc(forceUnhook); } ////// This method subclasses the specified window, such that the /// delegate specified to the constructor will be called to process /// the messages that are sent or posted to this window. /// /// /// The window to subclass. /// ////// An identifier that can be used to reference this instance of /// the HwndSubclass class in the static RequestDetach method. /// ////// Critical - It calls CriticalAttach. /// TreatAsSafe - Demands for AllWindows permission. /// [SecurityCritical, SecurityTreatAsSafe] internal IntPtr Attach(IntPtr hwnd) { SecurityHelper.DemandUIWindowPermission(); if (_bond != Bond.Unattached) throw new InvalidOperationException(SR.Get(SRID.HwndSubclassMultipleAttach)); return CriticalAttach( hwnd ) ; } ////// This method unsubclasses this HwndSubclass object from the window /// it previously subclassed. The HwndSubclass object is not thread /// safe, and should thus be called only by the thread that owns /// the window being unsubclassed. /// /// /// Whether or not the unsubclassing should be forced. Due to the /// way that Win32 implements window subclassing, it is not always /// possible to safely remove a window proc from the WNDPROC chain. /// However, the delegate will not be called again after this /// method returns. /// ////// Whether or not this HwndSubclass object was actually removed from /// the WNDPROC chain. /// ////// TreatAsSafe: Demands for all windows /// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] internal bool Detach(bool force) { SecurityHelper.DemandUIWindowPermission(); return CriticalDetach(force); } ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] internal bool CriticalDetach(bool force) { bool detached; // If we have already detached, return immediately. if(_bond == Bond.Detached || _bond == Bond.Unattached) { detached = true; } else { // When we detach, we simply make a note of it. _bond = Bond.Orphaned; // try to unhook the subclass detached = DisposeImpl(force); } return detached; } ////// This method sends a message to the window that is currently /// subclassed by this instance of the HwndSubclass class, in order /// to unsubclass the window. This is important if a different /// thread than the thread that owns the window wants to initiate /// the unsubclassing. /// /// /// Whether or not the unsubclassing should be forced. Due to the /// way that Win32 implements window subclassing, it is not always /// possible to safely remove a window proc from the WNDPROC chain. /// However, the delegate will not be called again after this /// method returns. /// ////// Nothing. /// ////// Critical - Probes _hwndAttached for value. /// TreatAsSafe - The RequestDetach overload we're calling is public and safe (demands UIWindowPermission). /// [SecurityCritical, SecurityTreatAsSafe] internal void RequestDetach(bool force) { // Let the static version do the work. if(_hwndAttached != IntPtr.Zero) { RequestDetach(_hwndAttached, (IntPtr) _gcHandle, force); } } ////// This method sends a message to the specified window in order to /// cause the specified bridge to unsubclass the window. This is /// important if a different thread than the thread that owns the /// window wants to initiate the unsubclassing. The HwndSubclass /// object must be identified by the value returned from Attach(). /// /// /// The window to unsubclass. /// /// /// The identifier of the subclass to unsubclass. /// /// /// Whether or not the unsubclassing should be forced. Due to the /// way that Win32 implements window subclassing, it is not always /// possible to safely remove a window proc from the WNDPROC chain. /// However, the delegate will not be called again after this /// method returns. /// ////// Nothing. /// ////// Critical - This touches the underlying windowing infrastructure; we may want to intercept windows-messages for security-related purposes in the future. /// In general we restrict access to window handles. /// TreatAsSafe - Demands UIWindowPermission. /// [SecurityCritical, SecurityTreatAsSafe] internal static void RequestDetach(IntPtr hwnd, IntPtr subclass, bool force) { SecurityHelper.DemandUIWindowPermission(); if(hwnd == IntPtr.Zero) { throw new ArgumentNullException("hwnd"); } if(subclass == IntPtr.Zero) { throw new ArgumentNullException("subclass"); } int iForce = force ? 1 : 0; UnsafeNativeMethods.UnsafeSendMessage(hwnd, DetachMessage, subclass, (IntPtr) iForce); } ////// This is the WNDPROC that gets inserted into the window's /// WNDPROC chain. It responds to various conditions that /// would cause this HwndSubclass object to unsubclass the window, /// and then calls the delegate specified to the HwndSubclass /// constructor to process the message. If the delegate does not /// handle the message, the message is then passed on down the /// WNDPROC chain for further processing. /// /// /// The window that this message was sent or posted to. /// /// /// The message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// ////// The value that is the result of processing the message. /// ////// Critical: this function elevates via unsafe native method calls /// [SecurityCritical] internal IntPtr SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) { IntPtr retval = IntPtr.Zero; bool handled = false; // If we are unattached and we receive a message, then we must have // been used as the original window proc. In this case, we insert // ourselves as if the original window proc had been DefWindowProc. // We pass in DefWndProcStub as a workaround for a bug in UxTheme on // Windows XP. For details see the comment on the DefWndProcWrapper method. if(_bond == Bond.Unattached) { HookWindowProc(hwnd, new NativeMethods.WndProc(SubclassWndProc), Marshal.GetFunctionPointerForDelegate(DefWndProcStub)); } else if(_bond == Bond.Detached) { throw new InvalidOperationException(); } IntPtr oldWndProc = _oldWndProc; // in case we get detached during this method if(msg == DetachMessage) { // We received our special message to detach. Make sure it is intended // for us by matching the bridge. if(wParam == IntPtr.Zero || wParam == (IntPtr)_gcHandle) { int param = (int)lParam; // 0 - normal, 1 - force, 2 - force and forward bool force = (param > 0); retval = CriticalDetach(force) ? new IntPtr(1) : IntPtr.Zero ; handled = (param < 2); } } else { // Pass this message to our delegate function. Do this under // the exception filter/handlers of the dispatcher for this thread. Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread); if(dispatcher != null && !dispatcher.HasShutdownFinished) { if (_dispatcherOperationCallback == null) _dispatcherOperationCallback = new DispatcherOperationCallback(this.DispatcherCallbackOperation); // _paramDispatcherCallbackOperation is a thread static member which should be reused to avoid // creating a new data structure every time we call DispatcherCallbackOperation // Cache the param locally in case of reentrance and set _paramDispatcherCallbackOperation to null so reentrancy calls will create a new param if (_paramDispatcherCallbackOperation == null) _paramDispatcherCallbackOperation = new DispatcherOperationCallbackParameter(); DispatcherOperationCallbackParameter param = _paramDispatcherCallbackOperation; _paramDispatcherCallbackOperation = null; param.hwnd = hwnd; param.msg = msg; param.wParam = wParam; param.lParam = lParam; //synchronous call object result = dispatcher.Invoke( DispatcherPriority.Send, _dispatcherOperationCallback, param); if (result != null) { handled = param.handled; retval = param.retVal; } // Restore _paramDispatcherCallbackOperation to the previous value so we will reuse it on the next call _paramDispatcherCallbackOperation = param; } // Handle WM_NCDESTROY explicitly to forcibly clean up. if(msg == NativeMethods.WM_NCDESTROY) { // The fact that we received this message means that we are // still in the call chain. This is our last chance to clean // up, and no other message should be received by this window // proc again. It is OK to force a cleanup now. CriticalDetach(true); // Always pass the WM_NCDESTROY message down the chain! handled = false; } } // If our window proc didn't handle this message, pass it on down the // chain. if(!handled) { retval = CallOldWindowProc(oldWndProc, hwnd, msg, wParam, lParam); } return retval; } // Perf bug: 1963989 // _paramDispatcherCallbackOperation is a thread static member which should be reused to avoid // creating a new data structure every time we DispatcherCallbackOperation is called // It also contains the return results (handled and retValue) from DispatcherCallbackOperation call ////// Critical: DispatcherOperationCallbackParameter contains hwnd, which is critical /// [SecurityCritical] [ThreadStatic] private static DispatcherOperationCallbackParameter _paramDispatcherCallbackOperation; // This class is used as a parameter and return result for DispatcherCallbackOperation call private class DispatcherOperationCallbackParameter { internal IntPtr hwnd, wParam, lParam, retVal; internal int msg; internal bool handled; } private DispatcherOperationCallback _dispatcherOperationCallback = null; ////// Critical: it calls GetWindowLongPtr(), which is Critical /// [ SecurityCritical ] internal IntPtr CriticalAttach( IntPtr hwnd ) { if(hwnd == IntPtr.Zero) { throw new ArgumentNullException("hwnd"); } if(_bond != Bond.Unattached) { throw new InvalidOperationException(); } NativeMethods.WndProc newWndProc = new NativeMethods.WndProc(SubclassWndProc); IntPtr oldWndProc = UnsafeNativeMethods.GetWindowLongPtr(new HandleRef(this,hwnd), NativeMethods.GWL_WNDPROC); HookWindowProc(hwnd, newWndProc, oldWndProc); // Return the GC handle as a unique identifier of this return (IntPtr) _gcHandle; } ////// Critical: This code is a callback into the dispatcher. It is present /// because under enforcements anonymous delagates throw a demand since /// it becomes a transparent calling critical for this particular call /// [SecurityCritical] private object DispatcherCallbackOperation(object o) { DispatcherOperationCallbackParameter param = (DispatcherOperationCallbackParameter)o; param.handled = false; param.retVal = IntPtr.Zero; if (_bond == Bond.Attached) { HwndWrapperHook hook= _hook.Target as HwndWrapperHook; if (hook != null) { // make the call param.retVal = hook(param.hwnd, param.msg, param.wParam, param.lParam, ref param.handled); } } return param; } ////// This method lets the user call the old WNDPROC, i.e /// the next WNDPROC in the chain directly. /// /// /// The WndProc to call. /// /// /// The window that this message was sent or posted to. /// /// /// The message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// ////// The value that is the result of processing the message. /// ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] IntPtr CallOldWindowProc(IntPtr oldWndProc, IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) { return UnsafeNativeMethods.CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam); } ////// Critical - it calls CriticalSetWindowLong() /// [SecurityCritical] private void HookWindowProc(IntPtr hwnd, NativeMethods.WndProc newWndProc, IntPtr oldWndProc) { _hwndAttached = hwnd; _hwndHandleRef = new HandleRef(null,_hwndAttached); _bond = Bond.Attached; _attachedWndProc = newWndProc; _oldWndProc = oldWndProc; IntPtr oldWndProc2 = (IntPtr)UnsafeNativeMethods.CriticalSetWindowLong(_hwndHandleRef, NativeMethods.GWL_WNDPROC, _attachedWndProc); // Track this window so that we can rip out the managed window proc // when the CLR shuts down. ManagedWndProcTracker.TrackHwndSubclass(this, _hwndAttached); } // This method should only be called from Dispose. Otherwise assumptions about the disposing/finalize state could be violated. // force - when true, remove this subclass from the WndProc chain regardless of // its current position. When false, remove this subclass only // if it is possible to do so without damaging other WndProcs // (i.e. only if this is at the head of the chain). // // Removing this subclass from the WndProc chain when it is not at the head // also removes all other WndProcs that appear before this one on the chain, // so is generally not appropriate. It is OK in the following situations: // a) in response to the WM_NCDESTROY message // b) in response to the AppDomainProcessExit event // c) in response to the AppDomainExit event // In cases (a) and (b) the HWND is being destroyed, so the earlier // WndProcs are no longer useful anyway. In case (c), we have to remove // all managed code from the chain lest it be called after it has been // removed from memory; removing earlier WndProcs is unfortunate, but // necessary. [Note that at AppDomainExit we remove all managed WndProcs, // regardless of which AppDomain they came from. There is room for // improvement here - we could remove only the ones belong to the AppDomain // that is exiting. This situation seems too unlikely to worry about in V1.] // // This method returns true if the subclass is no longer in the WndProc chain. // ////// Critical - Calls CriticalSetWindowLong() and GetWindowLongWndProc(). /// This touches the underlying windowing infrastructure; we may want to intercept windows-messages for security-related purposes in the future. /// In general we restrict access to window handles. /// [SecurityCritical] private bool UnhookWindowProc(bool force) { // if we're not in the WndProc chain, there's nothing to do if (_bond == Bond.Unattached || _bond == Bond.Detached) { return true; } // we'll remove ourselves from the chain if we're at the head, or if // the 'force' parameter was true. if (!force) { NativeMethods.WndProc currentWndProc = UnsafeNativeMethods.GetWindowLongWndProc(new HandleRef(this,_hwndAttached)); force = (currentWndProc == _attachedWndProc); } // if we're not unhooking, return and report if (!force) { return false; } // unhook from the tracker _bond = Bond.Orphaned; // ignore messages while we're unhooking ManagedWndProcTracker.UnhookHwndSubclass(this); // unhook, the Win32 way try { UnsafeNativeMethods.CriticalSetWindowLong(_hwndHandleRef, NativeMethods.GWL_WNDPROC, _oldWndProc); } catch (System.ComponentModel.Win32Exception e) { if (e.NativeErrorCode != 1400) // ERROR_INVALID_WINDOW_HANDLE { throw; } } // clear our state _bond = Bond.Detached; _oldWndProc = IntPtr.Zero; _attachedWndProc = null; _hwndAttached = IntPtr.Zero; _hwndHandleRef = new HandleRef(null,IntPtr.Zero); // un-Pin this object. // Note: the GC is free to collect this object at anytime // after we have freed this handle - that is, once all // other managed references go away. //AvDebug.Assert(_gcHandle.IsAllocated, "External GC handle has not been allocated."); if(null != _gcHandle) _gcHandle.Free(); return true; } ////// DefWndProcWrapper is a wrapper around DefWndProc. This is a workaround /// for WindowsSE bugs 124461 and 124455, which affect Windows XP SP2, Luna theme. /// /// HwndSubclass.SubclassWndProc is sometimes directly set as the window proc of a /// Window (HwndWrapper's constructor does this). When this happens it subclasses /// itself on the first message it receives. Since the old window proc is itself, /// it saves off DefWndProc as the old window proc instead. /// /// As described by the bugs and and KB article 319740, if we set DefWndProc /// as the window proc of the window, we'll leak 6 GDI region objects /// corresponding to the Luna-themed non-client area. /// /// The reason for this is that the kernel will flag that window as a server-side /// window. If the WndProc replacement happens in response to a WM_NCDESTROY message /// (as it does in the case where Avalon is creating and closing windows), the Shell /// won't clean up the regions. /// /// The fix is slated for XP SP3, so for now WPF is implementing a workaround: /// if we set the window proc to be a stub that calls into DefWndProc, the kernel /// won't set us as a server-side window. /// ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] private static IntPtr DefWndProcWrapper(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) { return UnsafeNativeMethods.CallWindowProc(DefWndProc, hwnd, msg, wParam, lParam); } // Message to cause a detach. // WPARAM=IntPtr returned from Attach(), or 0 to match all subclasses. // LPARAM= 0 - normal (unhook subclass if it is first on the chain) // 1 - force unhooking subclass from the chain // 2 - force, and forward message to next WndProc internal static readonly int DetachMessage; private enum Bond { Unattached, Attached, Detached, Orphaned } ////// This is a delegate that points to DefWndProcWrapper. It is set into /// a Window's WndProc instead of DefWndProc in order to work around a bug. /// See the comment on DefWndProcWrapper. /// /// By instantiating this delegate as a static variable we ensure that /// it will remain alive long enough to process messages. /// ////// Critical: This will expose DefWndProc /// [SecurityCritical] private static NativeMethods.WndProc DefWndProcStub = new NativeMethods.WndProc(DefWndProcWrapper); ////// Critical: This will expose wndproc /// [SecurityCritical] private static IntPtr DefWndProc; ////// Critical: This will expose the intptr of the window /// [SecurityCritical] private IntPtr _hwndAttached; ////// Critical: This will expose window handle /// [SecurityCritical] private HandleRef _hwndHandleRef; ////// Critical: This will expose wndproc /// [SecurityCritical] private NativeMethods.WndProc _attachedWndProc; ////// Critical: This will expose wndproc /// [SecurityCritical] private IntPtr _oldWndProc; private Bond _bond; private GCHandle _gcHandle; ////// Critical: This will expose hook /// [SecurityCritical] private WeakReference _hook; }; } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Runtime.InteropServices; using System.Security.Permissions; using MS.Internal; using MS.Utility; using System.Windows; using System.Windows.Threading; using System.Security; // CAS using System.Threading; // Thread // The SecurityHelper class differs between assemblies and could not actually be // shared, so it is duplicated across namespaces to prevent name collision. #if WINDOWS_BASE using MS.Internal.WindowsBase; #elif PRESENTATION_CORE using MS.Internal.PresentationCore; #elif PRESENTATIONFRAMEWORK using MS.Internal.PresentationFramework; #elif DRT using MS.Internal.Drt; #else #error Attempt to use a class (duplicated across multiple namespaces) from an unknown assembly. #endif namespace MS.Win32 { ////// The HwndSubclass class provides a managed way to subclass an existing /// HWND. This class inserts itself into the WNDPROC chain for the /// window, and will call a specified delegate to process the window /// messages that arrive. The delegate has a slightly different /// signature than a WNDPROC to be more specific about whether the /// message was handled or not. If the message was not handled by the /// delegate, this class passes the message on down the WNDPROC chain. /// /// To use this class properly, simply: /// 1) Create an instance of the HwndSubclass class and pass the delegate /// to the constructor. /// 2) Call Attach(HWND) to subclass an existing window. /// 3) Call Detach(false) to unsubclass the window when you are done. /// /// You can also just call RequestDetach() to send a message to the /// window that will cause the HwndSubclass to detach itself. This is /// important if you are on a different thread, as the HwndSubclass class /// is not thread safe and will be operated on by the thread that owns /// the window. /// ///Not available to partial trust callers [FriendAccessAllowed] // Built into Base, also used by Framework internal class HwndSubclass : IDisposable { ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] static HwndSubclass() { DetachMessage = UnsafeNativeMethods.RegisterWindowMessage("HwndSubclass.DetachMessage"); if (DetachMessage == 0) { throw new System.ComponentModel.Win32Exception(); } // Go find the address of DefWindowProc. IntPtr hModuleUser32 = UnsafeNativeMethods.GetModuleHandle(ExternDll.User32); IntPtr address = UnsafeNativeMethods.GetProcAddress(new HandleRef(null,hModuleUser32), "DefWindowProcW"); DefWndProc = address; } ////// This HwndSubclass constructor binds the HwndSubclass object to the /// specified delegate. This delegate will be called to process /// the messages that are sent or posted to the window. /// /// /// The delegate that will be called to process the messages that /// are sent or posted to the window. /// ////// Nothing. /// ////// Critical: This code creates an object that is not allowed in partial trust /// [SecurityCritical] internal HwndSubclass(HwndWrapperHook hook) { if(hook == null) { throw new ArgumentNullException("hook"); } _bond = Bond.Unattached; _hook = new WeakReference(hook); // Allocate a GC handle so that we won't be collected, even if all // references to us get released. This is because a component outside // of the managed code (ie. the window we are subclassing) still holds // a reference to us - just not a reference that the GC recognizes. _gcHandle = GCHandle.Alloc(this); } // This is LIVE OBJECT because it has a GCHandle. The only time LIVE OBJECTS // are destroyed is during Shutdown. But Shutdown cleanup is handled through // the ManagedWndProcTracker and hence no work needs to happen here. PLEASE // NOTE that reintroducing any cleanup logic in here will conflict with the cleanup // logic in ManagedWndProcTracker and hence must be avoided. If this instance // has been disposed its GCHandle is released at the time and hence this object // is available for GC thereafter. Even in that case since all the cleanup has been // done during dispose there is no further cleanup required. // ///// /// Critical - It calls the critical DisposeImpl. // /// // [SecurityCritical] // ~HwndSubclass() // { // // In Shutdown, the finalizer is called on LIVE OBJECTS. // // // // So this method can be called. (even though we are pinned) // // If it is, we're shutting down so it's OK to force unhooking // // the subclass. // DisposeImpl(true); // } ////// Critical - It calls the critical DisposeImpl. /// PublicOK - Demands for AllWindows permission. /// [SecurityCritical] public virtual void Dispose() { SecurityHelper.DemandUIWindowPermission(); DisposeImpl(false); } ////// Critical - Calls the critical UnhookWindowProc. /// [SecurityCritical] private bool DisposeImpl(bool forceUnhook) { _hook = null; return UnhookWindowProc(forceUnhook); } ////// This method subclasses the specified window, such that the /// delegate specified to the constructor will be called to process /// the messages that are sent or posted to this window. /// /// /// The window to subclass. /// ////// An identifier that can be used to reference this instance of /// the HwndSubclass class in the static RequestDetach method. /// ////// Critical - It calls CriticalAttach. /// TreatAsSafe - Demands for AllWindows permission. /// [SecurityCritical, SecurityTreatAsSafe] internal IntPtr Attach(IntPtr hwnd) { SecurityHelper.DemandUIWindowPermission(); if (_bond != Bond.Unattached) throw new InvalidOperationException(SR.Get(SRID.HwndSubclassMultipleAttach)); return CriticalAttach( hwnd ) ; } ////// This method unsubclasses this HwndSubclass object from the window /// it previously subclassed. The HwndSubclass object is not thread /// safe, and should thus be called only by the thread that owns /// the window being unsubclassed. /// /// /// Whether or not the unsubclassing should be forced. Due to the /// way that Win32 implements window subclassing, it is not always /// possible to safely remove a window proc from the WNDPROC chain. /// However, the delegate will not be called again after this /// method returns. /// ////// Whether or not this HwndSubclass object was actually removed from /// the WNDPROC chain. /// ////// TreatAsSafe: Demands for all windows /// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] internal bool Detach(bool force) { SecurityHelper.DemandUIWindowPermission(); return CriticalDetach(force); } ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] internal bool CriticalDetach(bool force) { bool detached; // If we have already detached, return immediately. if(_bond == Bond.Detached || _bond == Bond.Unattached) { detached = true; } else { // When we detach, we simply make a note of it. _bond = Bond.Orphaned; // try to unhook the subclass detached = DisposeImpl(force); } return detached; } ////// This method sends a message to the window that is currently /// subclassed by this instance of the HwndSubclass class, in order /// to unsubclass the window. This is important if a different /// thread than the thread that owns the window wants to initiate /// the unsubclassing. /// /// /// Whether or not the unsubclassing should be forced. Due to the /// way that Win32 implements window subclassing, it is not always /// possible to safely remove a window proc from the WNDPROC chain. /// However, the delegate will not be called again after this /// method returns. /// ////// Nothing. /// ////// Critical - Probes _hwndAttached for value. /// TreatAsSafe - The RequestDetach overload we're calling is public and safe (demands UIWindowPermission). /// [SecurityCritical, SecurityTreatAsSafe] internal void RequestDetach(bool force) { // Let the static version do the work. if(_hwndAttached != IntPtr.Zero) { RequestDetach(_hwndAttached, (IntPtr) _gcHandle, force); } } ////// This method sends a message to the specified window in order to /// cause the specified bridge to unsubclass the window. This is /// important if a different thread than the thread that owns the /// window wants to initiate the unsubclassing. The HwndSubclass /// object must be identified by the value returned from Attach(). /// /// /// The window to unsubclass. /// /// /// The identifier of the subclass to unsubclass. /// /// /// Whether or not the unsubclassing should be forced. Due to the /// way that Win32 implements window subclassing, it is not always /// possible to safely remove a window proc from the WNDPROC chain. /// However, the delegate will not be called again after this /// method returns. /// ////// Nothing. /// ////// Critical - This touches the underlying windowing infrastructure; we may want to intercept windows-messages for security-related purposes in the future. /// In general we restrict access to window handles. /// TreatAsSafe - Demands UIWindowPermission. /// [SecurityCritical, SecurityTreatAsSafe] internal static void RequestDetach(IntPtr hwnd, IntPtr subclass, bool force) { SecurityHelper.DemandUIWindowPermission(); if(hwnd == IntPtr.Zero) { throw new ArgumentNullException("hwnd"); } if(subclass == IntPtr.Zero) { throw new ArgumentNullException("subclass"); } int iForce = force ? 1 : 0; UnsafeNativeMethods.UnsafeSendMessage(hwnd, DetachMessage, subclass, (IntPtr) iForce); } ////// This is the WNDPROC that gets inserted into the window's /// WNDPROC chain. It responds to various conditions that /// would cause this HwndSubclass object to unsubclass the window, /// and then calls the delegate specified to the HwndSubclass /// constructor to process the message. If the delegate does not /// handle the message, the message is then passed on down the /// WNDPROC chain for further processing. /// /// /// The window that this message was sent or posted to. /// /// /// The message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// ////// The value that is the result of processing the message. /// ////// Critical: this function elevates via unsafe native method calls /// [SecurityCritical] internal IntPtr SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) { IntPtr retval = IntPtr.Zero; bool handled = false; // If we are unattached and we receive a message, then we must have // been used as the original window proc. In this case, we insert // ourselves as if the original window proc had been DefWindowProc. // We pass in DefWndProcStub as a workaround for a bug in UxTheme on // Windows XP. For details see the comment on the DefWndProcWrapper method. if(_bond == Bond.Unattached) { HookWindowProc(hwnd, new NativeMethods.WndProc(SubclassWndProc), Marshal.GetFunctionPointerForDelegate(DefWndProcStub)); } else if(_bond == Bond.Detached) { throw new InvalidOperationException(); } IntPtr oldWndProc = _oldWndProc; // in case we get detached during this method if(msg == DetachMessage) { // We received our special message to detach. Make sure it is intended // for us by matching the bridge. if(wParam == IntPtr.Zero || wParam == (IntPtr)_gcHandle) { int param = (int)lParam; // 0 - normal, 1 - force, 2 - force and forward bool force = (param > 0); retval = CriticalDetach(force) ? new IntPtr(1) : IntPtr.Zero ; handled = (param < 2); } } else { // Pass this message to our delegate function. Do this under // the exception filter/handlers of the dispatcher for this thread. Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread); if(dispatcher != null && !dispatcher.HasShutdownFinished) { if (_dispatcherOperationCallback == null) _dispatcherOperationCallback = new DispatcherOperationCallback(this.DispatcherCallbackOperation); // _paramDispatcherCallbackOperation is a thread static member which should be reused to avoid // creating a new data structure every time we call DispatcherCallbackOperation // Cache the param locally in case of reentrance and set _paramDispatcherCallbackOperation to null so reentrancy calls will create a new param if (_paramDispatcherCallbackOperation == null) _paramDispatcherCallbackOperation = new DispatcherOperationCallbackParameter(); DispatcherOperationCallbackParameter param = _paramDispatcherCallbackOperation; _paramDispatcherCallbackOperation = null; param.hwnd = hwnd; param.msg = msg; param.wParam = wParam; param.lParam = lParam; //synchronous call object result = dispatcher.Invoke( DispatcherPriority.Send, _dispatcherOperationCallback, param); if (result != null) { handled = param.handled; retval = param.retVal; } // Restore _paramDispatcherCallbackOperation to the previous value so we will reuse it on the next call _paramDispatcherCallbackOperation = param; } // Handle WM_NCDESTROY explicitly to forcibly clean up. if(msg == NativeMethods.WM_NCDESTROY) { // The fact that we received this message means that we are // still in the call chain. This is our last chance to clean // up, and no other message should be received by this window // proc again. It is OK to force a cleanup now. CriticalDetach(true); // Always pass the WM_NCDESTROY message down the chain! handled = false; } } // If our window proc didn't handle this message, pass it on down the // chain. if(!handled) { retval = CallOldWindowProc(oldWndProc, hwnd, msg, wParam, lParam); } return retval; } // Perf bug: 1963989 // _paramDispatcherCallbackOperation is a thread static member which should be reused to avoid // creating a new data structure every time we DispatcherCallbackOperation is called // It also contains the return results (handled and retValue) from DispatcherCallbackOperation call ////// Critical: DispatcherOperationCallbackParameter contains hwnd, which is critical /// [SecurityCritical] [ThreadStatic] private static DispatcherOperationCallbackParameter _paramDispatcherCallbackOperation; // This class is used as a parameter and return result for DispatcherCallbackOperation call private class DispatcherOperationCallbackParameter { internal IntPtr hwnd, wParam, lParam, retVal; internal int msg; internal bool handled; } private DispatcherOperationCallback _dispatcherOperationCallback = null; ////// Critical: it calls GetWindowLongPtr(), which is Critical /// [ SecurityCritical ] internal IntPtr CriticalAttach( IntPtr hwnd ) { if(hwnd == IntPtr.Zero) { throw new ArgumentNullException("hwnd"); } if(_bond != Bond.Unattached) { throw new InvalidOperationException(); } NativeMethods.WndProc newWndProc = new NativeMethods.WndProc(SubclassWndProc); IntPtr oldWndProc = UnsafeNativeMethods.GetWindowLongPtr(new HandleRef(this,hwnd), NativeMethods.GWL_WNDPROC); HookWindowProc(hwnd, newWndProc, oldWndProc); // Return the GC handle as a unique identifier of this return (IntPtr) _gcHandle; } ////// Critical: This code is a callback into the dispatcher. It is present /// because under enforcements anonymous delagates throw a demand since /// it becomes a transparent calling critical for this particular call /// [SecurityCritical] private object DispatcherCallbackOperation(object o) { DispatcherOperationCallbackParameter param = (DispatcherOperationCallbackParameter)o; param.handled = false; param.retVal = IntPtr.Zero; if (_bond == Bond.Attached) { HwndWrapperHook hook= _hook.Target as HwndWrapperHook; if (hook != null) { // make the call param.retVal = hook(param.hwnd, param.msg, param.wParam, param.lParam, ref param.handled); } } return param; } ////// This method lets the user call the old WNDPROC, i.e /// the next WNDPROC in the chain directly. /// /// /// The WndProc to call. /// /// /// The window that this message was sent or posted to. /// /// /// The message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// /// /// A parameter for the message that was sent or posted. /// ////// The value that is the result of processing the message. /// ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] IntPtr CallOldWindowProc(IntPtr oldWndProc, IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam) { return UnsafeNativeMethods.CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam); } ////// Critical - it calls CriticalSetWindowLong() /// [SecurityCritical] private void HookWindowProc(IntPtr hwnd, NativeMethods.WndProc newWndProc, IntPtr oldWndProc) { _hwndAttached = hwnd; _hwndHandleRef = new HandleRef(null,_hwndAttached); _bond = Bond.Attached; _attachedWndProc = newWndProc; _oldWndProc = oldWndProc; IntPtr oldWndProc2 = (IntPtr)UnsafeNativeMethods.CriticalSetWindowLong(_hwndHandleRef, NativeMethods.GWL_WNDPROC, _attachedWndProc); // Track this window so that we can rip out the managed window proc // when the CLR shuts down. ManagedWndProcTracker.TrackHwndSubclass(this, _hwndAttached); } // This method should only be called from Dispose. Otherwise assumptions about the disposing/finalize state could be violated. // force - when true, remove this subclass from the WndProc chain regardless of // its current position. When false, remove this subclass only // if it is possible to do so without damaging other WndProcs // (i.e. only if this is at the head of the chain). // // Removing this subclass from the WndProc chain when it is not at the head // also removes all other WndProcs that appear before this one on the chain, // so is generally not appropriate. It is OK in the following situations: // a) in response to the WM_NCDESTROY message // b) in response to the AppDomainProcessExit event // c) in response to the AppDomainExit event // In cases (a) and (b) the HWND is being destroyed, so the earlier // WndProcs are no longer useful anyway. In case (c), we have to remove // all managed code from the chain lest it be called after it has been // removed from memory; removing earlier WndProcs is unfortunate, but // necessary. [Note that at AppDomainExit we remove all managed WndProcs, // regardless of which AppDomain they came from. There is room for // improvement here - we could remove only the ones belong to the AppDomain // that is exiting. This situation seems too unlikely to worry about in V1.] // // This method returns true if the subclass is no longer in the WndProc chain. // ////// Critical - Calls CriticalSetWindowLong() and GetWindowLongWndProc(). /// This touches the underlying windowing infrastructure; we may want to intercept windows-messages for security-related purposes in the future. /// In general we restrict access to window handles. /// [SecurityCritical] private bool UnhookWindowProc(bool force) { // if we're not in the WndProc chain, there's nothing to do if (_bond == Bond.Unattached || _bond == Bond.Detached) { return true; } // we'll remove ourselves from the chain if we're at the head, or if // the 'force' parameter was true. if (!force) { NativeMethods.WndProc currentWndProc = UnsafeNativeMethods.GetWindowLongWndProc(new HandleRef(this,_hwndAttached)); force = (currentWndProc == _attachedWndProc); } // if we're not unhooking, return and report if (!force) { return false; } // unhook from the tracker _bond = Bond.Orphaned; // ignore messages while we're unhooking ManagedWndProcTracker.UnhookHwndSubclass(this); // unhook, the Win32 way try { UnsafeNativeMethods.CriticalSetWindowLong(_hwndHandleRef, NativeMethods.GWL_WNDPROC, _oldWndProc); } catch (System.ComponentModel.Win32Exception e) { if (e.NativeErrorCode != 1400) // ERROR_INVALID_WINDOW_HANDLE { throw; } } // clear our state _bond = Bond.Detached; _oldWndProc = IntPtr.Zero; _attachedWndProc = null; _hwndAttached = IntPtr.Zero; _hwndHandleRef = new HandleRef(null,IntPtr.Zero); // un-Pin this object. // Note: the GC is free to collect this object at anytime // after we have freed this handle - that is, once all // other managed references go away. //AvDebug.Assert(_gcHandle.IsAllocated, "External GC handle has not been allocated."); if(null != _gcHandle) _gcHandle.Free(); return true; } ////// DefWndProcWrapper is a wrapper around DefWndProc. This is a workaround /// for WindowsSE bugs 124461 and 124455, which affect Windows XP SP2, Luna theme. /// /// HwndSubclass.SubclassWndProc is sometimes directly set as the window proc of a /// Window (HwndWrapper's constructor does this). When this happens it subclasses /// itself on the first message it receives. Since the old window proc is itself, /// it saves off DefWndProc as the old window proc instead. /// /// As described by the bugs and and KB article 319740, if we set DefWndProc /// as the window proc of the window, we'll leak 6 GDI region objects /// corresponding to the Luna-themed non-client area. /// /// The reason for this is that the kernel will flag that window as a server-side /// window. If the WndProc replacement happens in response to a WM_NCDESTROY message /// (as it does in the case where Avalon is creating and closing windows), the Shell /// won't clean up the regions. /// /// The fix is slated for XP SP3, so for now WPF is implementing a workaround: /// if we set the window proc to be a stub that calls into DefWndProc, the kernel /// won't set us as a server-side window. /// ////// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical] private static IntPtr DefWndProcWrapper(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) { return UnsafeNativeMethods.CallWindowProc(DefWndProc, hwnd, msg, wParam, lParam); } // Message to cause a detach. // WPARAM=IntPtr returned from Attach(), or 0 to match all subclasses. // LPARAM= 0 - normal (unhook subclass if it is first on the chain) // 1 - force unhooking subclass from the chain // 2 - force, and forward message to next WndProc internal static readonly int DetachMessage; private enum Bond { Unattached, Attached, Detached, Orphaned } ////// This is a delegate that points to DefWndProcWrapper. It is set into /// a Window's WndProc instead of DefWndProc in order to work around a bug. /// See the comment on DefWndProcWrapper. /// /// By instantiating this delegate as a static variable we ensure that /// it will remain alive long enough to process messages. /// ////// Critical: This will expose DefWndProc /// [SecurityCritical] private static NativeMethods.WndProc DefWndProcStub = new NativeMethods.WndProc(DefWndProcWrapper); ////// Critical: This will expose wndproc /// [SecurityCritical] private static IntPtr DefWndProc; ////// Critical: This will expose the intptr of the window /// [SecurityCritical] private IntPtr _hwndAttached; ////// Critical: This will expose window handle /// [SecurityCritical] private HandleRef _hwndHandleRef; ////// Critical: This will expose wndproc /// [SecurityCritical] private NativeMethods.WndProc _attachedWndProc; ////// Critical: This will expose wndproc /// [SecurityCritical] private IntPtr _oldWndProc; private Bond _bond; private GCHandle _gcHandle; ////// Critical: This will expose hook /// [SecurityCritical] private WeakReference _hook; }; } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ProfileManager.cs
- XmlHierarchicalDataSourceView.cs
- EnumUnknown.cs
- DPCustomTypeDescriptor.cs
- TemplatedMailWebEventProvider.cs
- Stack.cs
- RankException.cs
- XmlReader.cs
- ReadOnlyDictionary.cs
- TextRenderer.cs
- VisualStyleInformation.cs
- _SafeNetHandles.cs
- ImportCatalogPart.cs
- ElementNotAvailableException.cs
- WindowsListViewGroupHelper.cs
- ConstraintEnumerator.cs
- EventProviderWriter.cs
- DocComment.cs
- HwndPanningFeedback.cs
- ProfileGroupSettingsCollection.cs
- FilterQueryOptionExpression.cs
- Thread.cs
- DataGridViewDataConnection.cs
- UnionCodeGroup.cs
- PageSetupDialog.cs
- MenuAutoFormat.cs
- InkCollectionBehavior.cs
- ChangeInterceptorAttribute.cs
- OletxTransactionManager.cs
- TextContainerChangeEventArgs.cs
- SrgsText.cs
- SqlTransaction.cs
- ArraySegment.cs
- WebPartChrome.cs
- Formatter.cs
- ScrollPattern.cs
- _Connection.cs
- EndPoint.cs
- ReadOnlyTernaryTree.cs
- __Error.cs
- WebDisplayNameAttribute.cs
- DropShadowEffect.cs
- XmlEncodedRawTextWriter.cs
- KeyGesture.cs
- GeneralTransformCollection.cs
- Int32CAMarshaler.cs
- TcpSocketManager.cs
- Context.cs
- SystemColors.cs
- NonSerializedAttribute.cs
- PixelFormats.cs
- SponsorHelper.cs
- ClassGenerator.cs
- ViewGenerator.cs
- Vector3DCollection.cs
- DataGridViewCellEventArgs.cs
- VisualStateManager.cs
- BrowserCapabilitiesCodeGenerator.cs
- CollectionBuilder.cs
- ServiceContractListItemList.cs
- ConfigXmlCDataSection.cs
- DNS.cs
- PresentationAppDomainManager.cs
- DataGridViewRowHeightInfoNeededEventArgs.cs
- TypeHelpers.cs
- NCryptNative.cs
- RichListBox.cs
- ExceptionUtil.cs
- Crc32.cs
- HwndTarget.cs
- ApplicationActivator.cs
- ComponentCollection.cs
- ToggleButtonAutomationPeer.cs
- BindingValueChangedEventArgs.cs
- PersonalizablePropertyEntry.cs
- XsdBuilder.cs
- Avt.cs
- HttpInputStream.cs
- ControlTemplate.cs
- RegexGroup.cs
- RegexNode.cs
- ComPlusSynchronizationContext.cs
- NullableDoubleAverageAggregationOperator.cs
- HandledMouseEvent.cs
- ContextBase.cs
- Selection.cs
- CollectionContainer.cs
- WhitespaceReader.cs
- PageAdapter.cs
- MarginsConverter.cs
- SafeNativeMethodsMilCoreApi.cs
- ComboBox.cs
- FixedSOMPageConstructor.cs
- XamlReaderHelper.cs
- EntryPointNotFoundException.cs
- LOSFormatter.cs
- PathFigure.cs
- ButtonFieldBase.cs
- SemanticBasicElement.cs
- ContractValidationHelper.cs