The number of messages on an MSMQ via Powershell
Asked Answered
F

12

11

I'd like to provide a queuepath and get the number of messages thereupon. Any advice on how this could be done?

Fawnia answered 18/2, 2010 at 14:52 Comment(0)
F
4

So, I saw this: What can I do with C# and Powershell? and went here:http://jopinblog.wordpress.com/2008/03/12/counting-messages-in-an-msmq-messagequeue-from-c/

And made this

# Add the .NET assembly MSMQ to the environment.
[Reflection.Assembly]::LoadWithPartialName("System.Messaging")

# Create a new QueueSizer .NET class help to warp MSMQ calls.
$qsource = @"
public class QueueSizer
    {
        public static System.Messaging.Message PeekWithoutTimeout(System.Messaging.MessageQueue q, System.Messaging.Cursor cursor, System.Messaging.PeekAction action)
        {
            System.Messaging.Message ret = null;
            try
            {
                // Peek at the queue, but timeout in one clock tick.
                ret = q.Peek(new System.TimeSpan(1), cursor, action);
            }
            catch (System.Messaging.MessageQueueException mqe)
            {
                // Trap MSMQ exceptions but only ones relating to timeout. Bubble up any other MSMQ exceptions.
                if (!mqe.Message.ToLower().Contains("timeout"))
                {
                    throw;
                }
            }
            return ret;
        }

        // Main message counting method.
        public static int GetMessageCount(string queuepath)
        {
            // Get a specific MSMQ queue by name.
            System.Messaging.MessageQueue q = new System.Messaging.MessageQueue(queuepath);

            int count = 0;

            // Create a cursor to store the current position in the queue.
            System.Messaging.Cursor cursor = q.CreateCursor();

            // Have quick peak at the queue.
            System.Messaging.Message m = PeekWithoutTimeout(q, cursor, System.Messaging.PeekAction.Current);

            if (m != null)
            {
                count = 1;

                // Keep on iterating through the queue and keep count of the number of messages that are found.
                while ((m = PeekWithoutTimeout(q, cursor, System.Messaging.PeekAction.Next)) != null)
                {
                    count++;
                }
            }

            // Return the tally.
            return count;
        }
    }
"@

# Add the new QueueSizer class helper to the environment.
Add-Type -TypeDefinition $qsource -ReferencedAssemblies C:\Windows\assembly\GAC_MSIL\System.Messaging\2.0.0.0__b03f5f7f11d50a3a\System.Messaging.dll

# Call the helper and get the message count.
[QueueSizer]::GetMessageCount('mymachine\private$\myqueue');

And it worked.

Fawnia answered 18/2, 2010 at 18:23 Comment(3)
+1 good answer. Also, not sure why, but I had to change "QueueSizer::" to "[QueueSizer]::" in the last line for this script to work.Fourchette
@Fourchette +1 The square brackets indicate a type. As you mentioned, is required. Because GetMessageCount is a static method, you need to reference the type directly. If it was an instance method you would have to create an object first like $qs = New-Object QueueSizer; then reference $qs.NetMessageCourt...Legman
this is a terrible answer as it's iterating through the messages and counting them up. Add 1,000,000 messages to your queue and run this and you'll see how long it takesEsmond
B
8

This will list all queues on a machine and the number of messages:

gwmi -class Win32_PerfRawData_MSMQ_MSMQQueue -computerName $computerName |
    ft -prop Name, MessagesInQueue
Bovid answered 13/6, 2011 at 17:43 Comment(4)
Actually, I've noticed that the WMI call doesn't always enumerate all the queues. Not sure why. I ended up calling [System.Messaging.MessageQueue]::GetPublicQueuesByMachine($machineName) on a list of servers and then used Irwin's [QueueSizer]::GetMessageCount($queuepath) method to get the message count for each queue.Bovid
Is there any way to get more than the first 100 messages out of it?Ageless
Unfortunately, this truncates the queue name to 64 characters as well as only returns the first 97 results. :(Khalkha
@Bovid is there a way to get a copy of your complete script? Thanks.Stomachic
L
6

PowerShell under Windows Server 2012/2012 R2 and Windows 8/8.1 has a bunch built-in Cmdlets that can be used by installing the Microsoft Message Queue (MSMQ) Server Core feature.

# Get all message queues
Get-MsmqQueue;

# Get all the private message queues.
# Display only the QueueName and MessageCount for each queue.
Get-MsmqQueue -QueueType Private | Format-Table -Property QueueName,MessageCount;

There is a number of other Cmdlets that can be used for queue management and message creation. i.e.

  • New-MsmqQueue
  • Remove-MsmqQueue
  • Send-MsmqQueue
  • Receive-MsmqQueue
  • Get-MsmqQueueManager

For the full list of MSMQ Cmdlet help see MSMQ Cmdlets in Windows PowerShell, or Get-Command -Module MSMQ if you already have the feature installed.

Legman answered 15/1, 2014 at 10:52 Comment(2)
@RobertMooney That link does not see to work. Yes, there is not a built-in -ComputerName parameter that connects to a remote computer, however you can use PowerShell Remoting.Legman
Note however that Get-MsMqQueue only works with local queues. See technet.microsoft.com/en-us/library/hh405018(v=vs.85).aspx.Rockefeller
F
4

So, I saw this: What can I do with C# and Powershell? and went here:http://jopinblog.wordpress.com/2008/03/12/counting-messages-in-an-msmq-messagequeue-from-c/

And made this

# Add the .NET assembly MSMQ to the environment.
[Reflection.Assembly]::LoadWithPartialName("System.Messaging")

# Create a new QueueSizer .NET class help to warp MSMQ calls.
$qsource = @"
public class QueueSizer
    {
        public static System.Messaging.Message PeekWithoutTimeout(System.Messaging.MessageQueue q, System.Messaging.Cursor cursor, System.Messaging.PeekAction action)
        {
            System.Messaging.Message ret = null;
            try
            {
                // Peek at the queue, but timeout in one clock tick.
                ret = q.Peek(new System.TimeSpan(1), cursor, action);
            }
            catch (System.Messaging.MessageQueueException mqe)
            {
                // Trap MSMQ exceptions but only ones relating to timeout. Bubble up any other MSMQ exceptions.
                if (!mqe.Message.ToLower().Contains("timeout"))
                {
                    throw;
                }
            }
            return ret;
        }

        // Main message counting method.
        public static int GetMessageCount(string queuepath)
        {
            // Get a specific MSMQ queue by name.
            System.Messaging.MessageQueue q = new System.Messaging.MessageQueue(queuepath);

            int count = 0;

            // Create a cursor to store the current position in the queue.
            System.Messaging.Cursor cursor = q.CreateCursor();

            // Have quick peak at the queue.
            System.Messaging.Message m = PeekWithoutTimeout(q, cursor, System.Messaging.PeekAction.Current);

            if (m != null)
            {
                count = 1;

                // Keep on iterating through the queue and keep count of the number of messages that are found.
                while ((m = PeekWithoutTimeout(q, cursor, System.Messaging.PeekAction.Next)) != null)
                {
                    count++;
                }
            }

            // Return the tally.
            return count;
        }
    }
"@

# Add the new QueueSizer class helper to the environment.
Add-Type -TypeDefinition $qsource -ReferencedAssemblies C:\Windows\assembly\GAC_MSIL\System.Messaging\2.0.0.0__b03f5f7f11d50a3a\System.Messaging.dll

# Call the helper and get the message count.
[QueueSizer]::GetMessageCount('mymachine\private$\myqueue');

And it worked.

Fawnia answered 18/2, 2010 at 18:23 Comment(3)
+1 good answer. Also, not sure why, but I had to change "QueueSizer::" to "[QueueSizer]::" in the last line for this script to work.Fourchette
@Fourchette +1 The square brackets indicate a type. As you mentioned, is required. Because GetMessageCount is a static method, you need to reference the type directly. If it was an instance method you would have to create an object first like $qs = New-Object QueueSizer; then reference $qs.NetMessageCourt...Legman
this is a terrible answer as it's iterating through the messages and counting them up. Add 1,000,000 messages to your queue and run this and you'll see how long it takesEsmond
S
2

The solution provided by Irwin is less than idea.

There is a .GetAllMessages call you can make to have this done in one check, instead of a foreach loop.

$QueueName = "MycomputerName\MyQueueName" 
$QueuesFromDotNet =  new-object System.Messaging.MessageQueue $QueueName


If($QueuesFromDotNet.GetAllMessages().Length -gt $Curr)
{
    //Do Something
}

The .Length gives you the number of messages in the given queue.

Siobhansion answered 18/3, 2010 at 16:43 Comment(5)
but is this safe? won't both of these solutions potentially block other processes reading those queue's?Basting
When you have a queue with tens of thousands of messages, the performance of this method is terrible and is not a good option.Khalkha
Same here. This script takes giga bytes of memory for large messages and long queuesOrdinal
Take a moment and up vote these last two comments (I just did). Its a fair critique. For you two, what option worked for you? Did you up vote that option? Do you have a better option? I haven't done MSMQ work in some time and hope to never do it again :-)Siobhansion
"This is inefficient way to get the message count because it requires to read all messages and store them in array which is in memory. This can cause cause huge memory consumption depending on the frequency, size of the message and current number of messages in the queue and can impact performances of the application." Better use WMI: dejanstojanovic.net/aspnet/2018/january/…Watermark
M
2

following the presciptions from this link, you can use

$queues = Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue
$queues | ft -property Name,MessagesInQueue

to get the size of the local queues, or

$host = ...
$cred = get-credential
$queues = Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue -computer $host -credential $cred
$queues | ft -property Name,MessagesInQueue

for remote queues.

Malathion answered 22/7, 2014 at 14:9 Comment(1)
this worked perfectly on a old 2008 R2 server with none of the new cmdletsCreekmore
U
1

There are a set of MSMQ management cmdlets in the PowerShell Community Extensions. Give these a try and see if any of them help (probably Get-MSMQueue):

Clear-MSMQueue
Get-MSMQueue
New-MSMQueue
Receive-MSMQueue
Send-MSMQueue
Test-MSMQueue

Note: Try grabbing the beta 2.0 module-based distrubtion - just remember to "unblock" the zip before unzipping it.

Usage answered 18/2, 2010 at 16:39 Comment(2)
PCX requires is installed? I want only XCOPY deploment for execute in remote serverShinny
Later versions of PowerShell ship with the MSMQ module - although I don't know if that module is only available on Win8/Server2012.Usage
A
1

Use this for your c# block to get the count. It uses performance counter to query a single time:

public static int GetMessageCount(string machineName, string queuepath)
    {
        var queueCounter = new PerformanceCounter(
            "MSMQ Queue",
            "Messages in Queue",
            string.Format("{0}\\{1}", machineName, queuepath),
            machineName);

        return (int)queueCounter.NextValue();
    }

This is more efficient than a repetitive peek, as the work is mostly done on the remote machine, also more efficient than GetAllMessages, as this returns additional message data, then counts the elements - terrible performance at any real load.

Allodium answered 30/1, 2017 at 22:41 Comment(0)
P
1

I have been hunting high and low for information on accessing queues in a cluster.

For others trying to use powershell commands on clustered queues:

On one of the cluster nodes:

$env:computername = "MsmqHostName"
Get-MsmqQueue | Format-Table -Property QueueName,MessageCount

remote from the cluster:

Invoke-Command -ScriptBlock {$env:computername = "msmqHostName";Get-MsmqQueue | Format-Table -Property QueueName,MessageCount } -ComputerName ClusternNodeName
Prepense answered 30/10, 2019 at 14:43 Comment(0)
L
0

Try one of these...

function GetMessageCount2($queuename)
{
    $queuename = $env:computername + "\" + $queuename
    return (Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue | Where-Object -filterscript {$_.Name -eq $queuename}).MessagesinQueue 
}

function GetMessageCount3($queuename)
{
    return (Get-MsmqQueue | Where-Object -FilterScript {$_.QueueName -eq $queuename}).MessageCount
}
Lissa answered 12/11, 2014 at 22:32 Comment(0)
S
0

I was looking for a good answer to this, and while Irwin's answer put me on the right basis I was looking for some code that was a bit more Powershell-ish. The main reason for that is to deal with changes, as you can't Add-Type multiple times due to the type being loaded in the .Net runtime, and can't be unloaded without closing your powershell instance.

So I took his answer and came up with:

# Add the .NET assembly MSMQ to the environment.
[Reflection.Assembly]::LoadWithPartialName("System.Messaging") | out-Null

function Get-QueueNames([Parameter(Mandatory=$true)][String]$machineName, [String]$servicePrefix)
{
    [System.Messaging.MessageQueue]::GetPrivateQueuesByMachine($machineName) |
        ForEach-Object { $_.Path } | 
        Where-Object { $_ -like "*$($servicePrefix).*" } 
}

function Get-MessageCount([parameter(Mandatory=$true)][String]$queueName)
{
    function HasMessage
    {
        param
        (
            [System.Messaging.MessageQueue]$queue,
            [System.Messaging.Cursor]$cursor,
            [System.Messaging.PeekAction]$action
        )

        $hasMessage = $false
        try
        {
            $timeout = New-Object System.TimeSpan -ArgumentList 1
            $message = $queue.Peek($timeout, $cursor, $action)
            if ($message -ne $null)
            {
                $hasMessage = $true
            }
        }
        catch [System.Messaging.MessageQueueException]
        {
            # Only trap timeout related exceptions
            if ($_.Exception.Message -notmatch "timeout")
            {
                throw
            }
        }

        $hasMessage
    }

    $count = 0
    $queue = New-Object System.Messaging.MessageQueue -ArgumentList $queueName
    $cursor = $queue.CreateCursor()

    $action = [System.Messaging.PeekAction]::Current
    $hasMessage = HasMessage $queue $cursor $action
    while ($hasMessage)
    {
        $count++
        $action = [System.Messaging.PeekAction]::Next
        $hasMessage = HasMessage $queue $cursor $action
    }

    $count
}

$machineName = "."
$prefix = "something"

Get-QueueNames $machineName $prefix |
    ForEach-Object {
        New-Object PSObject -Property @{
            QueueName = $_
            MessageCount = Get-MessageCount $_
        }
    }

It can be optimized so that the first function returns queues instead of the queue names, but I needed both for different scenarios.

Silda answered 15/1, 2015 at 17:18 Comment(0)
A
0
winrm s winrm/config/client '@{TrustedHosts="yourIp"}'
$securePassword = ConvertTo-SecureString "YourPassword" -AsPlainText -force
$credential = New-Object System.Management.Automation.PsCredential("Domain\Usernama",$securePassword)  
$session = New-PSSession YourIP -credential $credential
$command = {Get-WmiObject Win32_PerfFormattedData_msmq_MSMQQueue | ft -property Name,MessagesinJournalQueue,MessagesInQueue | out-String} 
Invoke-Command -session $session -scriptblock $command

enter image description here

Algology answered 19/12, 2015 at 22:50 Comment(1)
Some comments would be useful here.Triable
T
0

This worked for me

[System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null

$AlertCount = 39

$queuePath = ".\private$\test.pdpvts.error"

$queue = New-Object System.Messaging.MessageQueue $queuePath


If($queue.GetAllMessages().Length -gt $AlertCount)
{
    Send-MailMessage -To "Me" -From "Alerts" -Subject "Message queue is full" -Credential mycridentials -UseSsl -SmtpServer mail.google.com
}
Turmoil answered 28/10, 2019 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.