MSMQ CreateCursor NullReferenceException
Asked Answered
U

1

6

I have a website that uses MSMQ on a remote server to queue pending e-mails. I am able to write the message to the queue, and then call dispose on the queue. The queue still gets the message, but sometime later the GC comes along and tries to clean up and it causes IIS to crash. This is what I see in the event log:

Exception: System.NullReferenceException

Message: Object reference not set to an instance of an object.

StackTrace: at System.Messaging.Cursor.Finalize()

This code has been running fine for years, but it just started acting up recently. I have rebooting all the servers to troubleshoot it, but that does not help.

Edit 1

Here is the code that is sending the messages. QueueFactory is just a singleton that has a lock around creating a MessageQueue.

using (System.Messaging.MessageQueue queue
    = QueueFactory.Instance.BuildQueue(this.Path))
{
    System.Messaging.Message message = new System.Messaging.Message
    {
        Body = body,
        Formatter = new BinaryMessageFormatter(),
        TimeToBeReceived = this.ExpirationMinutes
    };

    queue.Send(message, label);
}

There is a try-catch around this code, and I know it never makes it into the catch block. I'm beginning to think this was caused when the entire application was upgraded from .NET 3.5 to .NET 4.0. However it started occurring sporadically, but now it happens every time I write a message to the queue.

Edit 2

Here is the code for BuildQueue

lock (lockHandler)
{
    return new System.Messaging.MessageQueue(queuePath);
}
Unwonted answered 2/1, 2013 at 17:17 Comment(26)
Might want to add more information like ... complete stack trace, source code that's queuing and disposing the queue. Seems like if you have garbage collection you don't need to be disposing objecting on your own.Camisado
That is the entire stack trace, and I'm simply calling Send on a MessageQueue object, which has been constructed in an using block. I think it's pretty standard for disposed objects to be visited by the GC.Unwonted
so is your exception happening on the sever that is queuing the messages or on the remote server that is reading them? From MSDN "A Cursor is used to maintain a specific location in a MessageQueue when reading the queue's messages." msdn.microsoft.com/en-us/library/system.messaging.cursor.aspxCamisado
It's happening on the server that is queuing the messages.Unwonted
There must be code that's trying to read the message queue. Try to find where you're calling "CreateCursor". If you've verified that you code hasn't changed, no new code has been added, and your IIS configuration hasn't changed, then maybe a hotfix/update has caused the bug. Without seeing the code, only guesses can be made about the solution, unless someone can dig up a known-issues bug report from Microsoft.Camisado
I added the code to the question, but like I said it's fairly simple.Unwonted
Might be a bug in the Dispose plumbing for MessageQueue. Does the error go away if you omit the using() around queue?Majuscule
Haven't tried. I am experiencing this on production, so my experimenting is a bit limited.Unwonted
Several questions, the server that is writing the queue have there been any configuration changes to IIS? No server upgrades? IIS version upgrades? The MessageQueue is not thread safe and everytime you call queue you should lock.Albinaalbinism
This is a known bug for the Cursor Dispose method in .NET 2 (databaseforum.info/25/806736.aspx) but it should have been fixed for .NET 4. Are you 100% sure your code is running off .NET 4 with System.Messaging.dll 4.0?Condenser
@Rob4md-Yes about 5 months ago the app was upgraded to .NET 4. That is right around the time this problem started happening, although it was intermittent. OS has remained Server 2003 R2. I find it hard to believe that MSMQ is not thread safe. How would you synchronize across potentially thousands of machines writing to the same queue?Unwonted
@SimonMourier-Yes I've read that, but like I stated previously this only started happening once I upgraded to .NET 4. I'm fairly certain that IIS is running a .NET 4 app. Is there a way to determine what dlls have been loaded?Unwonted
Who is writing to the queue where it is thousands of machines? I thought it was the webserver that is writing to the queue. The System.Messaging.MessageQueue object is not thread safe, msdn.microsoft.com/en-us/library/…. Note the reference note at the bottom of msdn. I would have one MessageQueue object and lock around the MessageQueue.Send() calls and MessageQueue.Peek() calls. Can you add the code to you BuildQueue method?Albinaalbinism
I just speculating that you could have thousands of machines writing to the same queue. Seems like the thread safety has changed since .NET 2, since those methods I am using were thread safe. I am having trouble visualizing thread safety regarding these methods though. Send() would just result in messages being queued in a non-deterministic order. Same with Peek(). And how would this cause a NullReferenceException?Unwonted
Also I found this post related to thread safety in MSMQ. yoelarnon.wordpress.com/2008/10/04/… I am using the .NET message object, so it should actually be thread safe.Unwonted
If you look with reflector at Cursor Dispose on .NET 2 and .NET 4 assemblies, you will notice that has changed. The .NET 4 Dispose method version cannot raise a NullReferenceException while the .NET 2 can. You really want to make sure the code that's executing the source you show really uses .NET 4.Condenser
What version of MSMQ are you using?Crystie
Also are you doing this through a WCF service?Crystie
I looked with .NET Reflector and couldn't see a finalizer implemented for Cursor. It appears to only inherit one from Object. However, if that were the case, the GC shouldn't actually invoke it at all. Is the source available?Liddell
Are you calling CreateCursor anywhere in your code?Liddell
Uses ProcessExplorer to see what DLLs are loaded. You will need to run it as admin to see all of the processes. Find the one that is hosting MSMQ and then go to view\lower pane view\show DLLs. That should help you find out if the .net 2 DLL is loaded or not.Ankylostomiasis
@RhysW Server 2003, so MSMQ v3. Also, there is no WCF involved.Unwonted
@Tragedian I am not calling CreateCursor(). Also I'm not sure what source you are looking for.Unwonted
@Ankylostomiasis ProcessExplorer confirms that I am using .NET 4 System.Messaging.Unwonted
I'm looking for the source for System.Messaging.Cursor. Both ILSpy and .NET Reflector indicate there's no finalizer for this class, yet your callstack shows a Finalize call being the source of the problem. If we could see the content of System.Messaging.Cursor.Finalize we might be able to identify possible root causes.Liddell
Yes I have done the same exercise and it doesn't make sense to me.Unwonted
K
0

Try using a transaction:

using (System.Messaging.MessageQueue queue
    = QueueFactory.Instance.BuildQueue(this.Path))
{
    System.Messaging.Message message = new System.Messaging.Message
    {
        Body = body,
        Formatter = new BinaryMessageFormatter(),
        TimeToBeReceived = this.ExpirationMinutes
    };

    MessageQueueTransaction transaction = new MessageQueueTransaction();

    try
    {
        transaction.Begin();
        queue.Send(message, label, transaction);
        transaction.Commit();
    }
    catch(System.Exception e)
    {
        transaction.Abort();
        throw e;
    }
    finally
    {
        transaction.Dispose();
    }
}
Kagoshima answered 11/1, 2013 at 14:53 Comment(4)
That may or may not work, but I'd like to understand why this is happening all of a sudden.Unwonted
There's a chance that the transaction might catch the error that the transactionless process doesn't.Kagoshima
Message queues should be set up either specifically as transactional, or as non-transactional - if they're non-transactional your code will fail if you try to open a transaction on them. You can look at the queue in computer management to see if it is transactional.Crystallo
@Crystallo true... I should have mentioned thatKagoshima

© 2022 - 2024 — McMap. All rights reserved.