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。
如果函式失敗,傳回值就是下列其中一個錯誤碼。
傳回碼 | Description |
---|---|
|
存取遭到拒絕。 此錯誤會在數種狀況下傳回:使用者缺少本機電腦上的必要系統管理許可權,或應用程式未在增強殼層中執行,因為內建系統管理員 (RunAs 系統管理員) 。 |
|
無效的參數已傳遞至 函式。 如果在 Row 參數中傳遞 NULL 指標、Row 參數所指向之MIB_UNICASTIPADDRESS_ROW的 Address 成員未設定為有效的單播 IPv4 或 IPv6 位址,或是未指定 Row 參數所指向之MIB_UNICASTIPADDRESS_ROW的 InterfaceLuid 和 InterfaceIndex 成員,就會傳回此錯誤。
這個錯誤也會針對 MIB_UNICASTIPADDRESS_ROW 結構中成員所設定的值中的其他錯誤傳回。 這些錯誤包括:如果 ValidLifetime 成員小於 PreferredLifetime 成員,如果 PrefixOrigin 成員設定為 IpPrefixOriginUnchanged,而 SuffixOrigin 是未設定為 IpSuffixOriginUnchanged,如果 PrefixOrigin 成員未設定為 IpPrefixOriginUnchanged,且 SuffixOrigin 設定為 IpSuffixOriginUnchanged,則為 PrefixOrigin如果 SuffixOrigin 成員未設定為來自 NL_PREFIX_ORIGIN NL_SUFFIX_ORIGIN列舉的值,或是 OnLinkPrefixLength 成員設定為大於 IP 位址長度的值,則為位 (32 的單播 IPv4 位址或單播 IPv6 位址的 128) 。 |
|
找不到指定的介面。 如果找不到 Row 參數所指向之MIB_UNICASTIPADDRESS_ROW的 InterfaceLuid 或 InterfaceIndex 成員所指定的網路介面,就會傳回此錯誤。 |
|
不支援此要求。 如果本機計算機上沒有IPv4堆棧,而且在Row參數所指向之MIB_UNICASTIPADDRESS_ROW的Address成員中指定了IPv4位址,就會傳回此錯誤。 如果本機計算機上沒有IPv6堆棧,且已在 Address 成員中指定IPv6位址,也會傳回此錯誤。 |
|
物件已存在。 如果 Row 參數所指向之MIB_UNICASTIPADDRESS_ROW的 Address 成員是 MIB_UNICASTIPADDRESS_ROW 之 InterfaceLuid 或 InterfaceIndex 成員所指定介面上現有單播 IP 位址的重複專案,就會傳回此錯誤。 |
|
使用 FormatMessage 取得傳回錯誤的訊息字串。 |
備註
CreateUnicastIpAddressEntry 函式是在 Windows Vista 和更新版本上定義。
CreateUnicastIpAddressEntry 函式可用來在本機計算機上新增單播 IP 位址專案。 CreateUnicastIpAddressEntry 函式所新增的單播 IP 位址不是持續性的。 只要配接器物件存在,IP 位址才會存在。 重新啟動計算機會終結IP位址,就像手動重設網路介面卡 (NIC) 一樣。 此外,某些 PnP 事件可能會終結位址。
若要建立保存的 IPv4 位址,可以使用 Windows Management Instrumentation (WMI) 控件中 Win32_NetworkAdapterConfiguration 類別的 EnableStatic 方法 。 netsh 命令也可用來建立持續性 IPv4 或 IPv6 位址。
如需詳細資訊,請參閱 Windows Sockets 檔中 Netsh.exe 的檔。
InitializeUnicastIpAddressEntry 函式應該用來初始化具有預設值之MIB_UNICASTIPADDRESS_ROW結構項目的成員。 應用程式接著可以變更其想要修改 之MIB_UNICASTIPADDRESS_ROW 專案中的成員,然後呼叫 CreateUnicastIpAddressEntry 函式 。
Row 參數所指向之MIB_UNICASTIPADDRESS_ROW結構中的 Address 成員必須初始化為有效的單播 IPv4 或 IPv6 位址。 地址成員中SOCKADDR_INET結構的si_family成員必須初始化為AF_INET或AF_INET6,且SOCKADDR_INET結構的相關 Ipv4 或 Ipv6 成員必須設定為有效的單播 IP 位址。 此外,指向 Row 參數的MIB_UNICASTIPADDRESS_ROW結構中至少必須初始化為介面的下列其中一個成員:InterfaceLuid 或 InterfaceIndex。
欄位會依照上面所列的順序使用。 因此,如果指定 InterfaceLuid ,則會使用此成員來判斷要新增單播 IP 位址的介面。 如果未為 InterfaceLuid 成員設定任何值, (此成員的值設定為零) ,則下一步會使用 InterfaceIndex 成員來判斷介面。
如果 Row 參數所指向之MIB_UNICASTIPADDRESS_ROW的 OnLinkPrefixLength 成員設定為 255,則 CreateUnicastIpAddressEntry 會新增新的單播 IP 位址,並將 OnLinkPrefixLength 成員設定為等於 IP 位址長度。 因此,針對單播 IPv4 位址, OnLinkPrefixLength 會設定為 32,而 OnLinkPrefixLength 會針對單播 IPv6 位址設定為 128。 如果這會導致 IPv4 位址的子網掩碼不正確,或 IPv6 位址的連結前置詞不正確,則應用程式應該先將此成員設定為正確的值,再呼叫 CreateUnicastIpAddressEntry。
如果使用 OnLinkPrefixLength 成員設定不正確建立單播 IP 位址,則藉由呼叫 SetUnicastIpAddressEntry 並將 OnLinkPrefixLength 成員設定為正確的值,即可變更 IP 位址。
呼叫 CreateUnicastIpAddressEntry 函式時,會忽略 Row 所指向之MIB_UNICASTIPADDRESS_ROW結構的 DadState、ScopeId 和 CreationTimeStamp 成員。 這些成員是由網路堆疊所設定。 ScopeId 成員會自動由新增位址的介面決定。 從 Windows 10 開始,如果在呼叫 CreateUnicastIpAddressEntry 時,DadState 設定為 ipDadStateP MIB_UNICASTIPADDRESS_ROW,則當呼叫 CreateUnicastIpAddressEntry 時,堆棧會將位址的初始 DAD 狀態設定為「慣用」,而不是「暫訂」,並且會針對地址執行開放式 DAD。
如果 Row 參數所指向之 MIB_UNICASTIPADDRESS_ROW地址成員中傳遞的單播 IP 位址是介面上現有單播 IP 位址的重複專案,CreateUnicastIpAddressEntry 函式將會失敗。 請注意,回送IP位址只能新增至使用 CreateUnicastIpAddressEntry 函式 的回送介面。
在 Row 參數所指向之MIB_UNICASTIPADDRESS_ROW地址成員中傳遞的單播 IP 位址無法立即使用。 IP 位址可在重複位址偵測程式成功完成之後使用。 重複的位址偵測程式可能需要幾秒鐘的時間才能完成,因為必須傳送IP封包,而且必須等候潛在的回應。 針對 IPv6,重複的位址偵測程式通常需要大約一秒的時間。 針對 IPv4,重複的位址偵測程式通常需要大約三秒的時間。
如果在呼叫 CreateUnicastIpAddressEntry 函式之後,需要知道 IP 位址何時可供使用的應用程式,則可以使用兩種方法。 其中一個方法會使用 polling 和 GetUnicastIpAddressEntry 函式 。 第二種方法會呼叫其中一個通知函式 NotifyAddrChange、 NotifyIpInterfaceChange 或 NotifyUnicastIpAddressChange ,以設定地址變更時的異步通知。
下列方法描述如何使用 GetUnicastIpAddressEntry 和 polling。 呼叫 CreateUnicastIpAddressEntry 函式成功之後,請根據是否) 建立 IPv6 或 IPv4 位址,暫停一到三秒 (,以允許成功完成重複位址偵測程序的時間。 然後呼叫 GetUnicastIpAddressEntry 函式來擷取更新 的MIB_UNICASTIPADDRESS_ROW 結構,並檢查 DadState 成員的值。 如果 DadState 成員的值設定為 IpDadStatePreferred,則 IP 位址現在可供使用。 如果 DadState 成員的值設定為 IpDadStateTentative,則重複的位址偵測尚未完成。 在此情況下,每隔半秒再次呼叫 GetUnicastIpAddressEntry 函式,而 DadState 成員仍設定為 IpDadStateTentative。 如果 DadState 成員的值傳回的值為 IpDadStatePreferred 或 IpDadStateTentative 以外的一些值,則重複的位址偵測失敗,且 IP 位址無法使用。
下列方法描述如何使用適當的通知函式。 呼叫 CreateUnicastIpAddressEntry 函式成功之後,請呼叫 NotifyUnicastIpAddressChange 函式來註冊以通知 IPv6 或 IPv4 單播 IP 位址的變更,視所建立的 IP 位址類型而定。 收到所建立IP位址的通知時,請呼叫 GetUnicastIpAddressEntry 函式以擷取 DadState 成員。 如果 DadState 成員的值設定為 IpDadStatePreferred,則 IP 位址現在可供使用。 如果 DadState 成員的值設定為 IpDadStateTentative,則重複的位址偵測尚未完成,而且應用程式必須等候未來的通知。 如果 DadState 成員的值傳回的值為 IpDadStatePreferred 或 IpDadStateTentative 以外的一些值,則重複的位址偵測失敗,且 IP 位址無法使用。
如果在重複的位址偵測程式期間,媒體會中斷連線,然後重新連線,則會重新啟動重複的位址偵測程式。 因此,完成此程式的時間可能會超過 IPv6 的典型 1 秒值或 IPv4 的 3 秒值。
CreateUnicastIpAddressEntry 函式只能由以 Administrators 群組成員身分登入的使用者呼叫。 如果 CreateUnicastIpAddressEntry 是由不是 Administrators 群組成員的使用者呼叫,則函式呼叫將會失敗,並傳回ERROR_ACCESS_DENIED。 此函式也可能因為 Windows Vista 和更新版本上的用戶帳戶控制 (UAC) 而失敗。 如果包含此函式的應用程式是由使用者以系統管理員以外的系統管理員群組成員身分登入來執行,除非應用程式已在指令清單檔中標示為 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) |
程式庫 | Iphlpapi.lib |
Dll | Iphlpapi.dll |