вторник, 15 января 2013 г.

Изменение параметров сетевых интерфейсов через WinApi

Всё началось с простого вопроса: как через WinApi в Delphi работать с сетевыми адаптерами, например включить или отключить сетевой интерфейс, узнать или изменить IP-адрес, проверить наличие Wi-Fi и т.д.? Быстрый гуглинг вывел на данный раздел MSDN: IP Helper
Пока разбирался, написал несколько простейший примеров использования основных функций, которые в данной статье и представлены.

Внимание! В Delphi XE3 для работы с представленными далее сетевым API необходимо подключить библиотеки Winapi.IpHlpApi, Winapi.Winsock2, Winapi.IpTypes и Winapi.IpRtrMib , в которых содержатся необходимые для работы типы и функции.

 Функция  GetAdaptersInfo

Извлекает информацию о сетевых адаптерах в системе. Следует помнить, что эта функция работает только с IPv4, поэтому не рекомендуется её использовать в Windows XP и старше.
Т.к. почитать описание вы можете и по ссылке, перейду к примеру на Delphi XE3.
Данная программа выведет список всех сетевых адаптеров:

program ipconfig;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Variants,
  System.Classes,
  Winapi.Windows,
  Winapi.Messages,
  Winapi.IpHlpApi,
  Winapi.IpRtrMib
  Winapi.IpTypes,
  Web.Win.Sockets;

const

// Типы адаптеров

MIB_IF_TYPE_OTHER     = 1;
MIB_IF_TYPE_ETHERNET  = 6;
MIB_IF_TYPE_TOKENRING = 9;
MIB_IF_TYPE_FDDI      = 15;
MIB_IF_TYPE_PPP       = 23;
MIB_IF_TYPE_LOOPBACK  = 24;
MIB_IF_TYPE_SLIP      = 28;
MIB_IF_TYPE_WIRELESS  = 71;

var

IFs, pAdapt: PIP_ADAPTER_INFO;
Err, AdapterInfoSize: Cardinal;

begin

  try
    { TODO -oUser -cConsole Main : Insert code here }
//перезапускаем интерфейс:

AdapterInfoSize:=0;

Err:=GetAdaptersInfo(nil, AdapterInfoSize);

if (Err<>0) and (Err<>ERROR_BUFFER_OVERFLOW) then

begin

writeln('Error');

exit;

end;

//Получить информацию об устройствах.

Ifs := PIP_ADAPTER_INFO(GlobalAlloc(GPTR, AdapterInfoSize));

GetAdaptersInfo(IFs, AdapterInfoSize);

pAdapt := IFs;

while pAdapt<>nil do

begin

case pAdapt.Type_ of

MIB_IF_TYPE_ETHERNET: writeln('Ethernet adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_TOKENRING:  writeln('Token Ring adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_FDDI: writeln('FDDI adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_PPP: writeln('PPP adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_LOOPBACK: writeln('Loopback adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_SLIP: writeln('Slip adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_OTHER: writeln('Other adapter '+pAdapt.AdapterName);
MIB_IF_TYPE_WIRELESS: writeln('Wireless adapter '+pAdapt.AdapterName)
 
else writeln('Other adapter '+pAdapt.AdapterName);

end;

pAdapt := pAdapt.Next;

end;

GlobalFree(Cardinal(IFs));

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

readln;
end.



Функция GetAdaptersAddresses

Данная функция возвращает  подробную информацию о сетевых адаптерах: адреса, имена, тип и т.д. и т.п., структура выходной записи имеет более 30 полей. Особенно приятно что функция в отличии от предыдущей возвращает и friendlyname - "человеческое" имя сетевого адаптера, которое мы можем видеть в списке сетевых подключений в виндовс. Итак, пример, которые возвращает список всех человеческих имен всех адаптеров:
 
procedure GetAdaptersAddressesTest;
const
WORKING_BUFFER_SIZE = 15000;
MAX_ITERATION = 3;
var
dwSize: Cardinal;
dwRetVal: Cardinal;
pAddresses, pCurrAdress: PIP_ADAPTER_ADDRESSES;
outbuffr: integer;
_p: Pointer;
iteration: Integer;
begin
dwSize:=0;
dwRetVal:=0;
iteration:=0;
outbuffr:= WORKING_BUFFER_SIZE;
_p:= @outbuffr;

repeat
pAddresses:=PIP_ADAPTER_ADDRESSES(GlobalAlloc(GPTR,WORKING_BUFFER_SIZE));
dwRetVal:=GetAdaptersAddresses(AF_UNSPEC,0,nil,pAddresses,_p);
if dwRetVal=ERROR_BUFFER_OVERFLOW then
  begin
    GlobalFree(cardinal(pAddresses));
    pAddresses:=NIL;
  end
  else break;
  Inc(iteration);
until (dwRetVal=ERROR_BUFFER_OVERFLOW) and (iteration<=MAX_ITERATION);

if dwRetVal=NO_ERROR then
begin
  pCurrAdress:=pAddresses;
  while pCurrAdress<>nil do
  begin
  Writeln(pCurrAdress.FriendlyName);
  pCurrAdress:=pCurrAdress.Next;
  end;
  GlobalFree(cardinal(pCurrAdress));
end;
GlobalFree(cardinal(pAddresses));
end;


Функции GetIfEntry и GetIfTable

GetIfTable служит для получения списка сетевых интерфейсов (не путать с адаптерами!). А GetIfEntry для подробной информации о конкретном интерфейсе;
Пример на процедуры на Delphi XE3, которая получает список всех интерфейсов и выводит имя каждого:

procedure  GetIfEntryTest;
var
// Объявляем и инициализируем переменные
pMibIfRow: PMIB_IFROW;
ifTable: PMIB_IFTABLE;
dwSize: Cardinal;
dwRetVal: Cardinal;
str: array [0..255] of char;
i,j: integer;
begin
dwSize:=0;
dwRetVal:=0;

// Выделяем память для указателей

pMibIfRow:= PMIB_IFROW(GlobalAlloc(GPTR, SizeOf(MIB_IFROW)));
ifTable:= PMIB_IFTABLE(GlobalAlloc(GPTR, SizeOf(MIB_IFTABLE)));
dwSize:=SizeOf(MIB_IFTABLE);


// Перед вызовом GetIfEntry мы вызовем GetIfTable чтобы убедиться
// что тут есть что получать
// Сделаем инициализационный вызов GetIfTable, чтобы получить
// необходимый размер в dwSize
if  GetIfTable(nil, dwSize, False)= ERROR_INSUFFICIENT_BUFFER then
begin
GlobalFree(Cardinal(iftable));
ifTable:= PMIB_IFTABLE(GlobalAlloc(GPTR, dwSize));
end;

// Делаем второй вызов GetIfTable для получения актуальной информации
// которую мы хотим.

dwRetVal:=GetIfTable(ifTable, dwSize, False);
if dwRetVal=NO_ERROR then
begin
  if ifTable.dwNumEntries>0 then
  begin
    for j := 1 to ifTable.dwNumEntries do
    begin
    pMibIfRow.dwIndex:=j;
    dwRetVal:=GetIfEntry(pMibIfRow);
    if dwRetVal = NO_ERROR then
    begin
      write(pMibIfRow.dwIndex);
      for i:=0 to 255 do
      str[i]:=chr(pMibIfRow.bDescr[i]);
      Writeln(str);
    end
    else begin
      writeln('Ошибка GetIfEntry failed');
      //Тут вы можете использовать FormatMessage чтобы
      //понять почему вылезла ошибка
    end;
  end;
  end
  else begin
  writeln('Ошибка GetIfTable');
  end;
end;
GlobalFree(Cardinal(pMibIfRow));
GlobalFree(Cardinal(ifTable));
end;


Функция SetIfEntry


Дання функция задаёт параметры сетевого интерфейса.В качестве входного параметра служит переменная типа _MIB_IFROW. Этот тип описывает структуру сетевого интерфейса, аналогичную тому, указатель на которую использует функция GetIfEntry. Т.е PMIB_IFROW = ^_MIB_IFROW.

1 комментарий:

  1. Пригодится. Гуглю инфу, чтобы написать свою версию Tele2-оболочки 4G-модема. А то у них там индикатор мелкий и показывает только суммарный трафик за за месяц (а нужно за сутки, за определённый интервал и т.д.)

    ОтветитьУдалить