1

I'm trying to handle errors that are passed through 2 dlls I've created. So Console.exe calls dll 1. dll 1 completes an async MQ message read and the handler calls dll 2. If dll 2 errors it passes the Exception (throw) without a problem. But the dll 1 (async) handler catch the throw from dll 2 and give me an unhandled by user message.. I have followed the msdn code to add in the IAsyncResult to keep the hander alive but the issue persists.

can anyone advise on how I should handle this stack and get the handler error returned to the console.exe program so I can present it to the user. Code below:-

Console.exe (snippet)

try
{
    _msmq.MSMQ_GetMessage(_msgPath);

     //set up the print of the number of queue messages
     Console.WriteLine("Main thread: starting a timer");
     Timer t = new Timer(ComputeBoundOp, _msgPath, 0, 2000);

     Console.Write("Press any key to continue . . .");
     Console.ReadKey(true);

     t.Dispose(); // Cancel the timer now

 }
 catch (MessageQueueException _msgQex)
 {
     Console.WriteLine("An error occurred with the queue:- " + _msgQex);
 }
 catch (Exception _ex)
 {
     Console.WriteLine("An error occurred with the queue:- " + _ex);
 }

dll 1

public void MSMQ_GetMessage(string _MQ_Path)
{
    try
    {
        //set the correct message queue
        MessageQueue _msgQ = new MessageQueue(_MQ_Path, QueueAccessMode.ReceiveAndAdmin);
        //set the format of the message queue
        _msgQ.Formatter = new XmlMessageFormatter(new Type[] { typeof(_TwitterStreamFeed) });
        _msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(_msgQ_RecieveCompleted);
        IAsyncResult _result = _msgQ.BeginReceive();
        _asyncList.Add(_result); // asyncList is a global variable of type System.Collections - > this allows the callback to remain open and therefore nit garbage collected while the async thread runs off on it's own
    }
    catch (Exception _ex)
    {
        throw new Exception("_msgQ_get Message threw the following error :- " + _ex);
    }
}

//method to process message
public void _msgQ_RecieveCompleted(object sender, ReceiveCompletedEventArgs e)
{
    try
    {
        //queue that have received a message
        MessageQueue _mq = (MessageQueue)sender;

        //get the messge off the queue
        Message _mqmsg = _mq.EndReceive(e.AsyncResult);

        //set the values back into a formatted struct 
        //now process your SQL....
        Azure_SQL _azuresql = new Azure_SQL();
        _azuresql.writeMessageToStorage((_TwitterStreamFeed)_mqmsg.Body);

        //refresh queue just in case any changes occurred (optional)
        _mq.Refresh();
        //tell MessageQueue to receive next message when it arrives
        _mq.BeginReceive();
    }
    catch (Exception _ex)
    {
        throw;
    }

dll 2

public void writeMessageToStorage(_TwitterStreamFeed _msmq_message_as_TSF)
{
    try
    {        
        // now do something with the class - i..e write the values to the database
        SqlConnection _azurecon = new SqlConnection(_AzuzeSQLConnection);
        SqlCommand _sqlcmd = new SqlCommand();

        //Setup the command string to call the stored procedure
        //Add the parameter to the parameters collection of the command

        blah blah blah.........  Do SQL writing to Db

        _azurecon.Open();
        SqlDataReader _sqldr_tweet_place = _sqlcmd_place.ExecuteReader(CommandBehavior.CloseConnection);
    }

    //now close things off
    _azurecon.Close();

    }
    catch (Exception _ex)
    {
        // Throw the error to preserve the original
        throw;
    }
zimdanen
  • 5,508
  • 7
  • 44
  • 89
Tim Windsor
  • 319
  • 1
  • 5
  • 17

2 Answers2

0

The reason for this is that, internally, the MessageQueue class is explicitly swallowing the exception. Where the MessageQueue class raises the ReceiveCompleted event, it's inside of a try-catch statement - and the catch block is empty. Suffice it to say, if an exception occurs inside your ReceiveCompleted event handler, _msgQ_RecieveCompleted(), nothing's ever going to know it happened.

I see a couple of options, in order of preference.

Option 1 - Shift where the asynchronous call is made

Since this exception-swallowing behavior only occurs when using BeginReceive(), in MSMQ_GetMessage(), you can switch from using BeginReceive() to just Receive(). Then, make your call to MSMQ_GetMessage() asynchronous and any exception that gets thrown will be propagated as expected.

As a side note, a new(er) alternative for making asynchronous calls is available; the Task<> class. As opposed to the Thread class, Task<> has exception handling functionality built in. It does, however, require Framework 4 or higher. There is a good explanation of it's use described in the answer here.

Option 2 - Use a custom event

If refactoring the asynchronous call isn't an option, you can create a custom event in your class in 'dll 2' and subscribe to that event in 'Console.exe'. So when an exception occurs in _msgQ_RecieveCompleted(), you can raise the event and 'Console.exe' will be notified.

Community
  • 1
  • 1
cokeman19
  • 2,405
  • 1
  • 25
  • 40
0

The MessageQueue.BeginReceive() method uses the standard .NET APM (Asynchronous Programming Model) pattern. It is very important to understand how it works to know how to properly deal with exceptions. Be sure to read the MSDN article, there are lots of other googable resources available.

In APM, the callback that tells you that a message was received in executed on a thread-pool thread. Which is a very efficient way to get code to run quickly. It is however also a very troublesome way when something goes wrong. The EndReceive() method call is likely to throw an exception, it does so to tell you that the receive operation could not be completed. A standard exception it will throw is ObjectDisposedException. Which will happen when the MessageQueue object gets disposed. In your case when your program terminates. You need to catch that exception and exit from your event handler, it is an expected exception and signals that nothing more useful is going to happen next since the queue was closed.

Then there's a raft of possible exceptions that can be raised by major mishaps in the message queue plumbing. Plus whatever you do with the message. Looks like you execute some Azure code, plenty of ways that can fall over. If you let such an exception escape from the callback method, like you do, then there's no catch clause anywhere in the call stack that is going to handle the exception. The standard way .NET deals with unhandled exceptions is to raise the AppDomain.UnhandledException event and terminate your program. If you didn't actually implement that event then there's nothing decent to look at to diagnose the reason your program ended, the Windows Error Reporting dialog has no good diagnostic.

Whether or not you should try to handle the exception and prevent the program from terminating is up to you. But it pretty strongly fits the "don't shoot the messenger" pattern, it is very unlikely your program can meaningfully continue to execute when such an exception is raised. It invariably takes a human to fix the problem, like restoring the network connection or fixing the message queue. If you do catch it then the odds that the same exception is raised over and over again is fairly likely. After all, there wasn't anything decent you could do in your code to repair the network.

So the best guidance here is to not try, just make sure that IT staff has a good diagnostic so they can repair the problem. Do implement the AppDomain.UnhandledException and display and log the e.UnhandledException.ToString() value. This will also let you learn the number of ways that your program can fail. There might be some conditions that are common enough to warrant catching, something like a temporary network outage. At that point you'll also know what to do about it, in other words what kind of code to write in the catch clause. There is no possible way you know what to write right now, you should therefore not try.

Last but not least, do note that you got yourself into this pickle because you used BeginReceive() unnecessarily. You've already got a perfectly good thread to do work on. But it doesn't do anything useful, it is stuck in the Console.ReadKey() method. Particularly in .NET 4.5 a very tricky method to call, it prevents other threads from writing anything to the console. So your error reporting won't work, it will deadlock when it tries to use Console.WriteLine() to write a diagnostic.

You might as well use MessageQueue.Read() instead. Now dealing with exceptions is a lot easier since they occur on the same thread. The MessageQueue.SynchronizingObject can also be helpful to get completion callbacks to occur on the main thread, but that only works in a GUI app, not in a console app.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536