在Windows messagequeue中获取所有消息时出现偶发错误

时间:2012-07-05 16:59:32

标签: c# asp.net windows message-queue system.messaging

在Windows Server 2003上运行的C#ASP.NET 3.5 Web应用程序中,偶尔会出现以下错误:

"Object reference not set to an instance of an object.:   at System.Messaging.Interop.MessagePropertyVariants.Unlock()
   at System.Messaging.Message.Unlock()
   at System.Messaging.MessageQueue.ReceiveCurrent(TimeSpan timeout, Int32 action, CursorHandle cursor, MessagePropertyFilter filter, MessageQueueTransaction internalTransaction, MessageQueueTransactionType transactionType)
   at System.Messaging.MessageEnumerator.get_Current()
   at System.Messaging.MessageQueue.GetAllMessages()".

抛出此错误的代码行是:

Message[] msgs = Global.getOutputQueue(mode).GetAllMessages();

其中Global.getOutputQueue(mode)给出了我想从中获取消息的消息队列。

更新:     

 Global.getPool(mode).WaitOne();
 commonClass.log(-1, "Acquired pool: " + mode, "Report ID: " + unique_report_id);
            ............../* some code /
            ..............
                        lock(getLock(mode))
                        {
                            bool yet_to_get = true;
                            int num_retry = 0;
                            do
                            {
                                try
                                {
                                    msgs = Global.getOutputQueue(mode).GetAllMessages();
                                    yet_to_get = false;
                                }
                                catch
                                {
                                    Global.setOutputQueue(mode);
                                    msgs = Global.getOutputQueue(mode).GetAllMessages();
                                    yet_to_get = false;
                                }
                                ++num_retry;
                            }
                            while (yet_to_get && num_retry < 2);
                        }
... / some code*/
....
finally
            {
                commonClass.log(-1, "Released pool: " + mode, "Report ID: " + unique_report_id);
                Global.getPool(mode).Release();
            }
 

2 个答案:

答案 0 :(得分:1)

您的说明和this thread表明了时间问题。我不经常创建MessageQueue对象(可能只有一次)并且Global.getOutputQueue(mode)返回一个缓存版本,似乎可能会解决这个问题。

编辑:进一步的细节表明你有相反的问题。我建议封装对消息队列的访问,捕获此异常并在发生异常时重新创建队列。因此,用以下内容替换对Global.getOutputQueue(mode).GetAllMessages()的调用:

public void getAllOutputQueueMessages()
{
    try
    {
        return queue_.GetAllMessages();
    }
    catch (Exception)
    {
        queue_ = OpenQueue();
        return queue_.GetAllMessages();
    }
}

您会注意到我没有保留您的mode功能,但您明白了。当然,您必须为您对队列进行的其他调用复制此模式,但仅限于您所创建的模式(而不是整个队列接口)。

答案 1 :(得分:1)

这是一个老线程,但谷歌把我带到这里,所以我将添加我的发现。

我同意用户的意见:这是一个时间问题。

创建消息队列后,它不会立即可用。

        try
        {
            return _queue.GetAllMessages().Length;
        }
        catch (Exception)
        {
            System.Threading.Thread.Sleep(4000);
            return _queue.GetAllMessages().Length;
        }

如果在访问您知道已创建的队列时遇到异常,请尝试添加暂停。

相关说明

_logQueuePath = logQueuePath.StartsWith(@".\") ? logQueuePath : @".\" + logQueuePath;
 _queue = new MessageQueue(_logQueuePath);
 MessageQueue.Create(_logQueuePath);
 bool exists = MessageQueue.Exists(_logQueuePath); 

运行MessageQueue.Exists(string nameofQ);创建队列后立即返回false的方法。所以在调用代码时要小心:

        public void CreateQueue()
    {
        if (!MessageQueue.Exists(_logQueuePath))
        {
            MessageQueue.Create(_logQueuePath);
        }
    }

因为它可能会抛出一个异常,说明您尝试创建的队列已经存在。

-edit :(抱歉,我没有这个新信息的相关链接)

我读到新创建的MessageQueue将在MessageQueue.Exists(QueuePath)上返回false,直到它收到至少一条消息。

保持这一点以及我提到的早期要点让我的代码可靠地运行。