IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]VC++玩转Native Wifi API

    lincyang发表于 2014-06-25 14:30:00
    love 0
    

    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.最后

    当屏幕上打印出“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);

    本文到此完毕,虽然没有涵盖大部分API,但还是欢迎大家留意讨论。













    


沪ICP备19023445号-2号
友情链接