.NET asynchronous MSMQ

Barguast picture Barguast · Mar 30, 2011 · Viewed 7.3k times · Source

I don't understand where this is going wrong. Basically, I have a program which receives from a message queue and processes the messages. The program can be stopped at any time, in which case the message loop finished what it is doing before the program exits. I'm trying to accomplish this with the following code:

private MessageQueue q;
private ManualResetEventSlim idle;

public void Start()
{
    idle = new ManualResetEventSlim();
    q.ReceiveCompleted += this.MessageQueue_ReceiveCompleted;    
    q.BeginReceive();
}    

public void Stop()
{ 
    this.q.Dispose();
    this.idle.Wait();    
}

private void MessageQueue_ReceiveCompleted(object sender, 
    ReceiveCompletedEventArgs e)
{
    Message inMsg;
    try
    {
        inMsg = e.Message;
    }
    catch (Exception ex)
    {
        this.idle.Set();
        return;
    }

    // Handle message

    this.q.BeginReceive();
}

As is hopefully apparent, the Stop method disposes of the message queue, and then waits for the idle wait handle to be set (which should occur as an ReceiveCompleted event will be called when disposed, but the e.Message property should except).

However, the message loop just continues! I've disposed the message queue, but it still manages to read from it and the exception handler is not invoked, meaning the idle.Wait line waits forever.

My understanding is that disposing a message queue SHOULD end any pending receives and invoke the event, but the e.Message (or q.EndReceive) should throw an exception. Is this not the case? If not, how else can I safely exit my message loop?

Thanks

UPDATE:

Here is a complete example (assumes the queue exists)

class Program
{
    static MessageQueue mq;
    static ManualResetEventSlim idleWH;

    static void Main(string[] args)
    {
        idleWH = new ManualResetEventSlim();

        Console.WriteLine("Opening...");
        using (mq = new MessageQueue(@".\private$\test"))
        {
            mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(int) });
            mq.ReceiveCompleted += mq_ReceiveCompleted;

            for (int i = 0; i < 10000; ++i)
                mq.Send(i);

            Console.WriteLine("Begin Receive...");
            mq.BeginReceive();

            Console.WriteLine("Press ENTER to exit loop");
            Console.ReadLine();

            Console.WriteLine("Closing...");

            mq.Close();
        }

        Console.WriteLine("Waiting...");
        idleWH.Wait();

        Console.WriteLine("Press ENTER (ex)");
        //Console.ReadLine();
    }

    static void mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
    {
        try
        {
            var msg = mq.EndReceive(e.AsyncResult);
            Console.Title = msg.Body.ToString();

            // Receive next message
            mq.BeginReceive();
        }
        catch (Exception ex)
        {
            idleWH.Set();
            return;
        }
    }
}

Answer

Hans Passant picture Hans Passant · Mar 30, 2011

Not quite sure how you made this work at all. You must call MessageQueue.EndReceive() in the event. Only that method can throw the exception. Review the MSDN sample code for the ReceiveCompleted event. And don't catch Exception, that just causes undiagnosable failure. Catch the specific exception you get when you dispose the queue, ObjectDisposedException.