我刚开始做编组工作,我很乐意听取你的任何建议。
在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之前,这意味着-错误的参数。
另外,我无法了解如何将字符串作为结构的一部分封送到非托管代码?
这是我的代码:
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)
{
}
//==================================================更新:我更改了代码如下:
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++.
发布于 2014-07-10 10:46:07
tl;博士使用C++/CLI来调用这个极其复杂的API。
我手头没有Windows 8,所以不能执行任何操作。但是,我可以在代码中找到一些错误。我会把它们列在下面。我希望会有比我发现的更多的错误,但希望这会有所帮助。
DNS_QUERY_CANCEL
typedef struct _DNS_QUERY_CANCEL {
CHAR Reserved[32];
} DNS_QUERY_CANCEL, *PDNS_QUERY_CANCEL;这是一个包含长度为32的char数组的结构。你的翻译不正确。直译很棘手,因为您必须创建可以固定的类型。如果可以使用unsafe上下文,那么可以使用固定大小的缓冲区。
public unsafe struct DnsQueryCancel
{
private fixed byte Reserved[32];
}否则,您只需放入一些字段,就可以为结构提供合适的长度。这很好,因为您不需要对此结构进行读写。
public struct DnsQueryCancel
{
private long Reserved1;
private long Reserved2;
private long Reserved3;
private long Reserved4;
}DNS_ADDR_ARRAY
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支持结构,那么可以这样声明结构:
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
typedef struct _Dns_Addr {
CHAR MaxSa[DNS_ADDR_MAX_SOCKADDR_LENGTH];
DWORD DnsAddrUserDword[8];
} DNS_ADDR, *PDNS_ADDR;再一次,你在C#上犯了这个严重的错误。假设使用固定方法,这将是:
public unsafe struct DnsAddr
{
public fixed byte MaxSa[DNS_ADDR_MAX_SOCKADDR_LENGTH];
public fixed uint DnsAddrUserDword[8];
}但是,使用固定缓冲区非常不方便。所以我可能会这样做:
public struct DnsAddr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = DNS_ADDR_MAX_SOCKADDR_LENGTH)]
public byte[] MaxSa[];
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public uint[] DnsAddrUserDword;
}然后,您需要使用Marshal.StructureToPtr和Marshal.PtrToStructure进行编组。UnmanagedType.ByValArray的使用使钉扎变得不可能。
摘要
我很确定我只是在抓表面而已。这是一个尝试从C#调用的非常可怕的接口。我不会尝试这样做,而且我认为我对p/invoke相当精通。这里的智能移动是使用混合模式C++/CLI程序集提供的Windows头文件。我敦促你考虑这一选择。
https://stackoverflow.com/questions/24664721
复制相似问题