Search code examples
c#windows-servicesmsmq

Handling Windows Service Errors during MSMQ Message Recived


I have a Windows Service that monitors a private MSMQ for messages by providing a handler for the MSMQ peekCompleted event, like this (assigned during service OnStart):

_queue.PeekCompleted += new PeekCompletedEventHandler(MessageReceived);

The MessageReceived method then does this (variables beginning with an underscore are declared globally in the service):

 private void MessageReceived(object sender, PeekCompletedEventArgs e)
    {
        try
        {
            _handler = new MessageHandler(); //does the heavy lifting
            _queueMessage = _queue.EndPeek(e.AsyncResult);
            _queueMessage.Formatter = new BinaryMessageFormatter();
            _currentMessage = (MyMessageType)_queueMessage.Body;

            // we have the message so no matter what we want it off of the queue
            _queue.ReceiveById(_queueMessage.Id);

            // Message Handler is going to swallow everything here
            // messages are processed OK or moved to a 'failed' or 'poison' queue
            // there should be no uncaught exceptions
            _handler.Process(_currentMessage);

            //go wait again
            _queue.BeginPeek();
        }
        catch (Exception exception)
        {
           // here is question

        }
    }

Question: given that _handler swallows any exceptions it generates/encounters (and deals with its message accordingly) I wouldn't expect exceptions in this try block but if any do happen I need to ensure that the queue is returned to the proper state, even tho I have no idea where the exception might have occurred. So do I just

 _queue.ReceiveById(_queueMessage.Id);
 _queue.BeginPeek();

in the catch block even though either of those two statements might either have already been executed? I can't really assume (ahem) that one of them is not generating the exception for some unknown reason, that being the nature of exceptions, yes?

How should this be handled?


Solution

  • Don't handle it. If you make your queues transactional then you can wrap you ReceiveById call in a transaction.:

    using (TransactionScope txnScope = new TransactionScope())
    {
        _queue.ReceiveById(_queueMessage.Id);
        txnScope.Commit();
    }
    

    This will ensure that if an exception is thrown before the commit the message will be left on the queue. This will only work if the queue you are receiving from is local. Otherwise you'll need to configure DTC on all the machines involved.