Delphi 如何从DNS获取主机名的IP地址?

Delphi 如何从DNS获取主机名的IP地址?,delphi,dns,ip,delphi-xe3,Delphi,Dns,Ip,Delphi Xe3,如果我在Delphi 2006中使用GetIP('server-name')或GetIP('google.com'),我有这个函数可以检索IP地址 但是现在我正在Delphi-XE3上试用它,它不起作用了。有什么想法吗 function GetIP(const HostName: string): string; var WSAData: TWSAData; R: PHostEnt; A: TInAddr; begin Result := IPNULL; // '0.0.0.0'

如果我在Delphi 2006中使用
GetIP('server-name')
GetIP('google.com')
,我有这个函数可以检索IP地址

但是现在我正在Delphi-XE3上试用它,它不起作用了。有什么想法吗

function GetIP(const HostName: string): string;
var
  WSAData: TWSAData;
  R: PHostEnt;
  A: TInAddr;
begin
  Result := IPNULL; // '0.0.0.0'
  WSAStartup($101, WSAData);
  R := Winsock.GetHostByName(PAnsiChar(HostName));
  if Assigned(R) then
  begin
    A := PInAddr(r^.h_Addr_List^)^;
    Result := string(WinSock.inet_ntoa(A));
  end;
end;

似乎没有分配
R
,因为结果总是
'0.0.0'

这两个版本的Delphi之间的最大区别在于现代Delphi本机使用宽UTF-16编码字符串,而旧版本使用ANSI编码字符串

许多API函数都有wide和ANSI版本。但是在Winsock中调用的函数始终是8位的

通过显式使用8位文本编码,可以使代码像以前一样工作

function GetIP(const HostName: string): string; 
var 
  WSAData: TWSAData;
  R: PHostEnt; 
  A: TInAddr; 
begin 
  Result := IPNULL; // '0.0.0.0' 
  WSAStartup($101, WSAData); 
  R := Winsock.GetHostByName(PAnsiChar(AnsiString(HostName))); 
  if Assigned(R) then 
  begin 
    A := PInAddr(r^.h_Addr_List^)^; 
    Result := WinSock.inet_ntoa(A); 
  end; 
end;

现在,细心的读者会说:

如果主机名包含非ASCII字符怎么办?被这些相当微弱的固定长度8位编码所限制,难道不是一种耻辱吗


好的,现在推荐的将主机名转换为地址的函数是Unicode函数。

这是我使用winsocks2和Unicode支持在XE3-XE7上解析IP地址的一个自包含示例

由于套接字支持在每次调用时都被初始化和取消初始化,所以它的性能不高,但重构是微不足道的

另外,请注意,它并不是为了方便维护而特意优化的

uses
  Winapi.Winsock2;

type
  PAddrInfo = ^TAddrInfo;
  TAddrInfo = packed record
    ai_flags: integer;
    ai_family: integer;
    ai_socktype: integer;
    ai_protocol: integer;
    ai_addrlen: NativeInt;
    ai_canonname: PCHAR;
    ai_addr: PSOCKADDR;
    ai_next: PAddrInfo;
  end;

function GetAddrInfo(const nodeName: PCHAR; const serviceName : PChar; const hints: PAddrInfo; var result: PAddrInfo): integer; stdcall; external 'ws2_32.dll' name 'GetAddrInfoW';
procedure FreeAddrInfo(const addrInfo: PAddrInfo); stdcall; external 'ws2_32.dll' name 'FreeAddrInfoW';

function ResolveIpAddress(const hostName: string; const ipv6: boolean): string;
const
  BUFFER_SIZE = 32768;
var
  data: TWSAData;
  error: integer;
  requestError: integer;
  r: PAddrInfo;
  s: string;
  hints: TAddrInfo;
  buffer: TArray<byte>;
  length: DWORD;
begin
  error:= WSAStartup(MAKEWORD(2, 2), data);
  try
    if (error = 0)  then
    begin
      r:= nil;
      try
        ZeroMemory(@hints, sizeof(TAddrInfo));
        if (ipv6) then
          hints.ai_family:= AF_INET6
        else
          hints.ai_family:= AF_INET;

        requestError:= GetAddrInfo(PCHAR(hostName), nil, @hints, r);
        if (requestError = 0) then
        begin
          length:= BUFFER_SIZE;
          SetLength(buffer, BUFFER_SIZE);
          if (WSAAddressToString(r.ai_addr^, r.ai_addrlen, nil, @buffer[0], length) = 0) then
          begin
            setLength(buffer, length * 2);
            s:= TUnicodeEncoding.Unicode.GetString(@buffer[0]);
            exit(s);
          end
          else
            exit('0.0.0.0');
        end
        else
          exit('0.0.0.0');

      finally
        FreeAddrInfo(r);
      end;
    end
  finally
    if (error = 0) then
      WSACleanup();
  end;
end;
使用
Winapi.Winsock2;
类型
PAddrInfo=^TAddrInfo;
TAddrInfo=压缩记录
ai_标志:整数;
艾尤族:整数;
ai_类型:整数;
ai_协议:整数;
艾杜·艾德伦:民族主义;
姓名:PCHAR ;;
地址:PSOCKADDR;
下一步:帕德林福;
结束;
函数GetAddrInfo(const nodeName:PCHAR;const serviceName:PCHAR;const提示:PAddrInfo;var结果:PAddrInfo):整数;stdcall;外部“ws2_32.dll”名称“GetAddrInfoW”;
程序FreeAddrInfo(const addrInfo:PAddrInfo);stdcall;外部“ws2_32.dll”名称“FreeAddrInfoW”;
函数ResolveIpAddress(常量主机名:字符串;常量ipv6:布尔值):字符串;
常数
缓冲区大小=32768;
变量
数据:TWSAData;
错误:整数;
请求错误:整数;
r:帕德林福;
s:字符串;
提示:TAddrInfo;
缓冲液:焦油;
长度:德沃德;
开始
错误:=WSAStartup(MAKEWORD(2,2),数据);
尝试
如果(错误=0),则
开始
r:=零;
尝试
零内存(@hits,sizeof(TAddrInfo));
如果是ipv6,那么
提示.ai_族:=AF_INET6
其他的
hits.ai_family:=AF_INET;
requestError:=GetAddrInfo(PCHAR(主机名),nil,@hints,r);
如果(requestError=0),则
开始
长度:=缓冲区大小;
设置长度(缓冲区、缓冲区大小);
如果(WSAAddressToString(r.ai_addr^,r.ai_addrlen,nil,@buffer[0],length)=0),则
开始
设置长度(缓冲区,长度*2);
s:=TUnicodeEncoding.Unicode.GetString(@buffer[0]);
出口;;
结束
其他的
退出(“0.0.0.0”);
结束
其他的
退出(“0.0.0.0”);
最后
FreeAddrInfo(r);
结束;
结束
最后
如果(错误=0),则
WSACleanup();
结束;
结束;

HostName
参数类型更改为
AnsiString
。这很重要,因为
PAnsiChar
是指向
AnsiChar
的指针,该指针的大小为单个字节,而
HostName
参数的类型为
string
,在Delphi中每个字符的大小为2个字节。您指向的是不同大小的字符串。顺便说一句,一个编译器告诉你关于W1044可疑类型转换字符串到PAnsiChar警告的事情。或者,你可以让
主机名保持原样,但是在你通过
PAnsiChar
类型转换将其传递给
GetHostByName
函数之前,你可以将其类型转换为
AnsiString
,类似于下面的
GetHostByName(PAnsiChar(AnsiString(HostName)))
@TLama您应该回答这个问题。不要关闭警告。:-)警告可能非常重要(如您在此处所见)。我总是用提示和警告来编译。出于某种原因,@TLama没有将此作为答案。问题需要答案。没有答案,提问者就没有什么可接受的。这就是我的答案。