Unmanaged API “fun”, finding out MSMQ subqueues names in C#
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 */
}
}
Comments
Thank you for sharing, too bad subqueues are not supported in MSMQ 3.0.
Comment preview