了解最新技术文章
我为 Microsoft Office 开发了某 .NET 加载项,它使用 Windows 设置子类操作。在客户同时运行我的加载项和其他加载项(例如,think cell)时,Office 出现故障。
此问题通常由 NativeWindow.AssignHandle
/ NativeWindow.ReleaseHandle
的子类设置操作所致。
相反,请按照 Microsoft 的建议 P/调用至 Comctl32.dll's
SetWindowSubclass
和 RemoveWindowSubclass
,如该 Microsoft 文档页中所述。要对您的项目进行此更改,可以在使用 NativeWindow
设置子类操作的地方将 NativeWindow
替换为 thinkcell.SubclassedWindow.cs
。
using System; using System.Diagnostics; namespace thinkcell { internal enum BOOL : int { FALSE = 0, TRUE = 1, } internal static partial class ComCtl32 { public delegate IntPtr SUBCLASSPROC( IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, UIntPtr uIdSubclass, UIntPtr dwRefData ); [System.Runtime.InteropServices.DllImport("comctl32.dll", ExactSpelling = true)] public static extern BOOL SetWindowSubclass( IntPtr hWnd, IntPtr pfnSubclass, UIntPtr uIdSubclass, UIntPtr dwRefData ); [System.Runtime.InteropServices.DllImport("comctl32.dll", ExactSpelling = true)] public static extern BOOL RemoveWindowSubclass( IntPtr hWnd, IntPtr pfnSubclass, UIntPtr uIdSubclass ); [System.Runtime.InteropServices.DllImport("comctl32.dll", ExactSpelling = true)] public static extern IntPtr DefSubclassProc( IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam ); } class SubclassedWindow : MarshalByRefObject, System.Windows.Forms.IWin32Window { // prevents collection of SubclassedWindow that is still in use static private System.Collections.Generic.HashSet<SubclassedWindow> _instancesInUse = new System.Collections.Generic.HashSet(); // The number of uses we still have for this instances: // - some window attached, or // - inside a window procedure private int _uses = 0; // Our window procedure delegate private ComCtl32.SUBCLASSPROC _windowProc; // The native handle for our delegate private IntPtr _windowProcHandle; static SubclassedWindow() { AppDomain.CurrentDomain.ProcessExit += OnShutdown; } public SubclassedWindow() { _windowProc = new ComCtl32.SUBCLASSPROC(Callback); _windowProcHandle = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(_windowProc); } /// <summary> /// Gets the handle for this window. /// </summary> public IntPtr Handle { get; private set; } /// <summary> /// Assigns a handle to this <see cref="NativeWindow"/> instance. /// </summary> public void AssignHandle(IntPtr handle) { CheckReleased(); Debug.Assert(handle != IntPtr.Zero, "handle is 0"); if (0 == _uses) { lock(_instancesInUse) { _instancesInUse.Add(this); } } // else may happen if handle gets reassigned inside WndProc. // This is legal after any call to DefWndProc. ++_uses; Handle = handle; ComCtl32.SetWindowSubclass(handle, _windowProcHandle, UIntPtr.Zero, UIntPtr.Zero); OnHandleChange(); } /// <summary> /// 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. /// </summary> private IntPtr Callback( IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, UIntPtr uIdSubclass, UIntPtr dwRefData ) { Debug.Assert(0 < _uses); ++_uses; try { var m = System.Windows.Forms.Message.Create(hWnd, msg, wParam, lParam); WndProc(ref m); return m.Result; } catch (Exception e) { OnThreadException(e); return IntPtr.Zero; } finally { if (msg == 0x82/*WM_NCDESTROY*/ && Handle != IntPtr.Zero) { InternalReleaseHandle(); } if (0 == --_uses) { lock (_instancesInUse) { _instancesInUse.Remove(this); } } } } /// <summary> /// Raises an exception if the window handle is not zero. /// </summary> private void CheckReleased() { if (Handle != IntPtr.Zero) { throw new InvalidOperationException("Window handle already exists."); } } /// <summary> /// Invokes the default window procedure associated with this Window. It is /// an error to call this method when the Handle property is zero. /// </summary> public void DefWndProc(ref System.Windows.Forms.Message m) { Debug.Assert(m.HWnd==Handle, "SubclassedWindow is not attached to the window m is addressed to."); m.Result = ComCtl32.DefSubclassProc(m.HWnd, m.Msg, m.WParam, m.LParam); } /// <summary> /// Specifies a notification method that is called when the handle for a /// window is changed. /// </summary> protected virtual void OnHandleChange() { } /// <summary> /// 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 /// </summary> [System.Runtime.ConstrainedExecution.PrePrepareMethod] private static void OnShutdown(object sender, EventArgs e) { // No lock because access here should be race-free, no concurrent SubclassedWindow.AttachHandle/ReleaseHandle // should happen while shutting down. Debug.Assert(0 == _instancesInUse.Count); } /// <summary> /// When overridden in a derived class, manages an unhandled thread exception. /// </summary> protected virtual void OnThreadException(Exception e) { } private void InternalReleaseHandle() { Debug.Assert(Handle != IntPtr.Zero); ComCtl32.RemoveWindowSubclass(Handle, _windowProcHandle, UIntPtr.Zero); Handle = IntPtr.Zero; OnHandleChange(); --_uses; } /// <summary> /// Releases the handle associated with this window. /// </summary> public void ReleaseHandle() { if (Handle != IntPtr.Zero) { InternalReleaseHandle(); if (0 == _uses) { lock (_instancesInUse) { _instancesInUse.Remove(this); } } } } /// <summary> /// Invokes the default window procedure associated with this window. /// </summary> protected virtual void WndProc(ref System.Windows.Forms.Message m) { DefWndProc(ref m); } } }
下载 SubclassedWindow.cs
原始 NativeWindow
也可以创建窗口,但是很少在 NativeWindow
的同一个实例中将其与设置子类操作结合使用。
thinkcell.SubclassedWindow
并非线程安全,但是设置子类操作和消息处理通常在同一线程中进行。
24小时免费咨询
请输入您的联系电话,座机请加区号