Compartilhar via


Função CreateUnicastIpAddressEntry (netioapi.h)

A função CreateUnicastIpAddressEntry adiciona uma nova entrada de endereço IP unicast no computador local.

Sintaxe

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

Parâmetros

[in] Row

Um ponteiro para uma entrada de estrutura MIB_UNICASTIPADDRESS_ROW para uma entrada de endereço IP unicast.

Retornar valor

Se a função for bem-sucedida, o valor retornado será NO_ERROR.

Se a função falhar, o valor retornado será um dos seguintes códigos de erro.

Código de retorno Descrição
ERROR_ACCESS_DENIED
Acesso negado. Esse erro é retornado sob várias condições que incluem o seguinte: o usuário não tem os privilégios administrativos necessários no computador local ou o aplicativo não está em execução em um shell aprimorado como administrador interno (administrador de RunAs).
ERROR_INVALID_PARAMETER
Um parâmetro inválido foi passado para a função. Esse erro será retornado se um ponteiro NULL for passado no parâmetro Row , o membro Address do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row não tiver sido definido como um endereço IPv4 ou IPv6 unicast válido ou os membros InterfaceLuid e InterfaceIndex do MIB_UNICASTIPADDRESS_ROW apontados pelo parâmetro Row não foram especificados.

Esse erro também é retornado para outros erros nos valores definidos para membros na estrutura MIB_UNICASTIPADDRESS_ROW . Esses erros incluem o seguinte: se o membro ValidLifetime for menor que o membro PreferredLifetime , se o membro PrefixOrigin estiver definido como IpPrefixOriginUnchanged e o SuffixOrigin não estiver definido como IpSuffixOriginUnchanged, se o membro PrefixOrigin não estiver definido como IpPrefixOriginUnchanged e o SuffixOrigin for definido como IpSuffixOriginUnchanged, se o PrefixOrigin member não é definido como um valor da enumeração NL_PREFIX_ORIGIN , se o membro SuffixOrigin não estiver definido como um valor da enumeração NL_SUFFIX_ORIGIN ou se o membro OnLinkPrefixLength for definido como um valor maior que o comprimento do endereço IP, em bits (32 para um endereço IPv4 unicast ou 128 para um endereço IPv6 unicast).

ERROR_NOT_FOUND
Não foi possível encontrar a interface especificada. Esse erro será retornado se o adaptador de rede especificado pelo membro InterfaceLuid ou InterfaceIndex do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row não puder ser encontrado.
ERROR_NOT_SUPPORTED
A solicitação não terá suporte. Esse erro será retornado se nenhuma pilha IPv4 estiver no computador local e um endereço IPv4 tiver sido especificado no membro Address do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row . Esse erro também será retornado se nenhuma pilha IPv6 estiver no computador local e um endereço IPv6 tiver sido especificado no membro Address .
ERROR_OBJECT_ALREADY_EXISTS
O objeto já existe. Esse erro será retornado se o membro Address do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row for uma duplicata de um endereço IP unicast existente na interface especificada pelo membro InterfaceLuid ou InterfaceIndex do MIB_UNICASTIPADDRESS_ROW.
Outros
Use FormatMessage para obter a cadeia de caracteres de mensagem para o erro retornado.

Comentários

A função CreateUnicastIpAddressEntry é definida no Windows Vista e posterior.

A função CreateUnicastIpAddressEntry é usada para adicionar uma nova entrada de endereço IP unicast em um computador local. O endereço IP unicast adicionado pela função CreateUnicastIpAddressEntry não é persistente. O endereço IP existe apenas enquanto o objeto do adaptador existir. Reiniciar o computador destrói o endereço IP, assim como redefinir manualmente o adaptador de rede cartão (NIC). Além disso, determinados eventos PnP podem destruir o endereço.

Para criar um endereço IPv4 que persiste, o método EnableStatic da Classe Win32_NetworkAdapterConfiguration nos controles WMI (Instrumentação de Gerenciamento do Windows) pode ser usado. O comando netsh também pode ser usado para criar um endereço IPv4 ou IPv6 persistente.

Para obter mais informações, consulte a documentação sobre Netsh.exe na documentação do Windows Sockets.

A função InitializeUnicastIpAddressEntry deve ser usada para inicializar os membros de uma entrada de estrutura MIB_UNICASTIPADDRESS_ROW com valores padrão. Em seguida, um aplicativo pode alterar os membros na entrada MIB_UNICASTIPADDRESS_ROW que deseja modificar e, em seguida, chamar a função CreateUnicastIpAddressEntry .

O membro Address na estrutura MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row deve ser inicializado para um endereço IPv4 ou IPv6 unicast válido. O membro si_family da estrutura SOCKADDR_INET no membro Address deve ser inicializado para AF_INET ou AF_INET6 e o membro Ipv4 ou Ipv6 relacionado da estrutura SOCKADDR_INET deve ser definido como um endereço IP unicast válido. Além disso, pelo menos um dos seguintes membros na estrutura MIB_UNICASTIPADDRESS_ROW apontado para o parâmetro Row deve ser inicializado para a interface: InterfaceLuid ou InterfaceIndex.

Os campos são usados na ordem listada acima. Portanto, se o InterfaceLuid for especificado, esse membro será usado para determinar a interface na qual adicionar o endereço IP unicast. Se nenhum valor tiver sido definido para o membro InterfaceLuid (os valores desse membro foram definidos como zero), o membro InterfaceIndex será usado em seguida para determinar a interface.

Se o membro OnLinkPrefixLength do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row for definido como 255, CreateUnicastIpAddressEntry adicionará o novo endereço IP unicast com o membro OnLinkPrefixLength definido igual ao comprimento do endereço IP. Portanto, para um endereço IPv4 unicast, OnLinkPrefixLength é definido como 32 e OnLinkPrefixLength é definido como 128 para um endereço IPv6 unicast. Se isso resultar na máscara de sub-rede incorreta para um endereço IPv4 ou o prefixo de link incorreto para um endereço IPv6, o aplicativo deverá definir esse membro como o valor correto antes de chamar CreateUnicastIpAddressEntry.

Se um endereço IP unicast for criado com o membro OnLinkPrefixLength definido incorretamente, o endereço IP poderá ser alterado chamando SetUnicastIpAddressEntry com o membro OnLinkPrefixLength definido como o valor correto.

Os membros DadState, ScopeId e CreationTimeStamp da estrutura MIB_UNICASTIPADDRESS_ROW apontada pela Linha são ignorados quando a função CreateUnicastIpAddressEntry é chamada. Esses membros são definidos pela pilha de rede. O membro ScopeId é determinado automaticamente pela interface na qual o endereço é adicionado. A partir de Windows 10, se DadState estiver definido como IpDadStatePreferred na estrutura MIB_UNICASTIPADDRESS_ROW ao chamar CreateUnicastIpAddressEntry, a pilha definirá o estado DAD inicial do endereço como "preferencial" em vez de "provisório" e fará o DAD otimista para o endereço.

A função CreateUnicastIpAddressEntry falhará se o endereço IP unicast passado no membro Address do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row for uma duplicata de um endereço IP unicast existente na interface. Observe que um endereço IP de loopback só pode ser adicionado a uma interface de loopback usando a função CreateUnicastIpAddressEntry .

O endereço IP unicast passado no membro Address do MIB_UNICASTIPADDRESS_ROW apontado pelo parâmetro Row não é utilizável imediatamente. O endereço IP é utilizável depois que o processo de detecção de endereço duplicado for concluído com êxito. Pode levar vários segundos para que o processo de detecção de endereço duplicado seja concluído, pois os pacotes IP precisam ser enviados e as respostas potenciais devem ser aguardadas. Para iPv6, o processo de detecção de endereço duplicado normalmente leva cerca de um segundo. Para iPv4, o processo de detecção de endereço duplicado normalmente leva cerca de três segundos.

Se um aplicativo que precisa saber quando um endereço IP é utilizável após uma chamada para a função CreateUnicastIpAddressEntry , há dois métodos que podem ser usados. Um método usa sondagem e a função GetUnicastIpAddressEntry . O segundo método chama uma das funções de notificação, NotifyAddrChange, NotifyIpInterfaceChange ou NotifyUnicastIpAddressChange para configurar uma notificação assíncrona para quando um endereço for alterado.

O método a seguir descreve como usar GetUnicastIpAddressEntry e sondagem. Após a chamada para a função CreateUnicastIpAddressEntry retornar com êxito , pause por um a três segundos (dependendo se um endereço IPv6 ou IPv4 está sendo criado) para dar tempo para a conclusão bem-sucedida do processo de detecção de endereço de duplicação. Em seguida, chame a função GetUnicastIpAddressEntry para recuperar a estrutura de MIB_UNICASTIPADDRESS_ROW atualizada e examinar o valor do membro DadState . Se o valor do membro DadState estiver definido como IpDadStatePreferred, o endereço IP agora poderá ser utilizável. Se o valor do membro DadState for definido como IpDadStateTentative, a detecção de endereço duplicada ainda não foi concluída. Nesse caso, chame a função GetUnicastIpAddressEntry novamente a cada meio segundo, enquanto o membro DadState ainda está definido como IpDadStateTentative. Se o valor do membro DadState retornar com algum valor diferente de IpDadStatePreferred ou IpDadStateTentative, a detecção de endereço duplicado falhará e o endereço IP não será utilizável.

O método a seguir descreve como usar uma função de notificação apropriada. Depois que a chamada para a função CreateUnicastIpAddressEntry retornar com êxito , chame a função NotifyUnicastIpAddressChange para registrar para ser notificada de alterações em endereços IP unicast IPv6 ou IPv4, dependendo do tipo de endereço IP que está sendo criado. Quando uma notificação for recebida para o endereço IP que está sendo criado, chame a função GetUnicastIpAddressEntry para recuperar o membro DadState . Se o valor do membro DadState estiver definido como IpDadStatePreferred, o endereço IP agora poderá ser utilizável. Se o valor do membro DadState estiver definido como IpDadStateTentative, a detecção de endereço duplicada ainda não foi concluída e o aplicativo precisará aguardar notificações futuras. Se o valor do membro DadState retornar com algum valor diferente de IpDadStatePreferred ou IpDadStateTentative, a detecção de endereço duplicado falhará e o endereço IP não será utilizável.

Se durante o processo de detecção de endereço duplicado a mídia for desconectada e reconectada, o processo de detecção de endereço duplicado será reiniciado. Portanto, é possível que o tempo para concluir o processo aumente além do valor típico de 1 segundo para o valor IPv6 ou 3 segundo para IPv4.

A função CreateUnicastIpAddressEntry só pode ser chamada por um usuário conectado como membro do grupo Administradores. Se CreateUnicastIpAddressEntry for chamado por um usuário que não é membro do grupo Administradores, a chamada de função falhará e ERROR_ACCESS_DENIED será retornado. Essa função também pode falhar devido ao UAC (controle de conta de usuário) no Windows Vista e posterior. Se um aplicativo que contém essa função for executado por um usuário conectado como membro do grupo Administradores diferente do Administrador interno, essa chamada falhará, a menos que o aplicativo tenha sido marcado no arquivo de manifesto com um requestedExecutionLevel definido para exigirAdministrator. Se o aplicativo no não tiver esse arquivo de manifesto, um usuário conectado como membro do grupo Administradores diferente do Administrador interno deverá executar o aplicativo em um shell aprimorado como administrador interno (administrador RunAs) para que essa função tenha êxito.

Exemplos

O exemplo a seguir demonstra como usar a função CreateUnicastIpAddressEntry para adicionar uma nova entrada de endereço IP unicast no computador local.


#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;
}

Requisitos

Requisito Valor
Cliente mínimo com suporte Windows Vista [somente aplicativos da área de trabalho]
Servidor mínimo com suporte Windows Server 2008 [somente aplicativos da área de trabalho]
Plataforma de Destino Windows
Cabeçalho netioapi.h (inclua Iphlpapi.h)
Biblioteca Iphlpapi.lib
DLL Iphlpapi.dll

Confira também

DeleteUnicastIpAddressEntry

GetUnicastIpAddressEntry

GetUnicastIpAddressTable

Referência de função auxiliar de IP

InitializeUnicastIpAddressEntry

MIB_UNICASTIPADDRESS_ROW

MIB_UNICASTIPADDRESS_TABLE

Netsh.exe

NotifyAddrChange

NotifyIpInterfaceChange

NotifyUnicastIpAddressChange

SetUnicastIpAddressEntry