Click here to Skip to main content
15,881,757 members
Articles / Desktop Programming / Win32

Preventing Unexpected Message Pumping in Third Party Libraries - An example using DirectShow

Rate me:
Please Sign up or sign in to vote.
4.33/5 (3 votes)
7 Jun 2009CPOL3 min read 27.3K   135   11   6
This article shows a tidy solution to unexpected third party code pumping messages.

Introduction

This project demonstrates a problem inherent in the design of windowing applications and a simple solution to the problem. The problem is that when you call a third party library, there is no guarantee that the library will not call Application.DoEvents() or in some other way cause pumping of your application's message queue. This is normally not a problem, but it can set up a race condition in your code.

For example, assume the user has rapidly clicked a button several times in succession. Your code is busy processing the first button click and your application is in an inconsistent state. You probably assumed (justifiably) that throughout your button event handler, you were quite safe from being preempted by another button click. You are after all using a single threaded model with an event queue that serialises user input. In the button click event handler, you then make a call out to some third party code. That third party code, for whatever reason, calls Application.DoEvents() or pumps messages in native code. Suddenly, the next button click event is fired, and very quickly throws an exception due to the inconsistent state left by the partly run event handler of the first click.

Background

I ran into this problem while working on a C# WinForms application that uses DirectShow to do video recording. The problem was found by a tester who rapidly clicked on various control buttons and caused a null ref exception. The diagnosis of the problem is recorded in a UseNet thread here.

I considered other solutions to the problem such as dropping all user input and timer messages during the processing of a user input or timer message. This solution would prevent the reentrancy, but dropping messages could cause other problems.

Solution

To prevent the nesting of event handlers, we need to prevent the message loop in the third party code - the DirectShow code in this case - from dequeing and processing messages destined for windows in your application. The solution I used to do this was to start a second thread with its own message loop. This code fragment is from the Main() method of the application:

C#
SynchronizationContext context = null;
bool started = false;

ThreadStart startRoutine =
    delegate
    {
        // Set the sync context on the thread
        context = new WindowsFormsSynchronizationContext(); 
        SynchronizationContext.SetSynchronizationContext(context);

        // Run a dedicated message loop with no forms
        Application.Run();
    };

// Start up the DS runner
Thread runner = new Thread(startRoutine);
runner.Start();

Note that a synchronization context is created and set on the new thread. This is to allow easy inter-thread messaging using SynchronizationContext.Send(). Simply place the calls to the third party library (the one that pumps messages) in a SendOrPostCallback delegate and Send() it to the other thread.

C#
SendOrPostCallback a =
    delegate
    {
        //Call code that pumps messages here
        ......
    };

context.Send(a, null);

One detail is that a WindowsFormsSynchronizationContext is used, not a plain SynchronizationContext. The problem is that SynchronizationContext.Send() actually executes the SendOrPostCallback on the calling thread! The WindowsFormsSynchronizationContext.Send() executes it correctly on the target thread.

Running the Demo

The demo application has two buttons. The first button runs a DirectShow call on the main thread after first queuing a message on the main thread's message queue. When the DirectShow call is made, the queued message is processed, nested inside the button click.

The second button runs the DirectShow call synchronously on a dedicated thread. This means that the queued message is not processed until after the button click handler returns.

Points of Interest

It should be noted that the solution to the problem demonstrated here is not production ready. If you use it, you will want to package it up in a helper class somehow. You'll also need to consider what happens to any exceptions thrown in the second thread (hint: if you use Send(), they will come back to the main thread; if you use Post(), they'll need to be handled on the second thread).

Disclaimer

I do not consider myself an expert in either DirectShow or in the details of Windows programming, be it Win32 or .NET. This article is submitted in the hope that it will help other people avoid the same situation, but also in the hope that more expert programmers will suggest other solutions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
New Zealand New Zealand
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralThanks for sharing your solution Pin
Christian Rodemeyer8-Jun-09 21:03
professionalChristian Rodemeyer8-Jun-09 21:03 
GeneralRe: Thanks for sharing your solution Pin
Felix Collins10-Jun-09 11:28
Felix Collins10-Jun-09 11:28 
GeneralRe: Thanks for sharing your solution Pin
Oneibus22-Dec-10 9:08
Oneibus22-Dec-10 9:08 
GeneralDescribe solution Pin
zlezj1-Jun-09 21:08
zlezj1-Jun-09 21:08 
GeneralRe: Describe solution Pin
Anthony Daly2-Jun-09 1:30
Anthony Daly2-Jun-09 1:30 
GeneralRe: Describe solution Pin
Felix Collins3-Jun-09 11:28
Felix Collins3-Jun-09 11:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.