One of my customers was trying to read a queue item within a DTC transactional context from a remote queue. He then aborted the transaction and expected to see the message back in the queue. However, that did not happen. He was trying to read from a Windows 7 machine. MSMQ was installed on a Windows Server 2008 R2 virtual machine. Both the machines were in the same domain.

He was using the code mentioned below :

using (var tran1 = new TransactionScope(TransactionScopeOption.Required))
            { 
                using (var q1 = new MessageQueue(@"FormatName:Direct=OS:MachineName\private$\QueueName",true))
                {
                    q1.Formatter = new BinaryMessageFormatter();
                    var newmsg1 = new MqProcessData(q1.Receive(MessageQueueTransactionType.Single).Body as string);
    
Assert.AreEqual(mqdata.EntityID, newmsg1.EntityID);

//                   Transaction.Current.Rollback();
                    q1.Close();
                }
            }

Using MessageQueueTransactionType.Single immediately commits after doing a remote read. So messages will not show up in the remote queue even if you try to abort the transaction.

So, here’s what I did.

I created test queue on a machine and posted 3 messages to that queue. From another machine, I ran the code mentioned below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;
using System.Transactions; 

namespace ConsoleApplication1
{
   
class Program   
{
        static void Main(string[] args)
        {
            using (var scope = new TransactionScope())
            using (var queue = new MessageQueue("formatname:Direct=OS:MachineName\\private$\\QueueName", true))
            {
                // timeout should be higher for remote queues
                // transaction type should be None for non-transactional queues
                var message = queue.Receive(TimeSpan.FromMilliseconds(0), MessageQueueTransactionType.Automatic);
                // ...normal processing...                
scope.Complete();
            }
        }
}

At the mentioned below line of the code, I placed a breakpoint and started executing the code.

var message = queue.Receive(TimeSpan.FromMilliseconds(0), MessageQueueTransactionType.Automatic);

When I reached the above line, the message was read from the queue.
I did not allow scope.Complete(); to be executed. 
I then stopped debugging (as good as aborting the transaction) and I could see message back in the remote queue.

Note: MSMQ 4.0 introduced transactional remote receive as a native feature. An application that wants to perform a transactional remote receive can simply perform the remote receive in the context of a local Distributed Transaction Coordinator (MS DTC) transaction. 

Written by
Kshitij Dattani

Reviewed by
Jainath Ramanathan

Microsoft India GTSC