Unmanaged API “fun”, finding out MSMQ subqueues names in C#

time to read 26 min | 5169 words

I needed to get a list of subqueues of a queue, and there is no way of actually doing that in managed code. I decided that I can still use P/Invoke and just make the appropriate unmanaged calls. In hindsight, it would have been significantly simpler to just build an unmanaged DLL in C++/CLR, but I was already too deep, and I can never remember how to properly compile a C++ app anymore.

I finally had to slap myself a couple of times and remind me that I was a C++ developer for a long time, and I damn well should remember how to treat memory like it was something both sacred and abused. I had tested that on a Windows 2008 64 bits machine. During this project, I also found out that there is basically no information at all about this sort of thing, so I am putting the code here.

public class MsmqUtil
{
    internal const int PROPID_MGMT_QUEUE_SUBQUEUE_NAMES = 27;
    internal const ushort VT_NULL = 1;
 
    public static unsafe string[] GetMsmqSubQueueNames(string queueFormatName)
    {
        var props = new MQMGMTPROPS {cProp = 1};
        var variant = new MQPROPVariant {vt = VT_NULL};
        try
        {
            props.aPropID = Marshal.AllocHGlobal(sizeof (int));
            Marshal.WriteInt32(props.aPropID, PROPID_MGMT_QUEUE_SUBQUEUE_NAMES);
 
            props.aPropVar = Marshal.AllocHGlobal(Marshal.SizeOf(typeof (MQPROPVariant)));
            Marshal.StructureToPtr(new MQPROPVariant {vt = VT_NULL}, props.aPropVar, false);
 
            props.status = Marshal.AllocHGlobal(sizeof (int));
            Marshal.WriteInt32(props.status, 0);
 
            var result = MQMgmtGetInfo(null, "queue=" + queueFormatName, ref props);
            if (result != 0)
                throw new Win32Exception(result);
 
            if (Marshal.ReadInt32(props.status) != 0)
            {
                return null;
            }
 
            variant = (MQPROPVariant) Marshal.PtrToStructure(props.aPropVar, typeof (MQPROPVariant));
 
            var subQueues = new List<string>();
            for (int i = 0; i < variant.Val.calpwstr.cElems; i++)
            {
                var item = new string(variant.Val.calpwstr.pElems[i]);
                subQueues.Add(item);
            }
            return subQueues.ToArray();
        }
        finally
        {
            if (variant.vt != VT_NULL)
            {
                for (var i = 0; i < variant.Val.calpwstr.cElems; i++)
                {
                    MQFreeMemory(variant.Val.calpwstr.pElems[i]);
                }
                MQFreeMemory(variant.Val.calpwstr.pElems);
            }
            Marshal.FreeHGlobal(props.aPropID);
            Marshal.FreeHGlobal(props.aPropVar);
            Marshal.FreeHGlobal(props.status);
        }
    }
 
    [DllImport("mqrt.dll")]
    internal static extern int MQMgmtGetInfo([MarshalAs(UnmanagedType.BStr)] string computerName,
                                             [MarshalAs(UnmanagedType.BStr)] string objectName,
                                             ref MQMGMTPROPS mgmtProps);
 
    [DllImport("mqrt.dll")]
    internal static extern unsafe int MQFreeMemory(void* queue);
 
    [StructLayout(LayoutKind.Sequential)]
    internal struct CALPWSTR
    {
        public uint cElems;
        public unsafe char** pElems;
    }
 
    [StructLayout(LayoutKind.Sequential)]
    internal struct MQMGMTPROPS
    {
        public uint cProp;
        public IntPtr aPropID;
        public IntPtr aPropVar;
        public IntPtr status;
    }
 
    [StructLayout(LayoutKind.Sequential, Size = 16)]
    internal struct MQPROPVariant
    {
        public ushort vt;
        public ushort wReserved1;
        public ushort wReserved2;
        public ushort wReserved3;
        public UnionedVariant Val; //8
    }
 
    [StructLayout(LayoutKind.Explicit, Size = 8)]
    internal struct UnionedVariant
    {
        [FieldOffset(0)] public uint ulVal; /* VT_UI4    */
        [FieldOffset(0)] public CALPWSTR calpwstr; /* VT_VECTOR | VT_LPWSTR  */
    }
}