【问题标题】:Use of NativeWindow for ComboBox causes exception in Dispose-method将 NativeWindow 用于 ComboBox 会导致 Dispose 方法出现异常
【发布时间】:2012-10-10 04:16:34
【问题描述】:

在 C# Windows.Forms 中,我想截取组合框的粘贴窗口消息。由于这不能通过覆盖组合框的 WndProc 方法来实现,因为我需要覆盖组合框内文本框的 WndProc,所以我决定创建一个 NativeWindow 类型的自定义类来覆盖 WndProc。当组合框句柄被破坏时,我分配句柄并释放它。但是,当调用组合框的 Dispose 时,问题是我得到一个 InvalidOperationException 表示发生了无效的跨线程操作,并且组合框是从创建它的线程以外的线程访问的。任何想法这里出了什么问题?

您将在下面看到我的课程是什么样子的:

public class MyCustomComboBox : ComboBox
{
    private WinHook hook = null;

    public MyCustomComboBox()
        : base()
    {
        this.hook = new WinHook(this);
    }

    private class WinHook : NativeWindow
    {
        public WinHook(MyCustomComboBox parent)
        {
            parent.HandleCreated += new EventHandler(this.Parent_HandleCreated);
            parent.HandleDestroyed += new EventHandler(this.Parent_HandleDestroyed);
        }

        protected override void WndProc(ref Message m)
        {
            // do something

            base.WndProc(ref m);
        }

        private void Parent_HandleCreated(object sender, EventArgs e)
        {
            MyCustomComboBox cbx = (MyCustomComboBox)sender;

            this.AssignHandle(cbx.Handle);
        }

        private void Parent_HandleDestroyed(object sender, EventArgs e)
        {
            this.ReleaseHandle();
        }
    }
}

【问题讨论】:

    标签: c# dispose invalidoperationexception nativewindow


    【解决方案1】:

    根据 Hans 的建议,我修改了代码以使用他自己的一个示例中的 CB_GETCOMBOBOXINFO

    public class PastelessComboBox : ComboBox {
    
        private class TextWindow : NativeWindow {
          [StructLayout(LayoutKind.Sequential)]
          private struct RECT {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
          }
    
          private struct COMBOBOXINFO {
            public Int32 cbSize;
            public RECT rcItem;
            public RECT rcButton;
            public int buttonState;
            public IntPtr hwndCombo;
            public IntPtr hwndEdit;
            public IntPtr hwndList;
          }
    
          [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
          private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);
    
          public TextWindow(ComboBox cb) {
            COMBOBOXINFO info = new COMBOBOXINFO();
            info.cbSize = Marshal.SizeOf(info);
            SendMessageCb(cb.Handle, 0x164, IntPtr.Zero, out info);
            this.AssignHandle(info.hwndEdit);
          }
    
          protected override void WndProc(ref Message m) {
            if (m.Msg == (0x0302)) {
              MessageBox.Show("No pasting allowed!");
              return;
            }
            base.WndProc(ref m);
          }
        }
    
        private TextWindow textWindow;
    
        protected override void OnHandleCreated(EventArgs e) {
          textWindow = new TextWindow(this);
          base.OnHandleCreated(e);
        }
    
        protected override void OnHandleDestroyed(EventArgs e) {
          textWindow.ReleaseHandle();
          base.OnHandleDestroyed(e);
        }
    
      }
    

    【讨论】:

    • Iffy 代码,您正在强制在构造函数中创建本机窗口。改写 OnHandleCreated()。请注意,当重新创建本机窗口时,它可以多次运行,因此它也可以正确处理。获取文本框句柄的最佳方法是 CB_GETCOMBOBOXINFO。
    • 非常感谢您的解决方案,终于可以使用了!我的第一种方法几乎相同,真的很奇怪为什么它不起作用......
    • 我现在发现我的代码有什么问题:唯一真正的区别是,我还覆盖了组合框的 OnHandleDestroyed 方法。我在 NativeWindow 上调用了 ReleaseHandle。一旦带有 ReleaseHandle 的这一行在代码中,我就会在调用 dispose 时得到异常。为什么在这种情况下不允许调用 ReleaseHandle?
    猜你喜欢
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 2012-12-28
    • 1970-01-01
    • 2017-11-24
    • 2011-07-04
    • 2016-01-06
    相关资源
    最近更新 更多