Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Shared / MS / Win32 / HwndSubclass.cs / 4 / 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] public 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); } ~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. Dispose(true); } public virtual void Dispose() { Dispose(false); } ////// Critical: It accesses the hooks /// TreatAsSafe: setting them to null and not exposing them is safe /// [SecurityCritical, SecurityTreatAsSafe] private bool Dispose(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 all windows /// [SecurityCritical, SecurityTreatAsSafe] public 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] public 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 = Dispose(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. /// ////// TreatAsSafe: Does not expose the hwnd /// Critical: Probes _hwndAttached for value /// [SecurityCritical, SecurityTreatAsSafe] public 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. /// ////// TreatAsSafe: Demands for all windows code /// Critical: Elevates by calling an UnsafeNativeMethod /// [SecurityCritical, SecurityTreatAsSafe ] public 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.SendMessage(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] public 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 { public IntPtr hwnd, wParam, lParam, retVal; public int msg; public 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 - it calls CriticalSetWindowLong() and GetWindowLongWndProc() /// TreatAsSafe - no input params, no exposed data, just removing ourselves from window proc chain /// [SecurityCritical, SecurityTreatAsSafe] 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
- CellParagraph.cs
- XmlSchemaSimpleTypeList.cs
- dsa.cs
- XsdBuildProvider.cs
- ColorMap.cs
- ProcessThreadCollection.cs
- DeflateStream.cs
- ItemList.cs
- DataSourceXmlClassAttribute.cs
- CacheMemory.cs
- SqlDataRecord.cs
- NonParentingControl.cs
- COM2TypeInfoProcessor.cs
- InternalConfigRoot.cs
- MimeTextImporter.cs
- AppSettingsExpressionBuilder.cs
- SymbolType.cs
- LifetimeServices.cs
- StaticTextPointer.cs
- IncrementalReadDecoders.cs
- Environment.cs
- NativeObjectSecurity.cs
- ProfileSection.cs
- PathFigureCollection.cs
- IPEndPoint.cs
- NavigationEventArgs.cs
- ColorConvertedBitmapExtension.cs
- Parallel.cs
- DateTimeFormatInfoScanner.cs
- metadatamappinghashervisitor.cs
- PolicyImporterElementCollection.cs
- ClientSettings.cs
- SimpleBitVector32.cs
- Regex.cs
- SortFieldComparer.cs
- ListViewUpdateEventArgs.cs
- autovalidator.cs
- HtmlInputControl.cs
- StringHelper.cs
- ContentPathSegment.cs
- SQLGuidStorage.cs
- XmlSchemaComplexContent.cs
- WindowsListBox.cs
- FixedTextContainer.cs
- WebSysDefaultValueAttribute.cs
- AdPostCacheSubstitution.cs
- IPCCacheManager.cs
- ParseHttpDate.cs
- ReferencedCollectionType.cs
- OracleNumber.cs
- AttachmentCollection.cs
- HideDisabledControlAdapter.cs
- PeerName.cs
- WindowsImpersonationContext.cs
- ClientEventManager.cs
- DataGridViewCellConverter.cs
- util.cs
- QueryConverter.cs
- ParseElement.cs
- ByteStreamBufferedMessageData.cs
- EntitySqlQueryBuilder.cs
- WindowsBrush.cs
- DummyDataSource.cs
- CollectionCodeDomSerializer.cs
- DrawToolTipEventArgs.cs
- TimerElapsedEvenArgs.cs
- PagedDataSource.cs
- IItemContainerGenerator.cs
- XmlToDatasetMap.cs
- SpotLight.cs
- Wizard.cs
- FileCodeGroup.cs
- RuleElement.cs
- WebContext.cs
- ImageAnimator.cs
- TraceInternal.cs
- HttpApplication.cs
- GridViewRowPresenterBase.cs
- DataKey.cs
- DbMetaDataCollectionNames.cs
- ObjectAnimationUsingKeyFrames.cs
- SerialReceived.cs
- CopyNamespacesAction.cs
- EncryptedKeyIdentifierClause.cs
- SecUtil.cs
- JournalEntryStack.cs
- Math.cs
- MembershipSection.cs
- ProfileSettings.cs
- ContactManager.cs
- RegexTree.cs
- TdsEnums.cs
- GeneralTransform3DTo2DTo3D.cs
- XmlUtf8RawTextWriter.cs
- HtmlInputFile.cs
- TagNameToTypeMapper.cs
- HttpRuntimeSection.cs
- HtmlInputRadioButton.cs
- FloaterBaseParagraph.cs
- PolicyException.cs