Marshalling a variable-length array from unmanaged code in C#
using System; | |
using System.Collections.Generic; | |
using System.Runtime.InteropServices; | |
namespace DnsMarshallingTest | |
{ | |
#region Data structures | |
[Flags] | |
internal enum DnsRecordType : ushort | |
{ | |
TEXT = 0x0010, | |
} | |
[Flags] | |
internal enum DnsQueryType : uint | |
{ | |
STANDARD = 0x00000000, | |
} | |
internal enum DnsFreeType : uint | |
{ | |
FreeFlat = 0, | |
FreeRecordList | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
internal struct DnsRecord | |
{ | |
public IntPtr Next; // 4 bytes | |
public string Name; // 4 bytes (ptr) | |
[MarshalAs(UnmanagedType.U2)] public DnsRecordType RecordType; // 2 bytes | |
[MarshalAs(UnmanagedType.U2)] public ushort DataLength; // 2 bytes | |
[StructLayout(LayoutKind.Explicit)] // 4 bytes | |
internal struct DnsRecordFlags | |
{ | |
[FieldOffset(0)] [MarshalAs(UnmanagedType.U4)] public uint DW; | |
[FieldOffset(0)] [MarshalAs(UnmanagedType.U4)] public uint S; | |
} | |
public DnsRecordFlags Flags; | |
[MarshalAs(UnmanagedType.U4)] public uint Ttl; // 4 bytes | |
[MarshalAs(UnmanagedType.U4)] public uint Reserved; // 4 bytes | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
internal struct DNS_TXT_DATA | |
{ | |
public uint dwStringCount; | |
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.SysUInt)] public IntPtr[] pStringArray; | |
} | |
#endregion | |
internal class Program | |
{ | |
#region Imports | |
[DllImport("dnsapi", EntryPoint = "DnsQuery_A")] | |
private static extern uint DnsQuery( | |
[MarshalAs(UnmanagedType.LPStr)] string Name, | |
[MarshalAs(UnmanagedType.U2)] DnsRecordType Type, | |
[MarshalAs(UnmanagedType.U4)] DnsQueryType Options, | |
IntPtr Servers, | |
[In, Out] ref IntPtr QueryResultsSet, | |
IntPtr Reserved | |
); | |
[DllImport("dnsapi", EntryPoint = "DnsRecordListFree")] | |
private static extern void DnsRecordListFree(IntPtr RecordList, DnsFreeType FreeType); | |
#endregion | |
private static void Main(string[] args) | |
{ | |
var domain = "enter-your-own-value-here"; | |
var pServers = IntPtr.Zero; | |
var ppQueryResultsSet = IntPtr.Zero; | |
var ret = DnsQuery(domain, DnsRecordType.TEXT, DnsQueryType.STANDARD, pServers, ref ppQueryResultsSet, | |
IntPtr.Zero); | |
if (ret != 0) throw new ApplicationException("DnsQuery failed: " + ret); | |
// Marshal DnsRecord and advance pointer to start of DNS_TXT_DATA record | |
var dnsRecord = (DnsRecord) Marshal.PtrToStructure(ppQueryResultsSet, typeof (DnsRecord)); | |
var ptr = new IntPtr(ppQueryResultsSet.ToInt32() + Marshal.SizeOf(dnsRecord)); | |
// Marshal DNS_TXT_DATA and any pointers beyond pStringArray | |
var txtData = (DNS_TXT_DATA) Marshal.PtrToStructure(ptr, typeof (DNS_TXT_DATA)); | |
ptr = new IntPtr(ptr.ToInt32() + sizeof (uint)); | |
var ptrs = new IntPtr[txtData.dwStringCount]; | |
Marshal.Copy(ptr, ptrs, 0, ptrs.Length); | |
// Each pointer in ptrs points to a string. Marshal those too. | |
var strings = new List<string>(); | |
for (var i = 0; i < ptrs.Length; ++i) | |
{ | |
strings.Add(Marshal.PtrToStringAnsi(ptrs[i])); | |
} | |
DnsRecordListFree(ppQueryResultsSet, DnsFreeType.FreeRecordList); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment