На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
  
    > Работа с сетевыми адаптерами
      Часто возникают вопросы такого типа:
      - А как получить IP-адрес?
      - А как установить IP-адрес?
      - А как узнать, какие сетевые интерфейсы есть в системе?

      Многие по ошибке лезут в реестр и правят там все руками. Однако сие - не есть правильно. Предлагаю вариант правильный. Он отвечает на вопросы №1 и №3. По вопросу №2 будут комментарии после.

      Итак, предлагаемый пример для изучения в аттаче. Рассмотрим его.

      Всю необходимую информацию, а также возможность ее изменить, нам предоставляет библиотека IpHlpApi.dll. Ей и воспользуемся (отметим то, что диалог изменения параметров интерфейсов в WINDOWS пользуется иными средствами - недокументированными, но нам это на данный момент знать не надо. Исследователи и любопытные могут сами посмотреть, что там происходит и рассказать нам об этом. Будем пользоваться средствами стандартными).

      Итак, подключим необходимые хидеры:
      ExpandedWrap disabled
        #include <iphlpapi.h>

      Необходимо и в свойствах линкера добавить библиотеку iphlpapi.lib.

      Функция, которая возвращает основную информацию по адаптерам: GetIfTable. Она нам покажет все имеющиеся в системе адаптеры и полезную информацию по ним. (см. ниже).
      Она нам вернет имена адаптеров, сколько байтов и куда ушло, тип, статус и скорость интерфейсов.
      Итак, получаем необходимую информацию:

      1. Сперва спросим ее, сколько памяти нам нужно под необходимые данные:
      ExpandedWrap disabled
            MIB_IFROW mbrow = { 0 };
            PMIB_IFTABLE pTable = NULL;
            DWORD dwSize = 0;
            if ( GetIfTable( pTable, &dwSize, FALSE ) == ERROR_INSUFFICIENT_BUFFER )
            { // ...

      2. Получаем в dwSize необходимый для буфера размер и теперь выделяем память под него и выдергиваем инфу:
      ExpandedWrap disabled
                pTable = PMIB_IFTABLE(LocalAlloc(LMEM_ZEROINIT, dwSize));
                if ( !GetIfTable( pTable, &dwSize, TRUE ) )
                {
                    printf("Total entries: %d\nEnumerating...\n", pTable->dwNumEntries);

      3. Получили? Начинаем перечислять (вся информация хранится в полученном буфере в виде массива структур):
      ExpandedWrap disabled
                    for(unsigned int i = 0; i < pTable->dwNumEntries; ++i)
                    {
                        PMIB_IFROW ifRow = &pTable->table[i];

      4. Итак. Что у нас там интересного? Ну, во-первых, описание интерфейса. Его и отображаем:
      ExpandedWrap disabled
                            char str[MAXLEN_IFDESCR + 1] = { 0 };
                            CharToOemA((char*) ifRow->bDescr, (char*) ifRow->bDescr);
                            printf("- if%d is:\n\t%s\n", i, ifRow->bDescr);

      5. Описание хранится в ANSI формате, поэтому не будем заморачиваться на тему проекта в UNICODE. Затем, нам интересен типа интерфейса. Что это? ЛВС? VPN? Или еще что? А также в каком статусе пребывает? Подключен? Подключается? Или вовсе не работает?
      ExpandedWrap disabled
                            printf("\tType: %s\n", get_iface_type(ifRow->dwType));
                            printf("\tStatus: %s\n", get_iface_status(ifRow->dwOperStatus));

      (ф-ции get_iface_type & get_iface_status возвращают строчки-описания типа и статуса. Их определения см. в исходниках).
      6. Одно из самых интересных возвращаемых полей - MAC-адрес интерфейса. В структуре есть поле, определяющее длину физического адреса. Я (лично я) еще не встречал размер адреса, отличный от 6 байт. Поэтому данное поле проверять не будем. Итак:
      ExpandedWrap disabled
                            printf("\tAddress: %02x:%02x:%02x:%02x:%02x:%02x\n",
                                    ifRow->bPhysAddr[0],
                                    ifRow->bPhysAddr[1],
                                    ifRow->bPhysAddr[2],
                                    ifRow->bPhysAddr[3],
                                    ifRow->bPhysAddr[4],
                                    ifRow->bPhysAddr[5]
                                    );

      ...MAC-адрес адаптера как на ладони! Бери и пользуйся. :)
      7. Многих еще интересует, а сколько данных ушло в сеть, а сколько оттуда пришло. Данные измеряются: а) в пакетах, которые прошли через адаптер; б) в октетах (байтах). Кроме того, пакеты подразделяются на некастовые и юникастовые (любопытные могут обратиться к документации). Отобразим все данные: кол-во байт, некастовые пакеты, юникастовые и общее количество пакетов:
      ExpandedWrap disabled
                            printf("\tBytes out transfered: %d b (In %d (non-cast) + %d (unicast) = %d packets)\n", ifRow->dwOutOctets, ifRow->dwOutNUcastPkts, ifRow->dwOutUcastPkts, ifRow->dwOutNUcastPkts + ifRow->dwOutUcastPkts);
                            printf("\tBytes in transfered: %d b (In %d (non-cast) + %d (unicast) = %d packets)\n", ifRow->dwInOctets, ifRow->dwInNUcastPkts, ifRow->dwInUcastPkts, ifRow->dwInNUcastPkts + ifRow->dwInUcastPkts);

      8. Также многие интересуются скоростью, которую может выдать адаптер. ВНИМАНИЕ: Не путать эту скорость со скоростью канала! Означенная скорость показывает, с какой максимальной скоростью адаптер может пропускать через себя данные! Так что, если вы увидите там 100 МБит/сек, а любимый порнофильм качается со скоростью 5 кБайт/сек, то не надо валить на некорректность данных - тут все правильно и вас никто не обманывает.
      ExpandedWrap disabled
                            printf("\tInterface speed: %d Bits per seconds\n", ifRow->dwSpeed);

      9. А теперь самое интересное! IP-адрес, назначенный данному интерфейсу. Проблема в том, что ф-ция GetIfTable не предоставляет нам такую информацию. Есть другая ф-ция: GetIpAddrTable. Затычка в том, что она работает также, как и GetIfTable. Т.е. возвращает массив структур с данными, в котором нужная информация и хранится. Но там есть поле dwIndex, которое по смыслу совпадает с тем, что нам возвращает в массиве GetIfTable. Чтобы не вызывать при каждой итерации ф-цию GetIpAddrEntry, которая возвращает всего одно поле по заданному индексу, создадим буфер и всю информацию туда затолкаем. Затем при каждой итерации будем искать нужное нам поле и по нему получать информацию по адресам. Этим занимаются 2 функции: get_ifaces_table - получает всю таблицу для всех адаптеров и get_iface_ipaddrow - получает нужную строчку по заданному индексу адаптера. Их исходные тексты см. в исходном тексте.
      Сперва, перед перечислением адаптеров, получим таблицу с адресами для адаптеров. Для этого изменим немного п.3:
      ExpandedWrap disabled
                    MIB_IPADDRTABLE * pAddresses = get_ifaces_table();
                    if ( !pAddresses ) printf("WARNING: Unable to obtain ip addresses table!\n\n");
                    printf("Total entries: %d\nEnumerating...\n", pTable->dwNumEntries);
                    for(unsigned int i = 0; i < pTable->dwNumEntries; ++i)
                    {

      Теперь у нас есть сразу и таблица с адресами, к которой будем обращаться в цикле.
      Вернемся к п.9. В нем будем извлекать с помощью get_iface_ipaddrow поле, соответствующее текущему перечисляемому адаптеру:
      ExpandedWrap disabled
                            if ( pAddresses )
                            {
                                MIB_IPADDRROW * pAddr = get_iface_ipaddrow(ifRow->dwIndex, pAddresses);
                                if ( pAddr )
                                {
                                    BYTE * addr = (BYTE*) &pAddr->dwAddr;
                                    char str[255];
                                    get_ipaddr_type( str, pAddr->wType );
                                    printf("\tIP address: %d.%d.%d.%d ( %s)\n",
                                            addr[0],
                                            addr[1],
                                            addr[2],
                                            addr[3],
                                            str);
                                    addr = (BYTE*) &pAddr->dwMask;
                                    printf("\tNetwork mask: %d.%d.%d.%d\n",
                                            addr[0],
                                            addr[1],
                                            addr[2],
                                            addr[3]);
                                }
                            }

      Здесь мы увидим сам IP-адрес, маску подсети и тип адреса: динамический (напр.: присвоен DHCP), отключенный или предназначенный для удаления (см. полное перечисление типов в MSDN). Тип адреса нам отобразит ф-ция get_ipaddr_type, которая также может быть найдена в исходниках.



      На сим наш экскурс в мир IpHlpApi закончился. Мы получили много всяких интересных данных, но...!! А как же адреса DNS? А как же то, что у одного адаптера может быть несколько IP-адресов, в конце концов и шлюз должен быть??!!! Впрочем, хочется менять IP-адрес и прочие параметры? Где это все?
      Но, не все сразу! Дабы не слыть людьми темными и уметь разбираться в предложенном материале, предлагаю для самостоятельного изучения такие ф-ции как: AddIpAddress и DeleteIpAddress. Кучу всего интересного сможете найти также и тут.

      Итак, прозвенел звонок, лекция окончилась. Вероятно, тема будет продолжена. Если сия информация Вам помогла - буду рад благодарностям в комментариях, если нет - критике туда же. Если благодарностей будет больше - продолжу. :)

      © ALXR. Founder1e2@gmail.com

      А теперь, обещанный исходный текст:
      Прикреплённый файлПрикреплённый файлiphlpapi.zip (1.81 Кбайт, скачиваний: 621)
        Спасибо большое, очень полезная информация :)
        попробую переписать таким методом, а не через реестр.
        а вот перезапустить сетевой интерфейс с назначенными параметрами я так понял нет такой функции?
        Сообщение отредактировано: boss_ua -
          Цитата boss_ua @
          а вот перезапустить сетевой интерфейс с назначенными параметрами я так понял нет такой функции?

          А зачем?
            ALXR, спасибо, продолжай плз :)
              Цитата ALXR @
              Цитата boss_ua @
              а вот перезапустить сетевой интерфейс с назначенными параметрами я так понял нет такой функции?

              А зачем?

              тоесть когда мы меняем настройки то они тут же вступают в силу,
              так как это проискодит когда я меняю настройки в "Сетевых подключениях"?
                По вопросам №1 и 3 - можно ещё пойти другим путем - с использованием WMI c запросом к Win32_NetworkAdapterConfiguration.
                К сожалению, у меня есть исходники только для BCB, оформленные в компонент. Но по "Win32_NetworkAdapterConfiguration" google выдаст код и для VS. Да и в MSDN есть примеры
                Шлюзы и т.п. можно узнать запросом к Win32_IP4RouteTable
                  Ну не знаю... WMI тяжеловат, ИМХО.
                    Цитата ALXR @
                    Ну не знаю... WMI тяжеловат, ИМХО.

                    если в получении информации скорость не критична то wmi самое то что нужно.
                      Цитата
                      если в получении информации скорость не критична то wmi самое то что нужно.

                      WMI - отличный способ собрать инфу о системе, но с его помощью нельзя ничего менять, в данном случае ИП адрес. Так что для п.2 WMI явно не подходит.
                        Цитата debugx @
                        WMI - отличный способ собрать инфу о системе, но с его помощью нельзя ничего менять, в данном случае ИП адрес. Так что для п.2 WMI явно не подходит.


                        опа что то новенькое...
                        Win32_NetworkAdapterConfiguration Class
                        имеет следующие методы

                        ExpandedWrap disabled
                          DisableIPSec
                          EnableDHCP
                          EnableDNS
                          EnableIPFilterSec
                          EnableIPSec
                          EnableStatic //Ставим ип
                          EnableWINS
                          ReleaseDHCPLease
                          ReleaseDHCPLeaseAll
                          RenewDHCPLease
                          RenewDHCPLeaseAll
                          SetArpAlwaysSourceRoute
                          SetArpUseEtherSNAP
                          SetDatabasePath
                          SetDeadGWDetect
                          SetDefaultTOS
                          SetDefaultTTL
                          SetDNSDomain
                          SetDNSServerSearchOrder
                          SetDNSSuffixSearchOrder
                          SetDynamicDNSRegistration
                          SetForwardBufferMemory
                          SetGateways
                          SetIGMPLevel
                          SetIPConnectionMetric
                          SetIPUseZeroBroadcast
                          SetIPXFrameTypeNetworkPairs
                          SetIPXVirtualNetworkNumber
                          SetKeepAliveInterval
                          SetKeepAliveTime
                          SetMTU
                          SetNumForwardPackets
                          SetPMTUBHDetect
                          SetPMTUDiscovery
                          SetTcpipNetbios
                          SetTcpMaxConnectRetransmissions
                          SetTcpMaxDataRetransmissions
                          SetTcpNumConnections
                          SetTcpUseRFC1122UrgentPointer
                          SetTcpWindowSize
                          SetWINSServer
                        Сообщение отредактировано: Nahel -
                          А можно не создавать холивар на тему WMI vs WinAPI?
                            Цитата ALXR @
                            А можно не создавать холивар на тему WMI vs WinAPI?
                            Это точно. Ещё и тут - этого не надо. :no-sad:
                            Просто хотелось отметить, что есть и другие "легальные" способы.
                            На работе у меня затишье (перед бурей) и вчера я начал оформлять код для VC - пусть будет в этом топике?
                            А что лучше - тут и в самом деле - кому что больше по-душе.
                            P.S. До чего неудобно в VC с variant'овским safearray - есть какая нибудь обёртка, ATL-вская например? А то как раз п.2 через VMI на этом завязан (Win32_NetworkAdapterConfiguration::EnableStatic как раз задаёт статический IP и параметрами принимает эти самые VT_SAFEARRAY)

                            Добавлено
                            CComSafeArray - вроде оно, но что-то с типом BSTR как-то не дружит?
                              Ещё можно менять IP адрес с помощью недокументированной ф-ции SetAdapterIpAddress.
                              Ссылки на ее описание и коротенькие примеры я писал здесь : Установить IP,DNS,Gateway программно
                              Однако я слышал, что под вистой она не работает.
                              Сообщение отредактировано: Jenya -
                                Штука в том, что у адептера может быть с десяток IP-адресов. Короче говоря - список. Задавать можно ф-циями из (см. 1-ый пост).
                                  Проблема возникла использую GetIfTable для получения индекса сетевого интерфейса по его имени.
                                  А мне в структуре MIB_IFROW поле wszName функция не инициализирует то есть там пусто(если всю структуру изначально занулить или бред если не занулять) главное все остальное верное.
                                  Использую вот этот код: http://msdn.microsoft.com/en-us/library/aa365943(VS.85).aspx
                                  Сообщение отредактировано: XshStasX -
                                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                  0 пользователей:


                                  Рейтинг@Mail.ru
                                  [ Script execution time: 0,0625 ]   [ 16 queries used ]   [ Generated: 27.03.25, 14:24 GMT ]