Search code examples
c#disposeinvalidoperationexceptionnativewindow

Use of NativeWindow for ComboBox causes exception in Dispose-method


In C# Windows.Forms I want to intercept the paste-windowmessage for a combobox. As this doesn't work by overriding the WndProc-method of the combobox, because I would need to override the WndProc of the textbox inside the combobox, I decided to create a custom class of type NativeWindow which overrides the WndProc. I assign the handle and release it, when the combobox-handle gets destroyed. But when Dispose for the combobox is called the problem is that I get an InvalidOperationException saying that an invalid cross-thread operation occured and that the combobox was accessed from a thread other than the thread it was created on. Any ideas what is going wrong here?

In the following you'll see, how my classes look like:

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();
        }
    }
}

Solution

  • Per Hans' suggestion, I modified the code to use CB_GETCOMBOBOXINFO from one of his own examples.

    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);
        }
    
      }