Created
September 29, 2014 16:20
-
-
Save russgray/4748c3f1815f6f2f273d to your computer and use it in GitHub Desktop.
Marshalling a variable-length array from unmanaged code in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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