国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

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

2020-05-23 14:07:11
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了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的狀態。
果然,這一切都有了了斷。



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 京山县| 乐陵市| 中牟县| 突泉县| 天水市| 彭泽县| 玛多县| 永康市| 成武县| 安康市| 北海市| 东乌珠穆沁旗| 南安市| 太和县| 呼玛县| 张掖市| 廊坊市| 博爱县| 平远县| 汉川市| 孝昌县| 和林格尔县| 彰化县| 泰兴市| 桑植县| 临朐县| 青铜峡市| 友谊县| 玉林市| 沙田区| 景东| 新野县| 新竹市| 赣榆县| 成武县| 龙州县| 桦甸市| 元阳县| 寿光市| 务川| 新竹市|