Use of NativeWindow for ComboBox causes exception in Dispose-method
Asked Answered
M

1

3

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();
        }
    }
}
Miniskirt answered 19/10, 2012 at 14:12 Comment(0)
L
4

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

  }
Luong answered 19/10, 2012 at 15:14 Comment(3)
Iffy code, you are forcing the native window to be created in the constructor. Override OnHandleCreated() instead. Note that it can run more than once when the native window is recreated so it also properly deals with that. The best way to get the textbox handle is CB_GETCOMBOBOXINFO.Muleteer
Thanks a lot for your solution, finally it works now! My first approach was nearly the same, really strange why it didn't work...Miniskirt
I've found out now what was the problem with my code: The only real difference was, that I've also overriden the OnHandleDestroyed-method of the combobox. There I called ReleaseHandle on the NativeWindow. As soon as this line with ReleaseHandle is inside the code, I get the exception when dispose is called. Why isn't it allowed to call ReleaseHandle in this case?Miniskirt

© 2022 - 2024 — McMap. All rights reserved.