Reputation: 13
The following code (used for looking up DNS TXT records) works fine in .NET 4, however I have had to downgrade the project to .NET 3.5 for various reasons, and now I am being presented with several errors
public String DnsGetTxtRecord(String name) {
const Int16 DNS_TYPE_TEXT = 0x0010;
const Int32 DNS_QUERY_STANDARD = 0x00000000;
const Int32 DNS_ERROR_RCODE_NAME_ERROR = 9003;
const Int32 DNS_INFO_NO_RECORDS = 9501;
var queryResultsSet = IntPtr.Zero;
try {
var dnsStatus = DnsQuery(
name,
DNS_TYPE_TEXT,
DNS_QUERY_STANDARD,
IntPtr.Zero,
ref queryResultsSet,
IntPtr.Zero
);
if (dnsStatus == DNS_ERROR_RCODE_NAME_ERROR || dnsStatus == DNS_INFO_NO_RECORDS)
return null;
if (dnsStatus != 0)
throw new Win32Exception(dnsStatus);
DnsRecordTxt dnsRecord;
for (var pointer = queryResultsSet; pointer != IntPtr.Zero; pointer = dnsRecord.pNext) {
dnsRecord = (DnsRecordTxt) Marshal.PtrToStructure(pointer, typeof(DnsRecordTxt));
if (dnsRecord.wType == DNS_TYPE_TEXT) {
var lines = new List<String>();
var stringArrayPointer = pointer
+ Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32();
for (var i = 0; i < dnsRecord.dwStringCount; ++i) {
var stringPointer = (IntPtr) Marshal.PtrToStructure(stringArrayPointer, typeof(IntPtr));
lines.Add(Marshal.PtrToStringUni(stringPointer));
stringArrayPointer += IntPtr.Size;
}
return String.Join(Environment.NewLine, lines);
}
}
return null;
}
finally {
const Int32 DnsFreeRecordList = 1;
if (queryResultsSet != IntPtr.Zero)
DnsRecordListFree(queryResultsSet, DnsFreeRecordList);
}
}
[DllImport("Dnsapi.dll", EntryPoint = "DnsQuery_W", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
static extern Int32 DnsQuery(String lpstrName, Int16 wType, Int32 options, IntPtr pExtra, ref IntPtr ppQueryResultsSet, IntPtr pReserved);
[DllImport("Dnsapi.dll")]
static extern void DnsRecordListFree(IntPtr pRecordList, Int32 freeType);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct DnsRecordTxt {
public IntPtr pNext;
public String pName;
public Int16 wType;
public Int16 wDataLength;
public Int32 flags;
public Int32 dwTtl;
public Int32 dwReserved;
public Int32 dwStringCount;
public String pStringArray;
}
var stringArrayPointer = pointer + Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32();
triggers "Operator + cannot be applied to operands IntPtr and int
return String.Join(Environment.NewLine, lines);
returns "The best overload method has some invalid arguments"
Assistance appreciated!
Upvotes: 0
Views: 3195
Reputation: 8816
You can't add to an IntPtr
, but you can do this:
long address = pointer.ToInt64()
+ Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt64();
var stringArrayPointer = (IntPtr)address;
(as @xanatos said in his answer, use ToInt64 instead of ToInt32 to make it work in x64 as well as x86.)
As for the problem with the String.Join
, you need to pass it an array as the second argument:
string.Join(Environment.NewLine, lines.ToArray());
Upvotes: 0
Reputation: 111950
This
var stringArrayPointer = pointer + Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32();
can be changed to
var stringArrayPointer = (IntPtr)((long)pointer + Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32());
(in .NET 4.0 they added some basic operations to IntPtr
, but in .NET 2.0/3.5 you had to cast it to int
/long
. Casting to long
is safer because it's 64bit ready)
This
return String.Join(Environment.NewLine, lines);
to
return String.Join(Environment.NewLine, lines.ToArray());
(In .NET 4.0 they added a String.Join
overload that accepted a IEnumerable<string>
as a parameter. In .NET 2.0/3.5 you had to use the overload that accepts a string[]
)
Upvotes: 3