Software design and other ramblings RSS 2.0
# Wednesday, 06 May 2009

I've seen a few newsgroups with SSIS developers having difficulties enlisting in the active transaction with a custom Data Flow component in .NET. I recently had to do this to enable message queue operations to be performed within the active transaction for the package. Unfortunately the built in Message Queue task has no support for enlisting in the active transaction so a custom approach was required. I'm sure that most SSIS developers who have dabbled with custom components will have seen the following Microsoft.SqlServer.Dts.Pipeline.PipelineComponent class:

/// <summary>
/// Establishes a connection to a connection manager
/// </summary>
public virtual void AcquireConnections (object transaction)

This method can be overriden a custom component and used to establish any required connections. Now we know that SSIS uses DTC transactions and that MSMQ supports DTC but how do the two map together? What is the transaction object that we are passed in? There seems to be very little documentation on this subject available on the internet so I experimented with the debugger and found a way to map this through to .NET. The first stage is to convert the supplied object into a managed System.Transactions.Transaction object. This can be achieved with the following code:

/// <summary>
/// Get a <code>System.Transactions.Transaction</code> from a supplied COM transaction pointer
/// </summary>
public static Transaction GetTransactionFromCom(object comTransaction)
{
    Transaction managedTransaction = null;

    ITransaction transactionInterface = comTransaction as ITransaction;

    if (transactionInterface != null)
    {
        managedTransaction =
            TransactionInterop.GetTransactionFromDtcTransaction((IDtcTransaction)transactionInterface);
    }

    return managedTransaction;
}

Once the supplied COM transaction pointer has been converted to a managed Transaction object it can be used as normal with your .NET code. For message queue transactions this means setting the ambient transaction and sending/receiving messages using MessageQueueTransactionType.Automatic. For example:

// Save the current transaction
Transaction previousTransaction = Transaction.Current;

// Set the current ambient transaction
Transaction.Current = managedTransaction;

try
{
    using (MessageQueue queue = new MessageQueue(messageQueuePath))
    {
        queue.Send("Body", "Label", MessageQueueTransactionType.Automatic);
    }
}
finally
{
    Transaction.Current = previousTransaction;
}
Wednesday, 06 May 2009 21:04:57 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
.NET | C#

I recently needed to access the Label property of a remote private MSMQ through .NET. Naturally my first thought was that this should be a pretty trivial operation as this property is exposed via the MessageQueue (System.Messaging) class in the .NET framework. Unfortunately however, this property can only be read for local private queues (those on the same machine as the executing code) not for remote private queues. According to Microsoft "Private queues are registered on the local computer, not in the directory service, and their properties cannot be obtained by Message Queuing applications running on remote computers".

After a bit of testing, I found that it is possible to access remote private queue properties through .NET using DCOM. The following example shows how this can be achieved for the Label property. The same technique can be applied to retrieve other queue properties such as the Transactional state of the queue.

public static string GetRemoteQueueLabel(MessageQueue queue, string serverAddress)
{
    // Find label for remote private queue
    object queueInfoDcom = Activator.CreateInstance(Type.GetTypeFromProgID("MSMQ.MSMQQueueInfo", serverAdress));

    Type classType = queueInfoDcom.GetType();

    // Invoke method using reflection and return outputs
    BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;

    classType.InvokeMember("FormatName", bindingFlags, null, queueInfoDcom, new object[] { queue.FormatName });

    bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod;

    classType.InvokeMember("Refresh", bindingFlags, null, queueInfoDcom, new object[] { });

    bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty;

    string label = (string)classType.InvokeMember("Label", bindingFlags, null, queueInfoDcom, new object[] { });

    return label;
}
Wednesday, 06 May 2009 20:37:55 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
.NET | C#
# Wednesday, 02 July 2008

The other day I had to update a existing windows GUI application to enable it to be called via the command line. In order to do this, you need to add the following import to your .NET application (C# example shown):

/// <summary>
/// AttachConsole gives the ability for a GUI application to write to the console window of the console from which it was started.
/// </summary>
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool AttachConsole(uint dwProcessId);

/// <summary>
/// Flag indicating that we should attach to parent console
/// </summary>
const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;

After adding these declarations to your main class, its a simple case of adapting your Main method to detect if you're running in console or GUI mode and acting accordingly. E.g:

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
     // Are we in console mode
     bool isConsole = args.Length > 0;

     if (!isConsole)
     {
          RunVisual();
     }
     else
     {
          // Attach to console
          AttachConsole(ATTACH_PARENT_PROCESS);

          // Run console app
          RunConsole(args);
     }
}
Wednesday, 02 July 2008 16:31:56 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
.NET | C# | Software Development
Top Cashback
TopCashback
Archive
<2017 December>
SunMonTueWedThuFriSat
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2017 Scott Hill

Sign In
Statistics
Total Posts: 8
This Year: 0
This Month: 0
This Week: 0
Comments: 2
All Content © 2017, Scott Hill