I have code which is reading from MSMQ using the asynchronous API support, that is using BeginReceive(), EndReceive() and the ReceivedCompleted event. The basic pattern is (taken from MessageQueue.ReceiveCompleted Event...
void StartListening()
{
_msgQ.ReceiveCompleted += ReceiveCompletedEventHandler(FooReceiveCompleted);
_msgQ.BeginReceive();
}
void FooReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
{
Message msg = _msgQ.EndReceive();
// Do stuff with message.
// Set up listening for next message.
_msgQ.BeginReceive();
}
void StopListening()
{
_msgQ.Close();
}
The issue I can see is that there is always a pending BeginReceive() waiting for a new message, and through reading the .Net docs there does not appear to be an official/recommended way of cleaning this up in order to stop listening.
If I call EndReceive() without there being a message to receive then the call blocks until a message becomes available. Alternatively it appears that Close() will not cleanup the underlying handle on the MSMQ (and therefore the pending listener) unless the EnableConnectionCache is set to false, otherwise the handles are cached and are not cleaned up up on a call to close. I can do this but ideally I would like to use the caching.
The only other option I could see was to enable caching and then call the static method MessageQueue.ClearConnectionCache() which presumably is application domain wide and therefore will affect queues not related to the queue I am trying to close.
ADDENDUM: Additional options (from MessageQueue.Close())...
Close does not always free the read and write handles to a queue, because they might be shared. You can take any of the following steps to ensure that Close frees the read and write handles to a queue:
Create the MessageQueue with exclusive access. To do so, call the MessageQueue(String, Boolean) or MessageQueue(String, Boolean, Boolean) constructor, and set the sharedModeDenyReceive parameter to true.
Create the MessageQueue with connection caching disabled. To do so, call the MessageQueue(String, Boolean, Boolean) constructor and set the enableConnectionCache parameter to false.
Disable connection caching. To do so, set the EnableConnectionCache property to false.
Thus my first impression of the API from the docs is that you cannot terminate a queue properly (when using BeginReceive/EndReceive) unless caching is not used or you have exclusive access to a queue.