Search code examples
c#.netmultithreadingwinformswndproc

Processing windows messages, threading COM objects


I have an application that captures Windows messages sent to the Form, and runs a method if an update is made to the system Clipboard:

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_CLIPBOARDUPDATE:

            if (IsClipboardListenerOn)
                OnClipboardChanged();

            break;
    }

    base.WndProc(ref m);
}

In some instances I don't want my method, OnClipboardChanged(), to run. My solution was to set a static global variable IsClipboardListenerOn and toggle it as necessary. This didn't get the job done because the Windows message wasn't processed till after my function that did the toggling returned, so it was always true:

private void some_event(object sender, EventArgs e)
{
    MainForm.IsClipboardListenerOn = false;

    // some code that makes the clipboard changed message fire

    MainForm.IsClipboardListenerOn = true;

   // when this returns the WM_CLIPBOARDUPDATE message gets caught
}

The next idea was to run the code that triggered a message event on a thread and wait for the thread to return to ensure it executed while the global flag was toggled off:

    Thread thread1 = new Thread(() => clipboard_stuff());
    thread1.SetApartmentState(ApartmentState.STA);

    MainForm.IsClipboardListenerOn = false;

    thread1.Start();        

    thread1.Join();
    MainForm.IsClipboardListenerOn = true;

But this didn't work, either. In fact, sometimes it would execute my OnClipboardChanged() method twice. I know the system Clipboard requires Single Threaded Apartment; Is this part of my problem? Am I going about this wrong? Is there some other technique I can utilize?


Solution

  • Right, that's not going to work. Windows is waiting for you to start pumping messages again. A workaround is to delay setting the flag back to true until all pending messages were dispatched. That can be elegantly done by using the Control.BeginInvoke() method. Like this:

    private void some_event(object sender, EventArgs e)
    {
        MainForm.IsClipboardListenerOn = false;
        // some code that makes the clipboard changed message fire
        //...
        this.BeginInvoke(new Action(() => MainForm.IsClipboardListenerOn = true));
    }
    

    With the assumption that some_event() is a member of a form. I'd guess that MainForm.BeginInvoke() would work as well.