Is there a way to check how many messages are in a MSMQ Queue?
Asked Answered
K

10

41

I was wondering if there is a way to programmatically check how many messages are in a private or public MSMQ using C#? I have code that checks if a queue is empty or not using the peek method wrapped in a try/catch, but I've never seen anything about showing the number of messages in the queue. This would be very helpful for monitoring if a queue is getting backed up.

Kneecap answered 6/10, 2010 at 1:56 Comment(0)
W
34

You can read the Performance Counter value for the queue directly from .NET:

using System.Diagnostics;

// ...
var queueCounter = new PerformanceCounter(
    "MSMQ Queue", 
    "Messages in Queue", 
    @"machinename\private$\testqueue2");

Console.WriteLine( "Queue contains {0} messages", 
    queueCounter.NextValue().ToString());
Wotton answered 30/10, 2012 at 12:37 Comment(6)
Works fine for one queue, but for error queue I'm getting "Instance 'machinename\private$\error' does not exist in the specified Category.". Is that because the queue haven't had any items in queue for a while or do I need to write in a special way for error queue?Overlive
I added a message in the error queue and then the code works fine for that queue as well. I guess you need to catch this exception (InvalidOperationException) and then it's empty. Bad thing is, the queue could also be non-existent if you get this exception.Overlive
I have found that a syntax like ".\private$\testqueue2" which is what I get from someQueue.Path will cause an exception. I ended up using someQueue.FormatName and extracting "machinename\private$\testqueue2" from its content. I also found this approach not to be 100 percent reliable when the queue is just being started or retrieved which made me think that I would be better off with an implementation based on the com dll but I hadn't had time to invest in more testing.Urethra
I am getting 'Category does not exist.' when attempting to instantiate the PerformanceCounter(). Must there be > 0 messages in the queue? Christian, did you find another work around? I cannot guarantee messages being in the queue in my case.Rocher
this is the fastest method of the ones I tested so farWarp
How can a question this "easy" be so hard to get right? I'm getting huge pauses or "does not exist in the specified category" error. So much for a "quick look at MSMQ" !!Destroyer
D
26

There is no API available, but you can use GetMessageEnumerator2 which is fast enough. Sample:

MessageQueue q = new MessageQueue(...);
int count = q.Count();

Implementation

public static class MsmqEx
{
    public static int Count(this MessageQueue queue)
    {
        int count = 0;
        var enumerator = queue.GetMessageEnumerator2();
        while (enumerator.MoveNext())
            count++;

        return count;
    }
}

I also tried other options, but each has some downsides

  1. Performance counter may throw exception "Instance '...' does not exist in the specified Category."
  2. Reading all messages and then taking count is really slow, it also removes the messages from queue
  3. There seems to be a problem with Peek method which throws an exception
Dunn answered 18/4, 2014 at 10:21 Comment(2)
Isn't there a race condition here? What happens if stuff is being added/removed while you are enumerating? Or if stuff is added/removed after you have counted? Is there a way to suspend all interaction with the queue while you count?Exscind
This method seemed to be thread-safe in a sense it reflects dynamic changes to the queue. From doc "For example, the enumerator can automatically access a lower-priority message placed beyond the cursor's current position, but not a higher-priority message inserted before that position.". Personally I was testing it under load during inserts and deletes and didn't have threading issues. Obviously, this will reflect approx. count of messages if your queue is used. If you need to have exact count, you need to get a static snapshot by calling GetAllMessagesDunn
R
10

If you need a fast method (25k calls/second on my box), I recommend Ayende's version based on MQMgmtGetInfo() and PROPID_MGMT_QUEUE_MESSAGE_COUNT:

for C# https://github.com/hibernating-rhinos/rhino-esb/blob/master/Rhino.ServiceBus/Msmq/MsmqExtensions.cs

for VB https://gist.github.com/Lercher/5e1af6a2ba193b38be29

The origin was probably http://functionalflow.co.uk/blog/2008/08/27/counting-the-number-of-messages-in-a-message-queue-in/ but I'm not convinced that this implementation from 2008 works any more.

Rothstein answered 11/12, 2015 at 1:21 Comment(5)
Don't look for another answer! Other than @Zartag's answer, any other method is either slow or unreliable (I have tested all of them, believe me!)Goods
The nuget package Rsft.Lib.Msmq.MessageCounter implements this.Betook
Slow is relative @Goods But yes this is definitely the fastest.Warp
but also the one that requires the most (complicated) codeWarp
Also @JeroenMaes unfortunately when calling the GetCount method of the library I get an exception.. Filed an issue on their repository.Warp
Y
4

We use the MSMQ Interop. Depending on your needs you can probably simplify this:

    public int? CountQueue(MessageQueue queue, bool isPrivate)
    {
        int? Result = null;
        try
        {
            //MSMQ.MSMQManagement mgmt = new MSMQ.MSMQManagement();
            var mgmt = new MSMQ.MSMQManagementClass();
            try
            {
                String host = queue.MachineName;
                Object hostObject = (Object)host;
                String pathName = (isPrivate) ? queue.FormatName : null;
                Object pathNameObject = (Object)pathName;
                String formatName = (isPrivate) ? null : queue.Path;
                Object formatNameObject = (Object)formatName;
                mgmt.Init(ref hostObject, ref formatNameObject, ref pathNameObject);
                Result = mgmt.MessageCount;
            }
            finally
            {
                mgmt = null;
            }
        }
        catch (Exception exc)
        {
            if (!exc.Message.Equals("Exception from HRESULT: 0xC00E0004", StringComparison.InvariantCultureIgnoreCase))
            {
                if (log.IsErrorEnabled) { log.Error("Error in CountQueue(). Queue was [" + queue.MachineName + "\\" + queue.QueueName + "]", exc); }
            }
            Result = null;
        }
        return Result;

    }
Yettayetti answered 6/10, 2010 at 10:11 Comment(5)
Thanks, but where is the MSMQ.ManagementClass? I read in another post that you have to include a COM library called "MSMQ 3.0" but I don't see this on the Com tab (using .NET 3.5.)Kneecap
Do you have Interop.MSMQ.dll anywhere on your machine? We have it in our assembly collection.Yettayetti
No I don't have that, nor do I see anywhere to download it online.Kneecap
See this answer on how to add this reference: https://mcmap.net/q/392815/-msmq-com-api-in-cPork
Tried this method and got the following COMException which unfortunately I cannot resolve as this would require an infrastructure change: This operation is not supported for Message Queuing installed in workgroup mode. :(Supersaturate
S
3
            //here queue is msmq queue which you have to find count.        
            int index = 0;
            MSMQManagement msmq = new MSMQManagement() ;   
            object machine = queue.MachineName;
            object path = null;
            object formate=queue.FormatName;
            msmq.Init(ref machine, ref path,ref formate);
            long count = msmq.MessageCount();

This is faster than you selected one. You get MSMQManagement class refferance inside "C:\Program Files (x86)\Microsoft SDKs\Windows" just brows in this address you will get it. for more details you can visit http://msdn.microsoft.com/en-us/library/ms711378%28VS.85%29.aspx.

Sematic answered 17/6, 2014 at 14:30 Comment(1)
Where in the Windows folder exactly? There are 4 direct subfolders and each of the subfolders have more subfolders... What file do I have to search for?Warp
N
1

I had real trouble getting the accepted answer working because of the xxx does not exist in the specified Category error. None of the solutions above worked for me.

However, simply specifying the machine name as below seems to fix it.

private long GetQueueCount()
{
    try
    {
        var queueCounter = new PerformanceCounter("MSMQ Queue", "Messages in Queue", @"machineName\private$\stream")
        {
            MachineName = "machineName"
        };

        return (long)queueCounter.NextValue();
    }
    catch (Exception e)
    {
        return 0;
    }
}
Newmark answered 12/6, 2017 at 14:26 Comment(0)
A
0

The fastest method I have found to retrieve a message queue count is to use the peek method from the following site:

protected Message PeekWithoutTimeout(MessageQueue q, Cursor cursor, PeekAction action)
{
  Message ret = null;
  try
  {
     ret = q.Peek(new TimeSpan(1), cursor, action);
  }
  catch (MessageQueueException mqe)
  {
     if (!mqe.Message.ToLower().Contains("timeout"))
     {
        throw;
     }
  }
  return ret;
}

protected int GetMessageCount(MessageQueue q)
{
  int count = 0;
  Cursor cursor = q.CreateCursor();

  Message m = PeekWithoutTimeout(q, cursor, PeekAction.Current);
  {
     count = 1;
     while ((m = PeekWithoutTimeout(q, cursor, PeekAction.Next)) != null)
     {
        count++;
     }
  }
return count;
}
Aquanaut answered 8/10, 2010 at 21:31 Comment(4)
System.Messaging.MessageQueueException: MQ_ACTION_PEEK_NEXT specified to MQReceiveMessage cannot be used with the current cursor position. at System.Messaging.MessageQueue.ReceiveCurrent(TimeSpan timeout, Int32 action, CursorHandle cursor, MessagePropertyFilter filter, MessageQueueTransaction internalTransaction, MessageQueueTransactionType transactionType) at System.Messaging.MessageQueue.Peek(TimeSpan timeout, Cursor cursor, PeekAction action)Kneecap
We are using this exact method, and find that the method GetMessageCount is failing because it is peeking while messages are consumed from the queue. By merely adding a 10ms thread.sleep command after count++ this operates much more smoothly but takes a lot longer. I still think there must be a better way to get count that to insistantly peek a million times... (are we there yet, are we there yet, are we there yet - doh)Anomalism
This code will fail if the queue contains 0 messages, because it will try PeekWithoutTimeout with PeekAction.Next even though PeekWithTimeout with PeekAction.Current returned null. Added null check to answerKoy
This is definitely not the fastest method. Highest Voted answer: 6x times faster. Second Highest Voted Answer: 3x times fasterWarp
D
0

This worked for me. Using a Enumarator to make sure the queue is empty first.

   Dim qMsg As Message ' instance of the message to be picked 
        Dim privateQ As New MessageQueue(svrName & "\Private$\" & svrQName) 'variable svrnme = server name ; svrQName = Server Queue Name
        privateQ.Formatter = New XmlMessageFormatter(New Type() {GetType(String)}) 'Formating the message to be readable the body tyep
        Dim t As MessageEnumerator 'declared a enumarater to enable to count the queue
        t = privateQ.GetMessageEnumerator2() 'counts the queues 

        If t.MoveNext() = True Then 'check whether the queue is empty before reading message. otherwise it will wait forever 
            qMsg = privateQ.Receive
            Return qMsg.Body.ToString
        End If
Darrickdarrill answered 31/7, 2014 at 13:14 Comment(1)
This is a C# question, your answer is VBChamfer
W
0

If you want a Count of a private queue, you can do this using WMI. This is the code for this:

// You can change this query to a more specific queue name or to get all queues
private const string WmiQuery = @"SELECT Name,MessagesinQueue FROM Win32_PerfRawdata_MSMQ_MSMQQueue WHERE Name LIKE 'private%myqueue'";

public int GetCount()
{
    using (ManagementObjectSearcher wmiSearch = new ManagementObjectSearcher(WmiQuery))
    {
        ManagementObjectCollection wmiCollection = wmiSearch.Get();

        foreach (ManagementBaseObject wmiObject in wmiCollection)
        {
            foreach (PropertyData wmiProperty in wmiObject.Properties)
            {
                if (wmiProperty.Name.Equals("MessagesinQueue", StringComparison.InvariantCultureIgnoreCase))
                {
                    return int.Parse(wmiProperty.Value.ToString());
                }
            }
        }
    }
}

Thanks to the Microsoft.Windows.Compatibility package this also works in netcore/netstandard.

Warp answered 14/2, 2020 at 15:13 Comment(0)
M
-1

The message count in the queue can be found using the following code.

MessageQueue messageQueue = new MessageQueue(".\\private$\\TestQueue");
var noOFMessages = messageQueue.GetAllMessages().LongCount();
Maidinwaiting answered 2/11, 2016 at 10:13 Comment(1)
In this case you get all messages from queue. It is too expensive.Turkmen

© 2022 - 2024 — McMap. All rights reserved.