首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PInvoke DnsQueryEx异步

PInvoke DnsQueryEx异步
EN

Stack Overflow用户
提问于 2014-07-09 22:30:50
回答 1查看 928关注 0票数 2

我刚开始做编组工作,我很乐意听取你的任何建议。

在Windows8和Windows 2012中,我们有一个新的Windows函数:DnsQueryEx

它允许异步地进行DNS查询。

使用C#,我试图调用它以接收MX主机记录。

作为标准,System.Net.DNS缺少对用UDP检索MX记录的支持。

在使用此示例之前,我已经成功地调用了DnsQuery API函数:

http://www.pinvoke.net/default.aspx/dnsapi.DnsQuery

不幸的是,没有关于DnsQueryEx的例子,我花了一些时间用可能的参数来规划,但是我想我无法找到我如何需要构建DnsAddressArray param。

为什么我需要异步调用它--因为我开发了具有良好套接字性能的异步SMTP服务器,并且我使用DnsQuery函数检索MX记录,但在从异步上下文调用期间,我不时地在那里得到DNS错误异常。

现在,当我调用DnsQueryEx应用程序时,进程会意外地终止,在我得到错误的87之前,这意味着-错误的参数。

另外,我无法了解如何将字符串作为结构的一部分封送到非托管代码?

这是我的代码:

代码语言:javascript
复制
private enum QueryOptions
    {
        DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
        DNS_QUERY_BYPASS_CACHE = 8,
        DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
        DNS_QUERY_NO_HOSTS_FILE = 0x40,
        DNS_QUERY_NO_LOCAL_NAME = 0x20,
        DNS_QUERY_NO_NETBT = 0x80,
        DNS_QUERY_NO_RECURSION = 4,
        DNS_QUERY_NO_WIRE_QUERY = 0x10,
        DNS_QUERY_RESERVED = -16777216,
        DNS_QUERY_RETURN_MESSAGE = 0x200,
        DNS_QUERY_STANDARD = 0,
        DNS_QUERY_TREAT_AS_FQDN = 0x1000,
        DNS_QUERY_USE_TCP_ONLY = 2,
        DNS_QUERY_WIRE_ONLY = 0x100
    }

    private enum QueryTypes
    {
        DNS_TYPE_MX = 15
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct QueryContextStruct
    {
        public int               RefCount;
        public IntPtr                QueryName;
        public short                QueryType;
        public ulong               QueryOptions;
        public DnsQueryResult    QueryResults;
        public CancelHandle    QueryCancelContext;
        public IntPtr              QueryCompletedEvent;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct DNSQueryRequest
    {
        public uint Version;
        //public string QueryName;
        public QueryTypes QueryType;
        public QueryOptions QueryOptions;
        public IntPtr pDnsServerList;
        public uint InterfaceIndex;
        public IntPtr pQueryCompletionCallback;
        public IntPtr pQueryContext;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DnsAddr {
        IntPtr  MaxSa;
        IntPtr DnsAddrUserDword;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DnsAddrArray
    {
        public uint       MaxCount;
        public uint           AddrCount;
        public uint           Tag;
        public ushort            Family;
        public ushort            WordReserved;
        public uint           Flags;
        public uint           MatchFlag;
        public uint           Reserved1;
        public uint           Reserved2;

        public IntPtr AddrArray;
    }

    [StructLayout(LayoutKind.Sequential)]
    private class DnsQueryResult
    {
        public uint Version;
        public int QueryStatus;
        public ulong QueryOptions;
        public IntPtr pQueryRecords;
        public IntPtr reserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    private class CancelHandle
    {
        public byte Handle = 32;
    }
    [DllImport("dnsapi")]
    private static extern int DnsQueryEx(IntPtr pQueryRequest, IntPtr pQueryResults, IntPtr pCancelHandle);

    delegate void DnsReceivedResultDelegate(IntPtr parameter);

    public static void ReceiveMxAsync(string domain)
    {
        // Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
        Thread.BeginThreadAffinity();
        GCHandle requestHandle = default(GCHandle);
        GCHandle resultHandle = default(GCHandle);
        GCHandle dnsListHandle = default(GCHandle);
        GCHandle dnsHandle = default(GCHandle);
        GCHandle cancelHandle = default(GCHandle);
        DnsReceivedResultDelegate changeDelegate = ReceivedStatusChangedEvent;
        IntPtr hSCM = IntPtr.Zero;
        IntPtr hService = IntPtr.Zero;
        try
        {
            //request
            DNSQueryRequest queryRequest = new DNSQueryRequest();
            queryRequest.Version = 0x1;
            queryRequest.QueryType = QueryTypes.DNS_TYPE_MX;
            queryRequest.QueryOptions = QueryOptions.DNS_QUERY_BYPASS_CACHE;
            DnsAddr address = new DnsAddr();
            dnsHandle = GCHandle.Alloc(address, GCHandleType.Pinned);
            IntPtr DnsStructure = dnsHandle.AddrOfPinnedObject();

            //getting server list
            DnsAddrArray addressArray = new DnsAddrArray();
            addressArray.MaxCount = 1;
            addressArray.AddrCount = 1;
            addressArray.AddrArray = DnsStructure;
            dnsListHandle = GCHandle.Alloc(addressArray, GCHandleType.Pinned);
            IntPtr pinnedDnsStructure = dnsListHandle.AddrOfPinnedObject();
            queryRequest.pDnsServerList = pinnedDnsStructure;
            queryRequest.InterfaceIndex = 0;
            queryRequest.pQueryCompletionCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);
            requestHandle = GCHandle.Alloc(queryRequest, GCHandleType.Pinned);
            IntPtr pinnedRequestStructure = requestHandle.AddrOfPinnedObject();

            //result
            DnsQueryResult result = new DnsQueryResult();
            result.Version = 0x1;
            resultHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
            IntPtr pinnedResultStructure = resultHandle.AddrOfPinnedObject();

            //cancel handle
            CancelHandle cancel = new CancelHandle();
            cancelHandle = GCHandle.Alloc(cancel, GCHandleType.Pinned);
            IntPtr pinnedCancleStructure = cancelHandle.AddrOfPinnedObject();

            var res = DnsQueryEx(pinnedRequestStructure, pinnedResultStructure, pinnedCancleStructure);

            //timeout
            //SleepEx(5000, true);
            if (res > 0)
                return;
        }
        catch (Exception ex) 
        {
            if (ex != null)
                return;
        }
        finally
        {
            GC.KeepAlive(changeDelegate);
            if (dnsListHandle != default(GCHandle))
            {
                dnsListHandle.Free();
            }
            if (dnsHandle != default(GCHandle))
            {
                dnsHandle.Free();
            }
            if (requestHandle != default(GCHandle))
            {
                requestHandle.Free();
            }
            if (resultHandle != default(GCHandle))
            {
                resultHandle.Free();
            }
            if (cancelHandle != default(GCHandle))
            {
                cancelHandle.Free();
            }
            Thread.EndThreadAffinity();
        }
    }

    private static void DnsReceivedResultEvent(IntPtr parameter, DnsQueryResult result)
    {

    }
    //==================================================

更新:我更改了代码如下:

代码语言:javascript
复制
 private enum QueryOptions
    {
        DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE = 1,
        DNS_QUERY_BYPASS_CACHE = 8,
        DNS_QUERY_DONT_RESET_TTL_VALUES = 0x100000,
        DNS_QUERY_NO_HOSTS_FILE = 0x40,
        DNS_QUERY_NO_LOCAL_NAME = 0x20,
        DNS_QUERY_NO_NETBT = 0x80,
        DNS_QUERY_NO_RECURSION = 4,
        DNS_QUERY_NO_WIRE_QUERY = 0x10,
        DNS_QUERY_RESERVED = -16777216,
        DNS_QUERY_RETURN_MESSAGE = 0x200,
        DNS_QUERY_STANDARD = 0,
        DNS_QUERY_TREAT_AS_FQDN = 0x1000,
        DNS_QUERY_USE_TCP_ONLY = 2,
        DNS_QUERY_WIRE_ONLY = 0x100
    }
    private enum QueryTypes
    {
        DNS_TYPE_MX = 15
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct QueryContextStruct
    {
        public int               RefCount;
        public IntPtr                QueryName;
        public short                QueryType;
        public ulong               QueryOptions;
        public DnsQueryResult    QueryResults;
        public CancelHandle    QueryCancelContext;
        public IntPtr              QueryCompletedEvent;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct DNSQueryRequest
    {
        public uint Version;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string QueryName;
        public QueryTypes QueryType;
        public QueryOptions QueryOptions;
        public IntPtr pDnsServerList;
        public uint InterfaceIndex;
        public IntPtr pQueryCompletionCallback;
        public IntPtr pQueryContext;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DnsAddr
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32500)] //todo: DNS_ADDR_MAX_SOCKADDR_LENGTH
        public byte[] MaxSa;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public uint[] DnsAddrUserDword;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DnsAddrArray
    {
        public uint MaxCount;
        public uint AddrCount;
        public uint Tag;
        public ushort Family;
        public ushort WordReserved;
        public uint Flags;
        public uint MatchFlag;
        public uint Reserved1;
        public uint Reserved2;
        public DnsAddr AddrArray;
    }

    [StructLayout(LayoutKind.Sequential)]
    private class DnsQueryResult
    {
        public uint Version;
        public int QueryStatus;
        public ulong QueryOptions;
        public IntPtr pQueryRecords;
        public IntPtr reserved;
    }

    public unsafe struct CancelHandle
    {
        private fixed byte Reserved[32];
    }

    [DllImport("dnsapi")]
    private static extern int DnsQueryEx(IntPtr pQueryRequest, IntPtr pQueryResults, IntPtr pCancelHandle);

    delegate void DnsReceivedResultDelegate(IntPtr parameter);

    public static void ReceiveMxAsync(string domain)
    {
        // Ensure that this thread's identity is mapped, 1-to-1, with a native OS thread.
        Thread.BeginThreadAffinity();
        GCHandle requestHandle = default(GCHandle);
        GCHandle resultHandle = default(GCHandle);
        GCHandle dnsListHandle = default(GCHandle);
        GCHandle dnsHandle = default(GCHandle);
        GCHandle cancelHandle = default(GCHandle);
        DnsReceivedResultDelegate changeDelegate = ReceivedStatusChangedEvent;
        IntPtr hSCM = IntPtr.Zero;
        IntPtr hService = IntPtr.Zero;
        try
        {
            //request
            DNSQueryRequest queryRequest = new DNSQueryRequest();
            queryRequest.Version = 0x1;
            queryRequest.QueryName = domain;
            queryRequest.QueryType = QueryTypes.DNS_TYPE_MX;
            queryRequest.QueryOptions = QueryOptions.DNS_QUERY_BYPASS_CACHE;
            queryRequest.pDnsServerList = Marshal.AllocHGlobal(Marshal.SizeOf(queryRequest.pDnsServerList));
            queryRequest.InterfaceIndex = 0;
            queryRequest.pQueryCompletionCallback = Marshal.GetFunctionPointerForDelegate(changeDelegate);

            var dnsServerAddrList = new DnsAddrArray();
            dnsServerAddrList.MaxCount = (uint)Marshal.SizeOf(dnsServerAddrList);
            dnsServerAddrList.AddrArray = new DnsAddr();
            Marshal.StructureToPtr(dnsServerAddrList, queryRequest.pDnsServerList, true);

            IntPtr pinnedRequestStructure = Marshal.AllocHGlobal(Marshal.SizeOf(queryRequest));

            //result
            DnsQueryResult result = new DnsQueryResult();
            result.Version = 0x1;
            resultHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
            IntPtr pinnedResultStructure = resultHandle.AddrOfPinnedObject();

            //cancel handle
            CancelHandle cancel = new CancelHandle();
            cancelHandle = GCHandle.Alloc(cancel, GCHandleType.Pinned);
            IntPtr pinnedCancleStructure = cancelHandle.AddrOfPinnedObject();

            //getting server list
            var res = DnsQueryEx(pinnedRequestStructure, pinnedResultStructure, pinnedCancleStructure);

            //timeout
            //SleepEx(5000, true);
            if (res > 0)
                return;
        }
        catch (Exception ex) 
        {
            if (ex != null)
                return;
        }
        finally
        {
            GC.KeepAlive(changeDelegate);
            if (dnsListHandle != default(GCHandle))
            {
                dnsListHandle.Free();
            }
            if (dnsHandle != default(GCHandle))
            {
                dnsHandle.Free();
            }
            if (requestHandle != default(GCHandle))
            {
                requestHandle.Free();
            }
            if (resultHandle != default(GCHandle))
            {
                resultHandle.Free();
            }
            if (cancelHandle != default(GCHandle))
            {
                cancelHandle.Free();
            }
            Thread.EndThreadAffinity();
        }
    }

    private static void DnsReceivedResultEvent(IntPtr parameter, DnsQueryResult result)
    {
        //SUCESS!
    }
    //==================================================

如果运气不好,还犯了87错误.我想我需要插入AddrArray结构与正确的服务器参数,我会感谢任何帮助.这里我们有C++工作示例:

应该可以将它用作C++库,但我想避免再使用一个网关,不幸的是,我现在正在C++.

EN

回答 1

Stack Overflow用户

发布于 2014-07-10 10:46:07

tl;博士使用C++/CLI来调用这个极其复杂的API。

我手头没有Windows 8,所以不能执行任何操作。但是,我可以在代码中找到一些错误。我会把它们列在下面。我希望会有比我发现的更多的错误,但希望这会有所帮助。

DNS_QUERY_CANCEL

代码语言:javascript
复制
typedef struct _DNS_QUERY_CANCEL {
  CHAR Reserved[32];
} DNS_QUERY_CANCEL, *PDNS_QUERY_CANCEL;

这是一个包含长度为32的char数组的结构。你的翻译不正确。直译很棘手,因为您必须创建可以固定的类型。如果可以使用unsafe上下文,那么可以使用固定大小的缓冲区。

代码语言:javascript
复制
public unsafe struct DnsQueryCancel
{
    private fixed byte Reserved[32];
}

否则,您只需放入一些字段,就可以为结构提供合适的长度。这很好,因为您不需要对此结构进行读写。

代码语言:javascript
复制
public struct DnsQueryCancel
{
    private long Reserved1;
    private long Reserved2;
    private long Reserved3;
    private long Reserved4;
}

DNS_ADDR_ARRAY

代码语言:javascript
复制
typedef struct _DnsAddrArray {
  DWORD    MaxCount;
  DWORD    AddrCount;
  DWORD    Tag;
  WORD     Family;
  WORD     WordReserved;
  DWORD    Flags;
  DWORD    MatchFlag;
  DWORD    Reserved1;
  DWORD    Reserved2;
  DNS_ADDR AddrArray[ ];
} DNS_ADDR_ARRAY, *PDNS_ADDR_ARRAY;

你的翻译不正确。最后一个字段不是指针。它是一个内联数组。因此,结构是可变大小的。你不能以一种让它被固定的方式来声明它。您需要手动对非托管内存执行封送处理。如果您只需要使用AddrCount of 1支持结构,那么可以这样声明结构:

代码语言:javascript
复制
public struct DnsAddrArray
{
    public uint    MaxCount;
    public uint    AddrCount;
    public uint    Tag;
    public ushort  Family;
    public ushort  WordReserved;
    public uint    Flags;
    public uint    MatchFlag;
    public uint    Reserved1;
    public uint    Reserved2;
    public DnsAddr AddrArray;
}

注意,MaxCount是结构的字节大小。你设置的不对。

DNS_ADDR

代码语言:javascript
复制
typedef struct _Dns_Addr {
  CHAR  MaxSa[DNS_ADDR_MAX_SOCKADDR_LENGTH];
  DWORD DnsAddrUserDword[8];
} DNS_ADDR, *PDNS_ADDR;

再一次,你在C#上犯了这个严重的错误。假设使用固定方法,这将是:

代码语言:javascript
复制
public unsafe struct DnsAddr
{
    public fixed byte MaxSa[DNS_ADDR_MAX_SOCKADDR_LENGTH];
    public fixed uint DnsAddrUserDword[8];
}

但是,使用固定缓冲区非常不方便。所以我可能会这样做:

代码语言:javascript
复制
public struct DnsAddr
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = DNS_ADDR_MAX_SOCKADDR_LENGTH)]
    public byte[] MaxSa[];

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public uint[] DnsAddrUserDword;
}

然后,您需要使用Marshal.StructureToPtrMarshal.PtrToStructure进行编组。UnmanagedType.ByValArray的使用使钉扎变得不可能。

摘要

我很确定我只是在抓表面而已。这是一个尝试从C#调用的非常可怕的接口。我不会尝试这样做,而且我认为我对p/invoke相当精通。这里的智能移动是使用混合模式C++/CLI程序集提供的Windows头文件。我敦促你考虑这一选择。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24664721

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档