Skip to content

Instantly share code, notes, and snippets.

@ramonsmits
Last active April 19, 2024 12:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramonsmits/3af6623436a443eb28f8a54fdd7688cf to your computer and use it in GitHub Desktop.
Save ramonsmits/3af6623436a443eb28f8a54fdd7688cf to your computer and use it in GitHub Desktop.
Powershell script to move MSMQ messages between queues with support for transactions and partial sets based on size.
<#
.SYNOPSIS
Moves messages between queues.
.DESCRIPTION
Moves messages between queues, move isn't really the right word as moving can only occur
between a queue and its sub-queues but its the Verb allowed by Powershell.
Is allows moving messages between queues of different transaction type.
It also supports to move messages in a single transaction. It does not prevent to send
non-transactional to a transactional queue in a transactional. This means that the
receives are not participating in the transaction and are lost if the transaction commit
would fail!
.PARAMETER SourcePath
The location of the source queue.
.PARAMETER DestinationPath
The location of the destination queue.
.PARAMETER Size
Moves a maximum number messages.
.EXAMPLE
# Move all messages
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b"
.EXAMPLE
# Move all messages in a transaction, only safe if both queues are transactional!
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" -Transactional
.EXAMPLE
# Move 10 messages
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" -BatchSize 10
.EXAMPLE
# Move 10 messages in a transaction, only safe if both queues are transactional!
Move-MsmqMessages ".\PRIVATE$\queue-a" ".\PRIVATE$\queue-b" -BatchSize 10 -Transactional
#>
Set-StrictMode -Version Latest
Add-Type -AssemblyName System.Messaging
Add-Type -AssemblyName System.Transactions
Function Move-MsmqMessages {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $SourcePath,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string] $DestinationPath,
[switch] $Transactional,
[int] $Size
)
$sourceQueue = New-Object System.Messaging.MessageQueue -Argumentlist $sourcePath, [System.Messaging.QueueAccessMode]::Receive
$destinationQueue = New-Object System.Messaging.MessageQueue -Argumentlist $destinationPath, [System.Messaging.QueueAccessMode]::Send
$scope = New-Object System.Transactions.TransactionScope
$transactionType = If ($Transactional) { [System.Messaging.MessageQueueTransactionType]::Automatic } Else { [System.Messaging.MessageQueueTransactionType]::Single }
try {
$count = 0;
$msgEnum = $sourceQueue.GetMessageEnumerator2()
foreach ($item in $msgEnum) {
$count++
if ($sourceQueue.Transactional) {
$msg = $sourceQueue.Receive($transactionType) # Not using `ReceiveById(id)` or `$sourceQueue.RemoveCurrent($transactionType)`
}
else {
$msg = $sourceQueue.Receive() # Not using `$item.RemoveCurrent() as that breaks the foreach "When you remove the current message, the cursor is moved to the next message".
}
if ($destinationQueue.Transactional) {
$destinationQueue.Send($msg, $transactionType)
}
else {
$destinationQueue.Send($msg)
}
if (($Size -gt 0) -and ($count -ge $Size)) {
break
}
}
if ($transactional) {
$scope.Complete();
}
return $count;
}
finally {
$msgEnum.Dispose()
$scope.Dispose()
$sourceQueue.Dispose()
$destinationQueue.Dispose()
}
return 0;
}
@NicolaiThagaard
Copy link

Excellent! I was browsing and stumbled upon your fine script. It really saved me some hours of scripting. Thank you @ramonsmits!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment