Поделиться через


Функция CreateUnicastIpAddressEntry (netioapi.h)

Функция CreateUnicastIpAddressEntry добавляет новую запись одноадресного IP-адреса на локальном компьютере.

Синтаксис

IPHLPAPI_DLL_LINKAGE _NETIOAPI_SUCCESS_ NETIOAPI_API CreateUnicastIpAddressEntry(
  [in] const MIB_UNICASTIPADDRESS_ROW *Row
);

Параметры

[in] Row

Указатель на запись структуры MIB_UNICASTIPADDRESS_ROW для записи одноадресного IP-адреса.

Возвращаемое значение

Если функция выполняется успешно, возвращаемое значение будет NO_ERROR.

Если функция завершается сбоем, возвращаемое значение представляет собой один из следующих кодов ошибок.

Код возврата Описание
ERROR_ACCESS_DENIED
Отказано в доступе". Эта ошибка возвращается при нескольких условиях, которые включают следующее: у пользователя отсутствуют необходимые права администратора на локальном компьютере или приложение не выполняется в расширенной оболочке в качестве встроенного администратора (администратора запуска от имени).
ERROR_INVALID_PARAMETER
В функцию передан недопустимый параметр. Эта ошибка возвращается, если в параметре Row передается указатель NULL, члену AddressMIB_UNICASTIPADDRESS_ROW, на который указывает параметр Row, не задан допустимый адрес IPv4 или IPv6 одноадресной рассылки, или оба элемента InterfaceLuid и InterfaceIndexMIB_UNICASTIPADDRESS_ROW, на которые указывает параметр Row, были не указаны.

Эта ошибка также возвращается для других ошибок в значениях, заданных для членов структуры MIB_UNICASTIPADDRESS_ROW . К этим ошибкам относятся следующие: если элемент ValidLifetime меньше элемента PreferredLifetime , Значение , если элемент PrefixOrigin имеет значение IpPrefixOriginUnchanged , а SuffixOrigin — не ipSuffixOriginUnchanged, если для элемента PrefixOrigin не задано значение IpPrefixOriginUnchanged , а для SuffixOrigin задано значение IpSuffixOriginUnchanged, если prefixOrigin Для элемента не задано значение из перечисления NL_PREFIX_ORIGIN , если для элемента SuffixOrigin не задано значение из перечисления NL_SUFFIX_ORIGIN или если для элемента OnLinkPrefixLength задано значение, превышающее длину IP-адреса, в битах (32 для IPv4-адреса одноадресной рассылки или 128 для одноадресного IPv6-адреса).

ERROR_NOT_FOUND
Не удалось найти указанный интерфейс. Эта ошибка возвращается, если не удалось найти сетевой интерфейс, заданный элементом InterfaceLuid или InterfaceIndexMIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row .
ERROR_NOT_SUPPORTED
Запрос не поддерживается. Эта ошибка возвращается, если на локальном компьютере нет стека IPv4 и в элементе AddressMIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row , указан IPv4-адрес. Эта ошибка также возвращается, если на локальном компьютере нет стека IPv6 и в элементе Address был указан IPv6-адрес.
ERROR_OBJECT_ALREADY_EXISTS
Объект уже существует. Эта ошибка возвращается, если элемент AddressMIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row , является дубликатом существующего одноадресного IP-адреса в интерфейсе, указанном элементом InterfaceLuid или InterfaceIndexMIB_UNICASTIPADDRESS_ROW.
Другое
Используйте FormatMessage , чтобы получить строку сообщения для возвращенной ошибки.

Комментарии

Функция CreateUnicastIpAddressEntry определена в Windows Vista и более поздних версиях.

Функция CreateUnicastIpAddressEntry используется для добавления новой записи одноадресной рассылки IP-адреса на локальном компьютере. Ip-адрес одноадресной рассылки, добавленный функцией CreateUnicastIpAddressEntry , не является постоянным. IP-адрес существует только до тех пор, пока существует объект адаптера. Перезагрузка компьютера приводит к удалению IP-адреса, как и при сбросе сетевого интерфейса вручную карта (NIC). Кроме того, некоторые события PnP могут уничтожить адрес.

Чтобы создать IPv4-адрес, который сохраняется, можно использовать метод EnableStatic класса Win32_NetworkAdapterConfiguration в элементах управления инструментария управления Windows (WMI). Команду netsh также можно использовать для создания постоянного адреса IPv4 или IPv6.

Дополнительные сведения см. в документации по Netsh.exe документации по сокетам Windows.

Функция InitializeUnicastIpAddressEntry должна использоваться для инициализации элементов записи структуры MIB_UNICASTIPADDRESS_ROW значениями по умолчанию. Затем приложение может изменить члены в MIB_UNICASTIPADDRESS_ROW записи, которую оно хочет изменить, а затем вызвать функцию CreateUnicastIpAddressEntry .

Элемент Address в структуре MIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row , должен быть инициализирован допустимым адресом IPv4 или IPv6 одноадресной рассылки. Элемент si_family структуры SOCKADDR_INET в элементе Address должен быть инициализирован для AF_INET или AF_INET6, а соответствующий элемент Ipv4 или Ipv6 структуры SOCKADDR_INET должен иметь допустимый IP-адрес одноадресной рассылки. Кроме того, по крайней мере один из следующих элементов в структуре MIB_UNICASTIPADDRESS_ROW , указываемой на параметр Row , должен быть инициализирован в интерфейсе: InterfaceLuid или InterfaceIndex.

Поля используются в указанном выше порядке. Поэтому если указан InterfaceLuid , то этот элемент используется для определения интерфейса, к которому добавляется одноадресный IP-адрес. Если для элемента InterfaceLuid не задано значение (значения этого элемента были равны нулю), то для определения интерфейса используется элемент InterfaceIndex .

Если элемент OnLinkPrefixLengthMIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row , имеет значение 255, то CreateUnicastIpAddressEntry добавит новый одноадресный IP-адрес с набором элементов OnLinkPrefixLength , равным длине IP-адреса. Поэтому для одноадресного IPv4-адреса OnLinkPrefixLength имеет значение 32, а OnLinkPrefixLength — 128 для одноадресного IPv6-адреса. Если это приведет к неправильной маске подсети для IPv4-адреса или неправильному префиксу ссылки для IPv6-адреса, приложение должно задать для этого элемента правильное значение перед вызовом CreateUnicastIpAddressEntry.

Если ip-адрес одноадресной рассылки создается с неправильно заданным элементом OnLinkPrefixLength , ip-адрес можно изменить, вызвав Метод SetUnicastIpAddressEntry с правильным значением элемента OnLinkPrefixLength .

Элементы DadState, ScopeId и CreationTimeStampструктуры MIB_UNICASTIPADDRESS_ROW , на которую указывает Строка , игнорируются при вызове функции CreateUnicastIpAddressEntry . Эти элементы задаются сетевым стеком. Элемент ScopeId автоматически определяется интерфейсом, в который добавляется адрес. Начиная с Windows 10, если для dadState задано значение IpDadStatePreferred в структуре MIB_UNICASTIPADDRESS_ROW при вызове Метода CreateUnicastIpAddressEntry, стек установит начальное состояние DAD адреса на "предпочтительное", а не "предварительное" и выполнит оптимистичный DAD для адреса.

Функция CreateUnicastIpAddressEntry завершится ошибкой , если одноадресный IP-адрес, переданный в элементе AddressMIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row , является дубликатом существующего одноадресного IP-адреса в интерфейсе. Обратите внимание, что IP-адрес замыкания на себя можно добавить в интерфейс замыкания на себя только с помощью функции CreateUnicastIpAddressEntry .

Ip-адрес одноадресной рассылки, переданный в элементе AddressMIB_UNICASTIPADDRESS_ROW , на который указывает параметр Row , нельзя использовать сразу. IP-адрес можно использовать после успешного завершения процесса обнаружения повторяющихся адресов. Процесс обнаружения повторяющихся адресов может занять несколько секунд, так как необходимо отправлять IP-пакеты и ожидать потенциальных ответов. Для IPv6 процесс обнаружения повторяющихся адресов обычно занимает около секунды. Для IPv4 процесс обнаружения повторяющихся адресов обычно занимает около трех секунд.

Если приложению необходимо знать, когда IP-адрес можно использовать после вызова функции CreateUnicastIpAddressEntry , можно использовать два метода. Один из методов использует опрос и функцию GetUnicastIpAddressEntry . Второй метод вызывает одну из функций уведомлений NotifyAddrChange, NotifyIpInterfaceChange или NotifyUnicastIpAddressChange , чтобы настроить асинхронное уведомление об изменении адреса.

Следующий метод описывает, как использовать GetUnicastIpAddressEntry и опрос. После успешного возврата функции CreateUnicastIpAddressEntry приостановите работу на одну-три секунды (в зависимости от того, создается ли IPv6- или IPv4-адрес), чтобы предоставить время для успешного завершения процесса обнаружения адресов дублирования. Затем вызовите функцию GetUnicastIpAddressEntry , чтобы получить обновленную структуру MIB_UNICASTIPADDRESS_ROW и проверить значение элемента DadState . Если для элемента DadState задано значение IpDadStatePreferred, IP-адрес теперь можно использовать. Если для элемента DadState задано значение IpDadStateTentative, обнаружение повторяющихся адресов еще не завершено. В этом случае вызывайте функцию GetUnicastIpAddressEntry каждые полсекунды, пока член DadState по-прежнему имеет значение IpDadStateTentative. Если значение элемента DadState возвращает значение, отличное от IpDadStatePreferred или IpDadStateTentative, обнаружение повторяющихся адресов завершилось ошибкой и IP-адрес недоступен.

Следующий метод описывает, как использовать соответствующую функцию уведомления. После успешного возврата функции CreateUnicastIpAddressEntry вызовите функцию NotifyUnicastIpAddressChange , чтобы зарегистрироваться для уведомления об изменениях ip-адресов одноадресной рассылки IPv6 или IPv4 в зависимости от типа создаваемого IP-адреса. При получении уведомления о создаваемом IP-адресе вызовите функцию GetUnicastIpAddressEntry , чтобы получить элемент DadState . Если для элемента DadState задано значение IpDadStatePreferred, IP-адрес теперь можно использовать. Если для элемента DadState задано значение IpDadStateTentative, обнаружение повторяющихся адресов еще не завершено и приложению необходимо дождаться будущих уведомлений. Если значение элемента DadState возвращает значение, отличное от IpDadStatePreferred или IpDadStateTentative, обнаружение повторяющихся адресов завершилось ошибкой и IP-адрес недоступен.

Если во время процесса обнаружения повторяющихся адресов носитель отключается, а затем повторно подключается, процесс обнаружения повторяющихся адресов перезапускается. Таким образом, время завершения процесса может превысить обычное значение 1 секунды для IPv6 или 3 секунды для IPv4.

Функция CreateUnicastIpAddressEntry может вызываться только пользователем, вошедшего в систему как член группы Администраторы. Если метод CreateUnicastIpAddressEntry вызывается пользователем, не входящий в группу Администраторы, вызов функции завершится ошибкой и возвращается ERROR_ACCESS_DENIED. Эта функция также может завершиться ошибкой из-за контроля учетных записей (UAC) в Windows Vista и более поздних версиях. Если приложение, содержащее эту функцию, выполняется пользователем, вошедшего в систему как участник группы администраторов, отличный от встроенного администратора, этот вызов завершится ошибкой, если приложение не было отмечено в файле манифеста параметром requestedExecutionLevel , для которого задано значение requireAdministrator. Если в приложении отсутствует этот файл манифеста, пользователь, вошедший в систему как член группы администраторов, отличный от встроенного администратора, должен выполнять приложение в расширенной оболочке в качестве встроенного администратора (администратора запуска от имени) для успешного выполнения этой функции.

Примеры

В следующем примере показано, как использовать функцию CreateUnicastIpAddressEntry для добавления новой записи одноадресного IP-адреса на локальном компьютере.


#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2ipdef.h> 
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>

// Need to link with Iphlpapi.lib and Ws2_32.lib
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

HANDLE gCallbackComplete;
HANDLE gNotifyEvent;

void CALLBACK CallCompleted (VOID *callerContext, 
    PMIB_UNICASTIPADDRESS_ROW row, 
    MIB_NOTIFICATION_TYPE notificationType);

int main(int argc, char **argv)  {

    // Declare and initialize variables
    
    unsigned long ipAddress = INADDR_NONE;
    unsigned long ipMask = INADDR_NONE;

    DWORD dwRetVal = 0;

    DWORD dwSize = 0;
    unsigned long status = 0;

    DWORD lastError = 0;
    SOCKADDR_IN localAddress;

    NET_LUID interfaceLuid;
    PMIB_IPINTERFACE_TABLE pipTable = NULL; 
    MIB_UNICASTIPADDRESS_ROW ipRow;

    // Validate the parameters
    if (argc != 3) {
        printf("usage: %s IPv4address IPv4mask\n", argv[0]);
        exit(1);
    }

    ipAddress = inet_addr(argv[1]);
    if (ipAddress == INADDR_NONE) {
        printf("usage: %s IPv4address IPv4mask\n", argv[0]);
        exit(1);
    }

    ipMask = inet_addr(argv[2]);
    if (ipMask == INADDR_NONE) {
        printf("usage: %s IPv4address IPv4mask\n", argv[0]);
        exit(1);
    }


    status = GetIpInterfaceTable( AF_INET, &pipTable );
    if( status != NO_ERROR )
    {
        printf("GetIpInterfaceTable returned error: %ld\n", 
            status);
        exit(1);
    }

    // Use loopback interface
    interfaceLuid = pipTable->Table[0].InterfaceLuid;

    localAddress.sin_family            = AF_INET;
    localAddress.sin_addr.S_un.S_addr  = ipAddress;
    
    FreeMibTable(pipTable);
    pipTable = NULL;    

    // Initialize the row
    InitializeUnicastIpAddressEntry( &ipRow );

    ipRow.InterfaceLuid = interfaceLuid;
    ipRow.Address.Ipv4 = localAddress;

    // Create a Handle to be notified of IP address changes
    gCallbackComplete = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (gCallbackComplete == NULL) {
        printf("CreateEvent failed with error: %d\n", GetLastError() );
        exit(1);
    }    
    
    // Use NotifyUnicastIpAddressChange to determine when the address is ready
    NotifyUnicastIpAddressChange(AF_INET, &CallCompleted, NULL, FALSE, &gNotifyEvent);

    status = CreateUnicastIpAddressEntry(&ipRow);
    if(status != NO_ERROR)
    {
        CancelMibChangeNotify2(gNotifyEvent);
        switch(status)
        {
            case ERROR_INVALID_PARAMETER:
                printf("Error: CreateUnicastIpAddressEntry returned ERROR_INVALID_PARAMETER\n");
                break;
            case ERROR_NOT_FOUND:
                printf("Error: CreateUnicastIpAddressEntry returned ERROR_NOT_FOUND\n");
                break;
            case ERROR_NOT_SUPPORTED:
                printf("Error: CreateUnicastIpAddressEntry returned ERROR_NOT_SUPPORTED\n");
                break;
            case ERROR_OBJECT_ALREADY_EXISTS:
                printf("Error: CreateUnicastIpAddressEntry returned ERROR_OBJECT_ALREADY_EXISTS\n");
                break;
            default:
                //NOTE: Is this case needed? If not, we can remove the ErrorExit() function
                printf("CreateUnicastIpAddressEntry returned error: %d\n", status);
                break;
        }
        exit (status);
        
    }
    else
        printf("CreateUnicastIpAddressEntry succeeded\n");
        
    // Set timeout to 6 seconds
    status = WaitForSingleObject(gCallbackComplete, 6000);
    if(status != WAIT_OBJECT_0)
    {
        CancelMibChangeNotify2(gNotifyEvent);
        CancelMibChangeNotify2(gCallbackComplete);
        switch(status)
        {
            case WAIT_ABANDONED:
                printf("Wait on event was abandoned\n");
                break;
            case WAIT_TIMEOUT:
                printf("Wait on event timed out\n");
                break;
            default:
                printf("Wait on event exited with status %d\n", status);
                break;
        }
        return status;
    }
    printf("Task completed successfully\n");
    CancelMibChangeNotify2(gNotifyEvent);
    CancelMibChangeNotify2(gCallbackComplete);

    exit (0);
}


void CALLBACK CallCompleted(PVOID callerContext, PMIB_UNICASTIPADDRESS_ROW row, MIB_NOTIFICATION_TYPE notificationType)
{

    ADDRESS_FAMILY addressFamily; 
    SOCKADDR_IN sockv4addr;
    struct in_addr ipv4addr;
    
    // Ensure that this is the correct notification before setting gCallbackComplete
    // NOTE: Is there a stronger way to do this?
    if(notificationType == MibAddInstance) {
        printf("NotifyUnicastIpAddressChange received an Add instance\n");
        addressFamily = (ADDRESS_FAMILY) row->Address.si_family;
        switch (addressFamily) {
            case AF_INET:
                printf("\tAddressFamily: AF_INET\n");
                break;
            case AF_INET6:
                printf("\tAddressFamily: AF_INET6\n");
                break;
            default:    
                printf("\tAddressFamily: %d\n", addressFamily);
                break;
       }
       if (addressFamily == AF_INET) {
            sockv4addr = row->Address.Ipv4;
            ipv4addr = sockv4addr.sin_addr;
            printf("IPv4 address:  %s\n", inet_ntoa(ipv4addr) );
       }     
       if (callerContext != NULL)
           printf("Received a CallerContext value\n");
           
       SetEvent(gCallbackComplete);
    }    
    return;
}

Требования

Требование Значение
Минимальная версия клиента Windows Vista [только классические приложения]
Минимальная версия сервера Windows Server 2008 [только классические приложения]
Целевая платформа Windows
Header netioapi.h (включая Iphlpapi.h)
Библиотека Iphlpapi.lib
DLL Iphlpapi.dll

См. также раздел

DeleteUnicastIpAddressEntry

GetUnicastIpAddressEntry

GetUnicastIpAddressTable

Справочник по вспомогательным функциям IP

InitializeUnicastIpAddressEntry

MIB_UNICASTIPADDRESS_ROW

MIB_UNICASTIPADDRESS_TABLE

Netsh.exe

NotifyAddrChange

NotifyIpInterfaceChange

NotifyUnicastIpAddressChange

SetUnicastIpAddressEntry