Dual-STA запроса и конфигурации
Заметка
Для разработки функций, описанных в этом разделе, требуется пакет SDK для Windows 11 (10.0.22000.194) и более поздних версий.
Существует два типа интерфейсов, связанных с адаптерами, поддерживающими подключения двухстанционных станций (двойное СТА), первичные интерфейсы STA и вторичные интерфейсы STA. Все адаптеры предоставляют основной интерфейс STA, но только адаптеры, поддерживающие функции двойного СТА, предоставляют вторичные интерфейсы STA.
По умолчанию Windows подключается только к основному интерфейсу STA. Windows подключается к дополнительному интерфейсу STA только в том случае, если выполнены все следующие условия:
- Драйвер указывает на поддержку вторичных интерфейсов STA в его возможностях.
- Нет политики, препятствующей вторичному подключению STA.
- Существует хотя бы одно приложение, которое вызывало WlanSetInterface с wlan_intf_opcode_secondary_sta_synchronized_connections opcode с параметром, заданным для
TRUE
.
Запрос интерфейсов с двумя sta
Чтобы определить, настроен ли адаптер для dual-STA и получить список дополнительных интерфейсов STA GUID, приложение должно вызывать WlanQueryInterface (wlanapi.h) с помощью wlan_intf_opcode_secondary_sta_interfaces opcode.
Этот opcode принимает в качестве параметра GUID интерфейса первичной станции (STA), который можно получить при вызове WlanEnumInterfaces), и возвращает список вторичных интерфейсов STA, связанных с этим основным интерфейсом STA.
Ниже приведен пример получения списка вторичных интерфейсов STA.
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <wlanapi.h>
#include <Windot11.h> // for DOT11_SSID struct
#include <objbase.h>
#include <wtypes.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Wlanapi.lib and Ole32.lib
#pragma comment(lib, "wlanapi.lib")
#pragma comment(lib, "ole32.lib")
DWORD QueryDualStaInterfaces()
{
HANDLE hClient;
DWORD version;
DWORD dwResult = WlanOpenHandle(WLAN_API_VERSION_2_0, nullptr, &version, &hClient);
if (dwResult != ERROR_SUCCESS)
{
wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);
return dwResult;
}
PWLAN_INTERFACE_INFO_LIST pPrimaryIntfList = nullptr;
dwResult = WlanEnumInterfaces(hClient, nullptr, &pPrimaryIntfList);
if (dwResult != ERROR_SUCCESS)
{
wprintf(L"WlanEnumInterfaces FAILed, error = %u\n", dwResult);
WlanCloseHandle(hClient, NULL);
return dwResult;
}
wprintf(L"There are %u primary interfaces in the system\n", pPrimaryIntfList->dwNumberOfItems);
for (UINT i = 0; i < pPrimaryIntfList->dwNumberOfItems; i++)
{
WCHAR* strPrimaryUuid = nullptr;
if (UuidToStringW(&pPrimaryIntfList->InterfaceInfo[i].InterfaceGuid, reinterpret_cast<RPC_WSTR*>(&strPrimaryUuid)) != RPC_S_OK)
{
strPrimaryUuid = nullptr;
}
DWORD dwDataSize = 0;
PWLAN_INTERFACE_INFO_LIST pSecondaryIntfList = nullptr;
dwResult = WlanQueryInterface(
hClient,
&pPrimaryIntfList->InterfaceInfo[i].InterfaceGuid,
wlan_intf_opcode_secondary_sta_interfaces,
NULL,
&dwDataSize,
reinterpret_cast<PVOID*>(&pSecondaryIntfList),
NULL);
if (dwResult == ERROR_SUCCESS)
{
wprintf(
L"\t[%d]\tInterface %ws (State = %d) has %u Secondary interfaces\n",
i,
strPrimaryUuid ? strPrimaryUuid : L"Unknown",
pPrimaryIntfList->InterfaceInfo[i].isState,
pSecondaryIntfList->dwNumberOfItems);
for (UINT j = 0; j < pSecondaryIntfList->dwNumberOfItems; j++)
{
WCHAR* strSecondaryUuid = nullptr;
if (UuidToStringW(&pSecondaryIntfList->InterfaceInfo[j].InterfaceGuid, reinterpret_cast<RPC_WSTR*>(&strSecondaryUuid)) == RPC_S_OK)
{
wprintf(
L"\t\t[%d]\tSecondary Interface GUID: %ws, (State = %d)\n",
j,
strSecondaryUuid,
pSecondaryIntfList->InterfaceInfo[j].isState);
RpcStringFreeW(reinterpret_cast<RPC_WSTR*>(&strSecondaryUuid));
}
}
WlanFreeMemory(pSecondaryIntfList);
}
else
{
wprintf(L"\t[%d]\tInterface %ws has 0 Secondary interfaces, error = %u\n", i, strPrimaryUuid ? strPrimaryUuid : L"Unknown", dwResult);
}
if (strPrimaryUuid)
{
RpcStringFreeW(reinterpret_cast<RPC_WSTR*>(&strPrimaryUuid));
}
}
WlanFreeMemory(pPrimaryIntfList);
WlanCloseHandle(hClient, NULL);
return dwResult;
}
Запрос состояния синхронизированных подключений с двумя sta
Чтобы определить, будет ли адаптер автоматически подключаться через дополнительный интерфейс STA после подключения через основной интерфейс STA, приложение может запрашивать текущее состояние, вызвав WlanQueryInterface с помощью wlan_intf_opcode_secondary_sta_synchronized_connections opcode.
Этот код опкода возвращает значение boOL, указывающее, синхронизированы ли первичные и вторичные соединения STA.
Ниже приведен пример запроса к этому состоянию.
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <wlanapi.h>
#include <Windot11.h> // for DOT11_SSID struct
#include <objbase.h>
#include <wtypes.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Wlanapi.lib and Ole32.lib
#pragma comment(lib, "wlanapi.lib")
#pragma comment(lib, "ole32.lib")
DWORD QueryDualStaConnectivity()
{
HANDLE hClient;
DWORD version;
DWORD dwResult = WlanOpenHandle(WLAN_API_VERSION_2_0, nullptr, &version, &hClient);
if (dwResult != ERROR_SUCCESS)
{
wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);
return dwResult;
}
PWLAN_INTERFACE_INFO_LIST pPrimaryIntfList = nullptr;
dwResult = WlanEnumInterfaces(hClient, nullptr, &pPrimaryIntfList);
if (dwResult != ERROR_SUCCESS)
{
wprintf(L"WlanEnumInterfaces FAILed, error = %u\n", dwResult);
WlanCloseHandle(hClient, NULL);
return dwResult;
}
//
// Need to call the API only once to query/change the state.
//
if (pPrimaryIntfList->dwNumberOfItems)
{
WCHAR* strPrimaryUuid = nullptr;
if (UuidToStringW(&pPrimaryIntfList->InterfaceInfo[0].InterfaceGuid, reinterpret_cast<RPC_WSTR*>(&strPrimaryUuid)) != RPC_S_OK)
{
strPrimaryUuid = nullptr;
}
DWORD dwDataSize = 0;
PBOOL bQueriedValue = NULL;
dwResult = WlanQueryInterface(
hClient,
&pPrimaryIntfList->InterfaceInfo[0].InterfaceGuid,
wlan_intf_opcode_secondary_sta_synchronized_connections,
NULL,
&dwDataSize,
(PVOID*)&bQueriedValue,
NULL);
if (dwResult == ERROR_SUCCESS)
{
wprintf(L"Secondary Sta Synchronized connections is currently %ws\n", *bQueriedValue ? L"Enabled" : L"Disabled");
WlanFreeMemory(bQueriedValue);
}
else
{
wprintf(L"Failed to query Secondary Sta Synchronized connections - error = %u\n", dwResult);
}
if (strPrimaryUuid)
{
RpcStringFreeW(reinterpret_cast<RPC_WSTR*>(&strPrimaryUuid));
}
}
WlanFreeMemory(pPrimaryIntfList);
WlanCloseHandle(hClient, NULL);
return dwResult;
}
Включение подключений на вторичном интерфейсе STA
Windows не будет подключаться к дополнительным интерфейсам STA, если приложение не будет использовать дополнительное подключение STA. Чтобы управлять подключением Windows к дополнительному интерфейсу STA, приложение должно вызывать WlanSetInterface с помощью wlan_intf_opcode_secondary_sta_synchronized_connections opcode с параметром BOOL, указывающим, следует ли включать или отключать подключения в дополнительном интерфейсе STA. Значение TRUE
указывает, что требуется включить дополнительное подключение STA, а значение FALSE
указывает, что больше не требуется дополнительное подключение STA.
Вызов API с же значением (TRUE
или FALSE
) несколько раз является избыточным, и только первый экземпляр нового значения приводит к изменению функциональности.
После включения дополнительного подключения STA приложение должно оставаться открытым дескриптором службы в течение длительности использования дополнительного подключения STA. После того как приложение отключает дополнительное подключение STA явным образом (путем вызова WlanSetInterface с wlan_intf_opcode_secondary_sta_synchronized_connections и значением параметра FALSE
или путем вызова WlanCloseHandle) или неявно (путем выхода), Windows отключит подключение через дополнительный интерфейс STA в какой-то момент времени после этого.
Дополнительное подключение STA будет оставаться включено до тех пор, пока приложение запрашивает его по крайней мере один раз.
Ниже приведен пример включения или отключения дополнительного подключения STA.
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <wlanapi.h>
#include <Windot11.h> // for DOT11_SSID struct
#include <objbase.h>
#include <wtypes.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Wlanapi.lib and Ole32.lib
#pragma comment(lib, "wlanapi.lib")
#pragma comment(lib, "ole32.lib")
DWORD SetDualStaConnectivity(BOOL bEnable)
{
HANDLE hClient;
DWORD version;
DWORD dwResult = WlanOpenHandle(WLAN_API_VERSION_2_0, nullptr, &version, &hClient);
if (dwResult != ERROR_SUCCESS)
{
wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);
return dwResult;
}
PWLAN_INTERFACE_INFO_LIST pPrimaryIntfList = nullptr;
dwResult = WlanEnumInterfaces(hClient, nullptr, &pPrimaryIntfList);
if (dwResult != ERROR_SUCCESS)
{
wprintf(L"WlanEnumInterfaces FAILed, error = %u\n", dwResult);
WlanCloseHandle(hClient, NULL);
return dwResult;
}
//
// Only need to call the API once to query/change the state
//
if (pPrimaryIntfList->dwNumberOfItems)
{
WCHAR* strPrimaryUuid = nullptr;
if (UuidToStringW(&pPrimaryIntfList->InterfaceInfo[0].InterfaceGuid, reinterpret_cast<RPC_WSTR*>(&strPrimaryUuid)) != RPC_S_OK)
{
strPrimaryUuid = nullptr;
}
dwResult = WlanSetInterface(
hClient,
&pPrimaryIntfList->InterfaceInfo[0].InterfaceGuid,
wlan_intf_opcode_secondary_sta_synchronized_connections,
sizeof(bEnable),
reinterpret_cast<PBYTE>(&bEnable),
NULL);
if (dwResult == ERROR_SUCCESS)
{
wprintf(L"Successfully set Sec Sta opcode = %x on Primary Interface %ws\n", bEnable, strPrimaryUuid);
}
else
{
wprintf(L"FAILed set Sec Sta opcode = %x on Primary Interface %ws -- error = %u\n", bEnable, strPrimaryUuid, dwResult);
}
if (strPrimaryUuid)
{
RpcStringFreeW(reinterpret_cast<RPC_WSTR*>(&strPrimaryUuid));
}
}
WlanFreeMemory(pPrimaryIntfList);
// Close the handle only when the app no longer needs the dual-sta connection
//
WlanCloseHandle(hClient, NULL);
return dwResult;
}