C++程序中使用Windows系统Native Wifi API的基本教程

Windows应用想要实现连接wifi,监听wifi信号,断开连接等功能,用NativeWifi API是个不错的选择。

打开MSDN,搜索NativeWifi Api,找到Native Wifi页。在这里。

信息量很大,如果像我着急实现上述功能,看海量的文档有些来不及。如果直接给我例子,在运行中调试,阅读代码,效率会更高。

但是,我并没有成功。首先,Sample在SDK中,参见这里。我下载几次都失败了,最后放弃这条路。后来同事给了我一份Sample,我不敢确定是否就是这个,但是代码写的也是很晦涩。我的初衷是简单的使用这些API的例子。

看来还是自己动手吧。看相关API,如果不懂,就找有经验人的例子。

几经周折,终于实现我的需求。让我慢慢道来。

1.获得可用AP列表

参见WlanGetAvailableNetworkList的官方文档,下面有例子。

DWORD WINAPI WlanGetAvailableNetworkList(

_In_ HANDLE hClientHandle,

_In_ const GUID *pInterfaceGuid,

_In_ DWORD dwFlags,

_Reserved_ PVOID pReserved,

_Out_ PWLAN_AVAILABLE_NETWORK_LIST *ppAvailableNetworkList

);

由可用列表便可以找到当前哪个AP正在连接,并显示信号强度。

2.监听当前连接

在获得可用AP列表的基础上,遍历当前AP,看谁正在连接,并取得它的信号。代码片段如下:

bool isConnect = false;

int numberOfItems = pWLAN_AVAILABLE_NETWORK_LIST->dwNumberOfItems;

for(int i = 0; i <= numberOfItems; i++)

{

WLAN_AVAILABLE_NETWORK wlanAN = pWLAN_AVAILABLE_NETWORK_LIST->Network[i];

if(wlanAN.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED)

{

Wprintf(WLAN signal is %s:%d\n", wlanAN.strProfileName, wlanAN.wlanSignalQuality);

isConnect = true;

}

}

if(!isConnect){

wprintf("Wifi is disconnected!\n");}

3.断开连接

如果wifi处于连接状态,将其断开。WlanDisconnect还是容易使用的。原型如下:

DWORD WINAPI WlanDisconnect(

_In_ HANDLE hClientHandle,

_In_ const GUID *pInterfaceGuid,

_Reserved_ PVOID pReserved

);

代码演示在后面。

4.连接一个有profile的AP(已保存过密码)

这是本文的重点。

虽然连接函数WlanConnect原型很简单:

DWORD WINAPI WlanConnect(

_In_ HANDLE hClientHandle,

_In_ const GUID *pInterfaceGuid,

_In_ const PWLAN_CONNECTION_PARAMETERS pConnectionParameters,

_Reserved_ PVOID pReserved

);

但参数PWLAN_CONNECTION_PARAMETERS却是很复杂,只要有一个配错,连接就会失败。

还好我的需求还是蛮简单的,只要连接已有的profile的AP。那么我的工作就会有针对性的开展。挫折了好多天,每次都连接失败,原因是ERROR_INVALID_PARAMETER。

就在今天,我终于成功了。真是会者不难,难者不会啊。

看看连接参数的结构体:

typedef struct _WLAN_CONNECTION_PARAMETERS {

WLAN_CONNECTION_MODE wlanConnectionMode;

LPCWSTR strProfile;

PDOT11_SSID pDot11Ssid;

PDOT11_BSSID_LIST pDesiredBssidList;

DOT11_BSS_TYPE dot11BssType;

DWORD dwFlags;

} WLAN_CONNECTION_PARAMETERS, *PWLAN_CONNECTION_PARAMETERS;

为了实现我的要求,可以这样赋值:

wlanConnectionMode这里设成wlan_connection_mode_profile;

strProfile写上你要连接ap的名称(通常是profile名称);

pDot11Ssid用不上,设置NULL;

pDesiredBssidList同样置成NULL;

dot11BssType我给设成dot11_BSS_type_infrastructure(基础设施?);

dwFlags设置为WLAN_CONNECTION_HIDDEN_NETWORK。

确实是工作了,strProfile如何获取呢?参见监听连接信号中对可用AP列表中第一个profile的获取。

完整代码如下:

//

#include "stdafx.h"

#include <windows.h>

#include <wlanapi.h>

#include <objbase.h>

#include <wtypes.h>

#include <string>

#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")

using namespace std;

int listenStatus()

{

HANDLE hClient = NULL;

DWORD dwMaxClient = 2;

DWORD dwCurVersion = 0;

DWORD dwResult = 0;

DWORD dwRetVal = 0;

int iRet = 0;

WCHAR GuidString[39] = {0};

//Listen the status of the AP you connected.

while(1){

Sleep(5000);

PWLAN_INTERFACE_INFO_LIST pIfList = NULL;//I think wlan interface means network card

PWLAN_INTERFACE_INFO pIfInfo = NULL;

DWORD dwFlags = 0;

dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);

return 1;

}

dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);

return 1;

} else {

wprintf(L"WLAN_INTERFACE_INFO_LIST for this system\n");

wprintf(L"Num Entries: %lu\n", pIfList->dwNumberOfItems);

wprintf(L"Current Index: %lu\n\n", pIfList->dwIndex);

int i;

for (i = 0; i < (int) pIfList->dwNumberOfItems; i++) {

pIfInfo = (WLAN_INTERFACE_INFO *) &pIfList->InterfaceInfo[i];

wprintf(L" Interface Index[%u]:\t %lu\n", i, i);

iRet = StringFromGUID2(pIfInfo->InterfaceGuid, (LPOLESTR) &GuidString,

sizeof(GuidString)/sizeof(*GuidString));

if (iRet == 0)

wprintf(L"StringFromGUID2 failed\n");

else {

wprintf(L" InterfaceGUID[%d]: %ws\n",i, GuidString);

}

wprintf(L" Interface Description[%d]: %ws", i,

pIfInfo->strInterfaceDescription);

wprintf(L"\n");

wprintf(L" Interface State[%d]:\t ", i);

switch (pIfInfo->isState) {

case wlan_interface_state_not_ready:

wprintf(L"Not ready\n");

break;

case wlan_interface_state_connected:

wprintf(L"Connected\n");

break;

case wlan_interface_state_ad_hoc_network_formed:

wprintf(L"First node in a ad hoc network\n");

break;

case wlan_interface_state_disconnecting:

wprintf(L"Disconnecting\n");

break;

case wlan_interface_state_disconnected:

wprintf(L"Not connected\n");

break;

case wlan_interface_state_associating:

wprintf(L"Attempting to associate with a network\n");

break;

case wlan_interface_state_discovering:

wprintf(L"Auto configuration is discovering settings for the network\n");

break;

case wlan_interface_state_authenticating:

wprintf(L"In process of authenticating\n");

break;

default:

wprintf(L"Unknown state %ld\n", pIfInfo->isState);

break;

}

}

}

}

}

int _tmain(int argc, _TCHAR* argv[])

{

HANDLE hClient = NULL;

DWORD dwMaxClient = 2;

DWORD dwCurVersion = 0;

DWORD dwResult = 0;

DWORD dwRetVal = 0;

int iRet = 0;

/* variables used for WlanEnumInterfaces */

PWLAN_INTERFACE_INFO_LIST pIfList = NULL;

PWLAN_INTERFACE_INFO pIfInfo = NULL;

LPCWSTR pProfileName = NULL;

LPWSTR pProfileXml = NULL;

DWORD dwFlags = 0;

pProfileName = argv[1];

wprintf(L"Information for profile: %ws\n\n", pProfileName);

dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);

return 1;

}

dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);

return 1;

} else {

dwResult = WlanDisconnect(hClient, &pIfList->InterfaceInfo[0].InterfaceGuid,NULL);//DISCONNECT FIRST

if(dwResult != ERROR_SUCCESS)

{

printf("WlanDisconnect failed with error: %u\n",dwResult);

return -1;

}

PWLAN_AVAILABLE_NETWORK_LIST pWLAN_AVAILABLE_NETWORK_LIST = NULL;

dwResult = WlanGetAvailableNetworkList(hClient, &pIfList->InterfaceInfo[0].InterfaceGuid,

WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,

NULL, &pWLAN_AVAILABLE_NETWORK_LIST);

if (dwResult != ERROR_SUCCESS)

{

printf("WlanGetAvailableNetworkList failed with error: %u\n",dwResult);

WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);

return -1;

}

WLAN_AVAILABLE_NETWORK wlanAN = pWLAN_AVAILABLE_NETWORK_LIST->Network[0];//PLEASE CHECK THIS YOURSELF

if(pProfileName == NULL)

pProfileName = wlanAN.strProfileName;

WLAN_CONNECTION_PARAMETERS wlanConnPara;

wlanConnPara.wlanConnectionMode =wlan_connection_mode_profile ; //YES,WE CONNECT AP VIA THE PROFILE

wlanConnPara.strProfile =pProfileName; // set the profile name

wlanConnPara.pDot11Ssid = NULL; // SET SSID NULL

wlanConnPara.dot11BssType = dot11_BSS_type_infrastructure; //dot11_BSS_type_any,I do not need it this time.

wlanConnPara.pDesiredBssidList = NULL; // the desired BSSID list is empty

wlanConnPara.dwFlags = WLAN_CONNECTION_HIDDEN_NETWORK; //it works on my WIN7\8

dwResult=WlanConnect(hClient,&pIfList->InterfaceInfo[0].InterfaceGuid,&wlanConnPara ,NULL);

if (dwResult==ERROR_SUCCESS)

{

printf("WlanConnect success!\n");

}

else

{

printf("WlanConnect failed err is %d\n",dwResult);

}

}

listenStatus(); //LISTEN THE STATUS

if (pProfileXml != NULL) {

WlanFreeMemory(pProfileXml);

pProfileXml = NULL;

}

if (pIfList != NULL) {

WlanFreeMemory(pIfList);

pIfList = NULL;

}

return dwRetVal;

}

 

5.打开网络设置界面

遇到以前没有连接过的AP,需要输入密码,那么,直接打开配置界面让用户自己来搞吧。

ShellExecute(

NULL,

L"open",

L"shell:::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{38a98528-6cbf-4ca9-8dc0-b1e1d10f7b1b}",

NULL,

NULL,

SW_SHOWNORMAL);

6.RSSI

当屏幕上打印出“WlanConnect success!”的时候,别提多高兴了。

就像爱迪生试验灯丝一下,在无数次失败后,终于找到了一种材料可以胜任灯丝的工作。这种喜悦真的令人振奋,往日的阴霾和不爽终于一扫而光。

其实我也尝试过WlanGetProfile和WlanSetProfile,虽然有时结果是能够连上指定AP,但是函数返回结果却总是ERROR_INVALID_PARAMETER。

网上的例子,很多都是抄来抄去的,写的不明不白,虽然有过帮助,但是也有些误导。

今天自己成功的连接到指定AP了(用命令行运行我的例子,输入参数profile name),我一定要把它发表出来,让其他人有个参考。

我认为这是一件诚意的作品,在此也谢谢给过我帮助的朋友。

最后说一下获得的信号。标准信号RSSI是负值,而这里获得的信号都是正值(0~100),在有些需要RSSI的地方,我们需要转换一下:

if (pBssEntry->wlanSignalQuality == 0)

iRSSI = -100;

else if (pBssEntry->wlanSignalQuality == 100)

iRSSI = -50;

else

iRSSI = -100 + (pBssEntry->wlanSignalQuality/2);

wprintf(L" Signal Quality[%u]:\t %u (RSSI: %i dBm)\n", j,

pBssEntry->wlanSignalQuality, iRSSI);

    

7.Wifi on与wifi off

下面要说的是在软件层面控制无线网卡的开和关。

问题听起来简单,调查起来复杂,但解决起来却也简单。关键函数便是Native wifi api中的WlanSetInterface。其实这个API功能也是非

常强大的,我只用到其中控制wifi radio state的功能。官网文档在此。

函数原型:

DWORD WINAPI WlanSetInterface(

_In_ HANDLE hClientHandle,

_In_ const GUID *pInterfaceGuid,

_In_ WLAN_INTF_OPCODE OpCode,

_In_ DWORD dwDataSize,

_In_ const PVOID pData,

_Reserved_ PVOID pReserved

);

重点说一下三个参数:

(1) OpCode,指定要设置的参数。我们选择wlan_intf_opcode_radio_state

(2) DwDataSize,pData的size。传入时用sizeof得到。

(3) pData,radio state对应的data是WLAN_PHY_RADIO_STATE。

看看这个state结构体:

typedef struct _WLAN_PHY_RADIO_STATE {

DWORD dwPhyIndex;

DOT11_RADIO_STATE dot11SoftwareRadioState;

DOT11_RADIO_STATE dot11HardwareRadioState;

} WLAN_PHY_RADIO_STATE, *PWLAN_PHY_RADIO_STATE;

Index设为0.

State设置如下:

typedef enum _DOT11_RADIO_STATE {

dot11_radio_state_unknown,

dot11_radio_state_on,

dot11_radio_state_off

} DOT11_RADIO_STATE, *PDOT11_RADIO_STATE;

与前几个API(比如wlanconnect)相比,这个函数的使用简单多了。全部源码如下:

// ManageWirelessNetwork.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include <stdio.h>

#include <windows.h>

#include <shellapi.h>

#include <wlanapi.h>

// Need to link with shell32.lib

#pragma comment(lib, "shell32.lib")

#pragma comment(lib, "wlanapi.lib")

int _tmain(int argc, _TCHAR* argv[])

{

DWORD dwResult = 0;

DWORD dwMaxClient = 2;

DWORD dwCurVersion = 0;

HANDLE hClient = NULL;

PWLAN_INTERFACE_INFO_LIST pIfList = NULL;

PWLAN_INTERFACE_INFO pIfInfo = NULL;

dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);

return false;

}

dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);

return false;

}

WLAN_PHY_RADIO_STATE state;

state.dwPhyIndex = 0;

state.dot11SoftwareRadioState = dot11_radio_state_on;

PVOID pData = &state;

dwResult = WlanSetInterface(hClient,&pIfList->InterfaceInfo[0].InterfaceGuid,

wlan_intf_opcode_radio_state,sizeof(WLAN_PHY_RADIO_STATE),pData,NULL);

if(dwResult == ERROR_SUCCESS)

{

wprintf(L"set state success!\n");

}

else

{

wprintf(L"set state failed!err is %d\n",dwResult);

}

return 0;

}

8.GOTO在释放资源时的作用

GOTO语句有着很臭的名声,我们的老师经常教导我们说,不要轻易使用它。

C++跳转语句有三个:goto、break和continue。它们只是工具,我觉得问题不能归咎于工具,问题在于人。

就像指针一样,goto这个无条件跳转语句力量还是很强大的,如果滥用,出现问题很难排查。

但有些时候goto确实是不二选择,例如我遇到的,在函数中有多个出口,而每个出口都遇到释放资源的时候,与其都把释放语句不厌其烦的写一遍,

不如一个goto语句来的干脆利落。

下面的例子取自上一篇Native Wifi API文章,由于我们的程序经常控制的wifi的on和off,必须注意释放资源。就拿WlanOpenHandle来说,

如果不注意对称WlanCloseHandler,程序几次运行后报错:ERROR_REMOTE_SESSION_LIMIT_EXCEEDED

官网解释为:Too many handles have been issued by the server.

所以我们会在每个API调用后,确认返回值,如果错误,程序将不再继续向下运行,return之前,我们必须释放资源。当出口很多时,我们要写很多同样的代码,

很烦躁,难读,代码急速膨胀。但使用goto后,问题便轻松了许多,请看简单例子:

// ManageWirelessNetwork.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include <stdio.h>

#include <windows.h>

#include <shellapi.h>

#include <wlanapi.h>

// Need to link with shell32.lib

#pragma comment(lib, "shell32.lib")

#pragma comment(lib, "wlanapi.lib")

int _tmain(int argc, _TCHAR* argv[])

{

DWORD dwResult = 0;

DWORD dwMaxClient = 2;

DWORD dwCurVersion = 0;

HANDLE hClient = NULL;

PWLAN_INTERFACE_INFO_LIST pIfList = NULL;

PWLAN_INTERFACE_INFO pIfInfo = NULL;

dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);

return false;

}

dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);

if (dwResult != ERROR_SUCCESS) {

wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);

goto RELEASE_RESOURCE;

}

WLAN_PHY_RADIO_STATE state;

state.dwPhyIndex = 0;

state.dot11SoftwareRadioState = dot11_radio_state_on;//off here too.

PVOID pData = &state;

dwResult = WlanSetInterface(hClient,&pIfList->InterfaceInfo[0].InterfaceGuid,

wlan_intf_opcode_radio_state,sizeof(WLAN_PHY_RADIO_STATE),pData,NULL);

if(dwResult == ERROR_SUCCESS)

{

wprintf(L"set state success!\n");

}

else

{

wprintf(L"set state failed!err is %d\n",dwResult);

}

RELEASE_RESOURCE:

if(hClient)

{

WlanCloseHandle(hClient,NULL);

hClient = NULL;

}

if(pIfList)

{

WlanFreeMemory(pIfList);

pIfList = NULL;

}

if(pIfInfo)

{

WlanFreeMemory(pIfInfo);

pIfInfo = NULL;

}

return 0;

}

最后,goto还会用来跳出多重循环。但需要注意的是,只能从内层跳到外层,不可逆操作。

后记:

其实几个月前就要实现windows上的wifi on和off,问了许多人,发了许多帖子,最后都不了了之。之后的日子里也发生了许多事。国内的

搜索无果,加上google的无法使用,都对调查增加了些许难度。我们把重点先放到了native wifi api的几个方法,见上一篇玩转文章。但

那并不是我想要的。

原以为windows也会想android一样,普通应用没有权限来控制wifi的开关呢,结果并不是这样。这也宣告了之前我的判断失误。

直到今天,通过Bing发现了几条线索。那是通过C#调用native wifi api的问题,里面提及了之前并没有重视的wlansetinterface。

Interface,在这里我觉得可以理解成无线网卡。类似的WlanEnumInterfaces中实现的功能是罗列出当前无线网卡。

无线网卡的设置,其中有一项是radio的状态。

果然,这一切都有了了断。

以上是 C++程序中使用Windows系统Native Wifi API的基本教程 的全部内容, 来源链接: utcz.com/z/325573.html

回到顶部