unit MyIp;interfaceuses
  SysUtils, Classes, Controls, Math, Dialogs;type
  TQQWry = class(TObject)
    public
      constructor Create(cQQWryFileName: string);
      destructor Destroy; override;
      function GetQQWryFileName: string;
      function GetQQWryFileSize: Cardinal;
      function GetIPRecordNum: Cardinal;
      function GetQQWryDate: TDate;
      function GetQQWryDataFrom: string;
      function GetIPLocation(IPLocationOffset: Cardinal): TStringlist;
      function GetIPMsg(IPRecordID: Cardinal): TStringlist;
      function GetIPRecordID(IP: string): Cardinal;
      function GetIPValue(IP: string): Cardinal;
    private
      QQWryFileName: string;
      QQWryFileStream: TFileStream;
      QQWryFileSize: Cardinal;
      IPRecordNum: Cardinal;
      FirstIPIndexOffset, LastIPIndexOffset: Cardinal;
  end;implementation///**
//* 构造一个TQQWry即QQIP地址数据库的对象
//* @param cQQWryFileName QQIP数据库文件的全名(包括路径),请确认文件存在和可读性
//* @return 无
//*/
constructor TQQWry.Create(cQQWryFileName: string);
begin
  inherited Create;
  QQWryFileName:=cQQWryFileName;
  QQWryFileStream:=TFileStream.Create(QQWryFileName, fmOpenRead or fmShareDenyWrite, 0);
  QQWryFileSize:=QQWryFileStream.Size;
  QQWryFileStream.Read(FirstIPIndexOffset, 4);
  QQWryFileStream.Read(LastIPIndexOffset, 4);
  IPRecordNum:=(LastIPIndexOffset - FirstIPIndexOffset) div 7 + 1;
end;///**
//* 析构函数,释放TQQWry对象,释放文件数据流对象
//* @param  无
//* @return 无
//*/
destructor TQQWry.Destroy;
begin
  QQWryFileStream.Free;
  inherited Destroy;
end;///**
//* 获取QQIP数据库文件的全名(包括路径)
//* @param  无
//* @return QQIP数据库文件的全名(包括路径)  string
//*/
function TQQWry.GetQQWryFileName: string;
begin
  Result:=QQWryFileName;
end;///**
//* 获取QQIP数据库文件大小
//* @param  无
//* @return QQIP数据库文件大小  Cardinal
//*/
function TQQWry.GetQQWryFileSize: Cardinal;
begin
  Result:=QQWryFileSize;
end;///**
//* 获取QQIP数据库内含有的IP地址信息记录条数
//* @param  无
//* @return QQIP数据库文件大小  Cardinal
//*/
function TQQWry.GetIPRecordNum: Cardinal;
begin
  Result:=IPRecordNum;
end;///**
//* 获取当前QQIP数据库的更新日期
//* @param  无
//* @return QQIP当前数据库的更新日期  TDate
//*/
function TQQWry.GetQQWryDate: TDate;
var
  DateString: string;
begin
  DateString:=GetIPMsg(GetIPRecordNum)[3];
  DateString:=copy(DateString, 1, pos('IP数据', DateString) - 1);
  DateString:=StringReplace(DateString, '年', '-', [rfReplaceAll, rfIgnoreCase]);
  DateString:=StringReplace(DateString, '月', '-', [rfReplaceAll, rfIgnoreCase]);
  DateString:=StringReplace(DateString, '日', '-', [rfReplaceAll, rfIgnoreCase]);
  Result:=StrToDate(DateString);
end;///**
//* 获取当前QQIP数据库的来源信息
//* @param  无
//* @return 当前QQIP数据库的来源信息  string
//*/
function TQQWry.GetQQWryDataFrom: string;
begin
  Result:=GetIPMsg(GetIPRecordNum)[2];
end;///**
//* 给定一个IP国家地区记录的偏移,返回该IP地址的信息
//* @param  IPLocationOffset  国家记录的偏移  Cardinal
//* @return IP地址信息(国家信息/地区信息)  string
//*/
function TQQWry.GetIPLocation(IPLocationOffset: Cardinal): TStringlist;
const
  //实际信息字串存放位置的重定向模式
  REDIRECT_MODE_1 = 1;
  REDIRECT_MODE_2 = 2;
var
  RedirectMode: byte;
  CountryFirstOffset, CountrySecondOffset: Cardinal;
  CountryMsg, AreaMsg: string;
  ///**
  //* 给定一个文件偏移值,返回在数据文件中该偏移下的字符串,即读取到0结尾的字符前
  //* @param  StringOffset  字符串在文件中的偏移值  Cardinal
  //* @return 字符串  string
  //*/
  function ReadString(StringOffset: Cardinal): string;
  var
    ReadByte: char;
  begin
    Result:='';
    QQWryFileStream.Seek(StringOffset, soFromBeginning);
    QQWryFileStream.Read(ReadByte, 1);
    while ord(ReadByte)<>0 do begin
      Result := Result + ReadByte;
      QQWryFileStream.Read(ReadByte, 1);
    end;
  end;
  ///**
  //* 给定一个地区信息偏移值,返回在数据文件中该偏移量下的地区信息
  //* @param  AreaOffset 地区信息在文件中的偏移值  Cardinal;
  //* @return 地区信息字符串  string
  //*/
  function ReadArea(AreaOffset: Cardinal): string;
  var
    ModeByte: byte;
    ReadAreaOffset: Cardinal;
  begin
    QQWryFileStream.Seek(AreaOffset, soFromBeginning);
    QQWryFileStream.Read(ModeByte, 1);
    if (ModeByte = REDIRECT_MODE_1) or (ModeByte = REDIRECT_MODE_2) then begin
      QQWryFileStream.Read(ReadAreaOffset, 3);
      if ReadAreaOffset=0 then Result:='未知地区'
      else Result:=ReadString(ReadAreaOffset);
    end else begin
      Result:=ReadString(AreaOffset);
    end;
  end;
begin
  //跳过4个字节,该4字节内容为该条IP信息里IP地址段中的终止IP值
  QQWryFileStream.Seek(IPLocationOffset + 4, soFromBeginning);
  //读取国家信息的重定向模式值
  QQWryFileStream.Read(RedirectMode, 1);  //重定向模式1的处理
  if RedirectMode = REDIRECT_MODE_1 then begin
    //模式值为1,则后3个字节的内容为国家信息的重定向偏移值
    QQWryFileStream.Read(CountryFirstOffset, 3);
    //进行重定向
    QQWryFileStream.Seek(CountryFirstOffset, soFromBeginning);
    //第二次读取国家信息的重定向模式
    QQWryFileStream.Read(RedirectMode, 1);
    //第二次重定向模式为模式2的处理
    if RedirectMode = REDIRECT_MODE_2 then begin
      //后3字节的内容即为第二次重定向偏移值
      QQWryFileStream.Read(CountrySecondOffset, 3);
      //读取第二次重定向偏移值下的字符串值,即为国家信息
      CountryMsg:=ReadString(CountrySecondOffset);
      //若第一次重定向模式为1,进行重定向后读取的第二次重定向模式为2,
      //则地区信息存放在第一次国家信息偏移值的后面
      QQWryFileStream.Seek(CountryFirstOffset + 4, soFromBeginning);
    //第二次重定向模式不是模式2的处理
    end else begin
      CountryMsg:=ReadString(CountryFirstOffset);
    end;
    //在重定向模式1下读地区信息值
    AreaMsg:=ReadArea(QQWryFileStream.Position);
  //重定向模式2的处理
  end else if RedirectMode = REDIRECT_MODE_2 then begin
    QQWryFileStream.Read(CountrySecondOffset, 3);
    CountryMsg:=ReadString(CountrySecondOffset);
    AreaMsg:=ReadArea(IPLocationOffset + 8);
  //不是重定向模式的处理,存放的即是IP地址信息
  end else begin
    CountryMsg:=ReadString(QQWryFileStream.Position - 1);
    AreaMsg:=ReadArea(QQWryFileStream.Position);
  end;
  Result:=TStringlist.Create;
  Result.Add(CountryMsg);
  Result.Add(AreaMsg);
end;///**
//* 给定一个IP地址信息记录号,返回该项记录的信息
//* @param  IPRecordID  IP地址信息记录号  Cardinal
//* @return 记录号信息(含3个部分:①起始IP地址  ②终止IP地址  ③国家信息/地区信息)  TStringlist
//*/
function TQQWry.GetIPMsg(IPRecordID: Cardinal): TStringlist;
var
  aryStartIP: array[1..4] of byte;
  strStartIP: string;  EndIPOffset: Cardinal;
  aryEndIP: array[1..4] of byte;
  strEndIP: string;  i: integer;
begin
  //根据记录ID号移到该记录号的索引处
  QQWryFileStream.Seek(FirstIPIndexOffset + (IPRecordID - 1) * 7, soFromBeginning);
  //索引的前4个字节为起始IP地址
  QQWryFileStream.Read(aryStartIP, 4);
  //后3个字节是内容区域的偏移值
  QQWryFileStream.Read(EndIPOffset, 3);  //移至内容区域
  QQWryFileStream.Seek(EndIPOffset, soFromBeginning);
  //内容区域的前4个字节为终止IP地址
  QQWryFileStream.Read(aryEndIP, 4);  //将起止IP地址转换为点分的形式
  strStartIP:='';
  for i:=4 downto 1 do begin
    if i<>1 then strStartIP:=strStartIP + IntToStr(aryStartIP[i]) + '.'
    else strStartIP:=strStartIP + IntToStr(aryStartIP[i]);
  end;  strEndIP:='';
  for i:=4 downto 1 do begin
    if i<>1 then strEndIP:=strEndIP + IntToStr(aryEndIP[i]) + '.'
    else strEndIP:=strEndIP + IntToStr(aryEndIP[i]);
  end;  Result:=TStringlist.Create;
  Result.Add(strStartIP);
  Result.Add(strEndIP);
  //获取该条记录下的IP地址信息
  //以下三者是统一的:①内容区域的偏移值  ②终止IP地址的存放位置  ③国家信息紧接在终止IP地址存放位置后
  Result.AddStrings(GetIPLocation(EndIPOffset));
end;///**
//* 给定一个IP地址(四段点分字符串形式),返回该IP的数值
//* @param  IP  IP地址(四段点分字符串形式)  string
//* @return 该IP的数值  Cardinal
//*/
function TQQWry.GetIPValue(IP: string): Cardinal;
var
  tsIP: TStringlist;
  i: integer;
  function SplitStringToStringlist(aString: string; aSplitChar: string): TStringlist;
  begin
    Result:=TStringList.Create;
    while pos(aSplitChar, aString)>0 do begin
      Result.Add(copy(aString, 1, pos(aSplitChar, aString)-1));
      aString:=copy(aString, pos(aSplitChar, aString)+1, length(aString)-pos(aSplitChar, aString));
    end;
    Result.Add(aString);
  end;
begin
  tsIP:=SplitStringToStringlist(IP, '.');
  Result:=0;
  for i:=3 downto 0 do begin
    Result:=Result + StrToInt(tsIP[i]) * trunc(power(256, 3-i));
  end;
end;///**
//* 给定一个IP地址(四段点分字符串形式),返回该IP地址所在的记录号
//* @param  IP  IP地址(四段点分字符串形式)  string
//* @return 该IP地址所在的记录号  Cardinal
//*/
function TQQWry.GetIPRecordID(IP: string): Cardinal;
  function SearchIPRecordID(IPRecordFrom, IPRecordTo, IPValue: Cardinal): Cardinal;
  var
    CompareIPValue1, CompareIPValue2: Cardinal;
  begin
    Result:=0;
    QQWryFileStream.Seek(FirstIPIndexOffset + ((IPRecordTo - IPRecordFrom) div 2 + IPRecordFrom - 1) * 7, soFromBeginning);
    QQWryFileStream.Read(CompareIPValue1, 4);
    QQWryFileStream.Seek(FirstIPIndexOffset + ((IPRecordTo - IPRecordFrom) div 2 + IPRecordFrom) * 7, soFromBeginning);
    QQWryFileStream.Read(CompareIPValue2, 4);
    //找到了
    if (IPValue>=CompareIPValue1) and (IPValue<CompareIPValue2) then begin
      Result:=(IPRecordTo - IPRecordFrom) div 2 + IPRecordFrom;
    end else
      //后半段找
      if IPValue>CompareIPValue1 then begin
        Result:=SearchIPRecordID((IPRecordTo - IPRecordFrom) div 2 + IPRecordFrom + 1, IPRecordTo, IPValue);
      end else
        //前半段找
        if IPValue<CompareIPValue1 then begin
          Result:=SearchIPRecordID(IPRecordFrom, (IPRecordTo - IPRecordFrom) div 2 + IPRecordFrom - 1, IPValue);
        end;
  end;
begin
  Result:=SearchIPRecordID(1, GetIPRecordNum, GetIPValue(IP));
end;end.QQWryStr:=PChar(ExtractFilePath(Application.Exename))+'QQWry.Dat';
QQWry:=TQQWry.Create(QQWryStr);
item.subitems.add(QQWry.GetIPMsg(QQWry.GetIPRecordID(Port.Strings[0]))[2]);    //地理位置
QQWry.Free;