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

https://www.jb51.net/article/81199.htm效果更好

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

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

信息量很大,如果像我着急实现上述功能,看海量的文档有些来不及。如果直接给我例子,在运行中调试,阅读代码,效率会更高。
但是,我并没有成功。首先,Sample在SDK中,参见这里。我下载几次都失败了,最后放弃这条路。后来同事给了我一份Sample,我不敢确定是否就是这个,但是代码写的也是很晦涩。我的初衷是简单的使用这些API的例子。

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

几经周折,终于实现我的需求。让我慢慢道来。
1.获得可用AP列表
参见WlanGetAvailableNetworkList的官方文档,下面有例子。

?

1

2

3

4

5

6

7

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,看谁正在连接,并取得它的信号。代码片段如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

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还是容易使用的。原型如下:

?

1

2

3

4

5

DWORD WINAPI WlanDisconnect(

 _In_  HANDLE hClientHandle,

 _In_  const GUID *pInterfaceGuid,

 _Reserved_ PVOID pReserved

);

代码演示在后面。
4.连接一个有profile的AP(已保存过密码)
这是本文的重点。
虽然连接函数WlanConnect原型很简单:

?

1

2

3

4

5

6

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。
就在今天,我终于成功了。真是会者不难,难者不会啊。
看看连接参数的结构体:

?

1

2

3

4

5

6

7

8

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的获取。
完整代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

//

#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,需要输入密码,那么,直接打开配置界面让用户自己来搞吧。

?

1

2

3

4

5

6

7

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的地方,我们需要转换一下:

?

1

2

3

4

5

6

7

8

9

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的功能。官网文档在此
函数原型:

?

1

2

3

4

5

6

7

8

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结构体:

?

1

2

3

4

5

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设置如下:

?

1

2

3

4

5

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)相比,这个函数的使用简单多了。全部源码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

// 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后,问题便轻松了许多,请看简单例子:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

// 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的状态。
果然,这一切都有了了断。

猜你喜欢

转载自blog.csdn.net/thanklife/article/details/83751590