Skip to content

Instantly share code, notes, and snippets.

@russgray
Created September 29, 2014 16:20
Show Gist options
  • Save russgray/4748c3f1815f6f2f273d to your computer and use it in GitHub Desktop.
Save russgray/4748c3f1815f6f2f273d to your computer and use it in GitHub Desktop.
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