Следующий код (используемый для поиска записей DNS TXT) отлично работает в .NET 4, однако мне пришлось по разным причинам понизить версию проекта до .NET 3.5, и теперь мне показывают несколько ошибок.

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(); триггеры "Оператор + не может применяться к операндам IntPtr и int

return String.Join(Environment.NewLine, lines); возвращает «Лучший метод перегрузки имеет недопустимые аргументы»

Помощь приветствуется!

0
jim 25 Фев 2015 в 15:28

2 ответа

Лучший ответ

Этот

var stringArrayPointer = pointer + Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32();

Можно изменить на

var stringArrayPointer = (IntPtr)((long)pointer + Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt32());

(в .NET 4.0 они добавили некоторые базовые операции к IntPtr, но в .NET 2.0 / 3.5 вы должны были преобразовать его в int / long. Преобразование в long выполняется безопаснее, потому что он готов к 64 бит)

Этот

return String.Join(Environment.NewLine, lines);

К

return String.Join(Environment.NewLine, lines.ToArray());

(В .NET 4.0 они добавили перегрузку String.Join, которая принимала IEnumerable<string> в качестве параметра. В .NET 2.0 / 3.5 вам приходилось использовать перегрузку, которая принимает string[])

2
xanatos 25 Фев 2015 в 12:31

Вы не можете добавить в IntPtr, но вы можете сделать это:

long address = pointer.ToInt64()
             + Marshal.OffsetOf(typeof(DnsRecordTxt), "pStringArray").ToInt64();
var stringArrayPointer = (IntPtr)address;

(как сказал @xanatos в своем ответе, используйте ToInt64 вместо ToInt32, чтобы он работал как в x64, так и в x86.)


Что касается проблемы с String.Join, вам нужно передать ему массив в качестве второго аргумента:

string.Join(Environment.NewLine, lines.ToArray());
0
Wai Ha Lee 25 Фев 2015 в 12:42