CreateUnicastIpAddressEntry 関数 (netioapi.h)

CreateUnicastIpAddressEntry 関数は、ローカル コンピューターに新しいユニキャスト IP アドレス エントリを追加します。

構文

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

パラメーター

[in] Row

ユニキャスト IP アドレス エントリの MIB_UNICASTIPADDRESS_ROW 構造体エントリへのポインター。

戻り値

関数が成功した場合、戻り値はNO_ERROR。

関数が失敗した場合、戻り値は次のいずれかのエラー コードになります。

リターン コード 説明
ERROR_ACCESS_DENIED
アクセスが拒否されました。 このエラーは、次のようないくつかの条件で返されます。ユーザーがローカル コンピューターに必要な管理特権を持っていないか、アプリケーションが組み込みの管理者 (RunAs 管理者) として拡張シェルで実行されていません。
ERROR_INVALID_PARAMETER
無効なパラメーターが関数に渡されました。 Row パラメーターに NULL ポインターが渡された場合、Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWAddress メンバーが有効なユニキャスト IPv4 アドレスまたは IPv6 アドレスに設定されていない場合、または Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWInterfaceLuid メンバーと InterfaceIndex メンバーの両方が指定されていない場合、このエラーが返されます。

このエラーは、 MIB_UNICASTIPADDRESS_ROW 構造体のメンバーに設定された値の他のエラーにも返されます。 これらのエラーには、ValidLifetime メンバーPreferredLifetime メンバーより小さい場合、PrefixOrigin メンバーが IpPrefixOriginUnchanged に設定されていて、SuffixOriginIpSuffixOriginUnchanged に設定されていない場合、PrefixOrigin メンバーが IpPrefixOriginUnchanged に設定されておらず、SuffixOriginIpSuffixOriginUnchanged に設定されている場合は、SuffixOrigin メンバーがNL_SUFFIX_ORIGIN列挙体の値に設定されていない場合、または OnLinkPrefixLength メンバーが IP アドレスの長さより大きい値に設定されている場合は、ビット単位 (ユニキャスト IPv4 アドレスの場合は 32、ユニキャスト IPv6 アドレスの場合は 128) の場合、member はNL_PREFIX_ORIGIN列挙の値に設定されません。

ERROR_NOT_FOUND
指定したインターフェイスが見つかりませんでした。 Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWInterfaceLuid メンバーまたは InterfaceIndex メンバーによって指定されたネットワーク インターフェイスが見つからなかった場合、このエラーが返されます。
ERROR_NOT_SUPPORTED
要求はサポートされていません。 このエラーは、ローカル コンピューター上に IPv4 スタックがなく、Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWの Address メンバーに IPv4 アドレスが指定されている場合に返されます。 このエラーは、ローカル コンピューターに IPv6 スタックがなく、 アドレス メンバーに IPv6 アドレスが指定されている場合にも返されます。
ERROR_OBJECT_ALREADY_EXISTS
オブジェクトが既に存在します。 Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWの Address メンバーが、MIB_UNICASTIPADDRESS_ROWInterfaceLuid メンバーまたは InterfaceIndex メンバーによって指定されたインターフェイス上の既存のユニキャスト IP アドレスの複製である場合、このエラーが返されます
その他
FormatMessage を使用して、返されたエラーのメッセージ文字列を取得します。

注釈

CreateUnicastIpAddressEntry 関数は、Windows Vista 以降で定義されています。

CreateUnicastIpAddressEntry 関数は、ローカル コンピューターに新しいユニキャスト IP アドレス エントリを追加するために使用されます。 CreateUnicastIpAddressEntry 関数によって追加されたユニキャスト IP アドレスは永続的ではありません。 IP アドレスは、アダプター オブジェクトが存在する限り存在します。 ネットワーク インターフェイス カード (NIC) を手動でリセットする場合と同様に、コンピューターを再起動すると IP アドレスが破棄されます。 また、特定の PnP イベントによってアドレスが破棄される場合があります。

永続化する IPv4 アドレスを作成するには、Windows Management Instrumentation (WMI) コントロール の Win32_NetworkAdapterConfiguration クラスの EnableStatic メソッド を使用できます。 netsh コマンドを使用して、永続的な IPv4 または IPv6 アドレスを作成することもできます。

詳細については、Windows ソケットのドキュメントの Netsh.exe に関するドキュメントを参照してください。

InitializeUnicastIpAddressEntry 関数を使用して、MIB_UNICASTIPADDRESS_ROW構造体エントリのメンバーを既定値で初期化する必要があります。 アプリケーションは、変更する MIB_UNICASTIPADDRESS_ROW エントリのメンバーを変更し、 CreateUnicastIpAddressEntry 関数を呼び出すことができます。

Row パラメーターが指すMIB_UNICASTIPADDRESS_ROW構造体の Address メンバーは、有効なユニキャスト IPv4 または IPv6 アドレスに初期化する必要があります。 Address メンバー内のSOCKADDR_INET構造体のsi_family メンバーは、AF_INETまたはAF_INET6に初期化する必要があり、SOCKADDR_INET構造体の関連する Ipv4 または Ipv6 メンバーを有効なユニキャスト IP アドレスに設定する必要があります。 さらに、Row パラメーターを指すMIB_UNICASTIPADDRESS_ROW構造体の少なくとも 1 つのメンバーをインターフェイスに初期化する必要があります。 InterfaceLuid または InterfaceIndex

フィールドは、上記の順序で使用されます。 したがって、 InterfaceLuid が指定されている場合は、このメンバーを使用して、ユニキャスト IP アドレスを追加するインターフェイスを決定します。 InterfaceLuid メンバーに値が設定されていない場合 (このメンバーの値が 0 に設定されました)、InterfaceIndex メンバーが次に使用されてインターフェイスが決定されます。

Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWOnLinkPrefixLength メンバーが 255 に設定されている場合、CreateUnicastIpAddressEntry は、IP アドレスの長さと等しい OnLinkPrefixLength メンバーが設定された新しいユニキャスト IP アドレスを追加します。 したがって、ユニキャスト IPv4 アドレスの場合、 OnLinkPrefixLength は 32 に設定され、ユニキャスト IPv6 アドレスの 場合は OnLinkPrefixLength が 128 に設定されます。 これにより、IPv4 アドレスのサブネット マスクが正しくないか、IPv6 アドレスのリンク プレフィックスが正しくない場合、アプリケーションは CreateUnicastIpAddressEntry を呼び出す前に、このメンバーを正しい値に設定する必要があります。

OnLinkPrefixLength メンバー セットが正しく設定されていないユニキャスト IP アドレスが作成された場合、OnLinkPrefixLength メンバーが正しい値に設定された SetUnicastIpAddressEntry を呼び出すことによって、IP アドレスが変更される可能性があります。

CreateUnicastIpAddressEntry 関数が呼び出されると、Row が指すMIB_UNICASTIPADDRESS_ROW構造体の DadStateScopeIdCreationTimeStamp メンバーは無視されます。 これらのメンバーは、ネットワーク スタックによって設定されます。 ScopeId メンバーは、アドレスが追加されるインターフェイスによって自動的に決定されます。 Windows 10以降、CreateUnicastIpAddressEntry を呼び出すときにMIB_UNICASTIPADDRESS_ROW構造体で DadStateIpDadStatePreferred に設定されている場合、スタックはアドレスの初期 DAD 状態を "仮の" ではなく "優先" に設定し、アドレスに対して楽観的な DAD を実行します。

Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWAddress メンバーで渡されたユニキャスト IP アドレスがインターフェイス上の既存のユニキャスト IP アドレスの複製である場合、CreateUnicastIpAddressEntry 関数は失敗します。 ループバック IP アドレスは、 CreateUnicastIpAddressEntry 関数を使用してのみループバック インターフェイスに追加できることに注意してください。

Row パラメーターが指すMIB_UNICASTIPADDRESS_ROWAddress メンバーに渡されたユニキャスト IP アドレスは、すぐには使用できません。 IP アドレスは、重複アドレス検出プロセスが正常に完了した後に使用できます。 IP パケットを送信する必要があり、潜在的な応答を待機する必要があるため、重複アドレス検出プロセスが完了するまでに数秒かかることがあります。 IPv6 の場合、重複アドレス検出プロセスには通常約 1 秒かかります。 IPv4 の場合、重複アドレス検出プロセスには通常約 3 秒かかります。

CreateUnicastIpAddressEntry 関数の呼び出し後に IP アドレスがいつ使用可能であるかを知る必要があるアプリケーションには、使用できるメソッドが 2 つあります。 1 つのメソッドでは、ポーリングと GetUnicastIpAddressEntry 関数を 使用します。 2 番目のメソッドは、 通知関数 NotifyAddrChangeNotifyIpInterfaceChange、または NotifyUnicastIpAddressChange のいずれかを呼び出して、アドレスが変更されたときの非同期通知を設定します。

次のメソッドでは、 GetUnicastIpAddressEntry とポーリングを使用する方法について説明します。 CreateUnicastIpAddressEntry 関数の呼び出しが正常に返されたら、1 ~ 3 秒間 (IPv6 アドレスまたは IPv4 アドレスが作成されているかどうかに応じて) 一時停止して、重複アドレス検出プロセスが正常に完了する時間を許可します。 次に 、GetUnicastIpAddressEntry 関数を呼び出して、更新 されたMIB_UNICASTIPADDRESS_ROW 構造体を取得し、 DadState メンバーの値を調べます。 DadState メンバーの値が IpDadStatePreferred に設定されている場合、IP アドレスが使用可能になります。 DadState メンバーの値が IpDadStateTentative に設定されている場合、重複アドレスの検出はまだ完了していません。 この場合、DadState メンバーがまだ IpDadStateTentative に設定されている間は、GetUnicastIpAddressEntry 関数を 0.5 秒ごとに再度呼び出します。 DadState メンバーの値が IpDadStatePreferred または IpDadStateTentative 以外の値で返された場合、重複アドレスの検出は失敗し、IP アドレスは使用できません。

次のメソッドでは、適切な通知関数を使用する方法について説明します。 CreateUnicastIpAddressEntry 関数の呼び出しが正常に返されたら、NotifyUnicastIpAddressChange 関数を呼び出して、作成される IP アドレスの種類に応じて、IPv6 または IPv4 ユニキャスト IP アドレスへの変更を通知するように登録します。 作成される IP アドレスの通知を受信したら、 GetUnicastIpAddressEntry 関数を呼び出して DadState メンバーを取得します。 DadState メンバーの値が IpDadStatePreferred に設定されている場合、IP アドレスが使用可能になります。 DadState メンバーの値が IpDadStateTentative に設定されている場合、重複アドレス検出はまだ完了していないため、アプリケーションは今後の通知を待つ必要があります。 DadState メンバーの値が IpDadStatePreferred または IpDadStateTentative 以外の値で返された場合、重複アドレスの検出は失敗し、IP アドレスは使用できません。

重複アドレス検出プロセス中にメディアが切断されてから再接続された場合、重複アドレス検出プロセスが再開されます。 そのため、プロセスを完了するまでの時間が、IPv6 の場合は一般的な 1 秒の値を超え、IPv4 の場合は 3 秒の値を超える可能性があります。

CreateUnicastIpAddressEntry 関数は、Administrators グループのメンバーとしてログオンしているユーザーのみが呼び出すことができます。 Administrators グループのメンバーではないユーザーによって CreateUnicastIpAddressEntry が呼び出された場合、関数呼び出しは失敗し、ERROR_ACCESS_DENIEDが返されます。 この関数は、Windows Vista 以降のユーザー アカウント制御 (UAC) が原因で失敗する場合もあります。 この関数を含むアプリケーションが、組み込みの Administrator 以外の Administrators グループのメンバーとしてログオンしているユーザーによって実行された場合、 requestedExecutionLevel が requireAdministrator に設定されたマニフェスト ファイルでアプリケーションがマークされていない限り、この呼び出しは失敗します。 上のアプリケーションにこのマニフェスト ファイルがない場合、組み込みの管理者以外の Administrators グループのメンバーとしてログオンしているユーザーは、この関数を成功させるために、組み込みの管理者 (RunAs 管理者) として拡張シェルでアプリケーションを実行する必要があります。

次の例では、 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
ヘッダー netioapi.h (Iphlpapi.h を含む)
Library Iphlpapi.lib
[DLL] Iphlpapi.dll

こちらもご覧ください

DeleteUnicastIpAddressEntry

GetUnicastIpAddressEntry

GetUnicastIpAddressTable

IP ヘルパー関数リファレンス

InitializeUnicastIpAddressEntry

MIB_UNICASTIPADDRESS_ROW

MIB_UNICASTIPADDRESS_TABLE

Netsh.exe

NotifyAddrChange

NotifyIpInterfaceChange

NotifyUnicastIpAddressChange

SetUnicastIpAddressEntry