【Window】Win32 API连接热点

22 篇文章 0 订阅
11 篇文章 0 订阅


Window 无线网络处理相关博文:

【Window】获取电脑连接的网络信息



Window 平台操作无线网络相关的的问题,大多都是使用 wlanapi 库,以下连接Wifi也不例外。


要连接热点得必须有ssid 和 key.,window没有提供现成的函数,提供两个参数可以直接连接wifi,wifi 连接过程也是有学问的,

那就是要提供一个profile ,也就是一个符合某个协议的数据,里面提供了ssid和key.

具体参考:无线网络Profile类型与格式


不同的wifi 类型有不同的profile 格式,所以必须一一对应。

下面以WPA2-PSK类型为例,在msdn找到格式如下:

<?xml version="1.0" encoding="US-ASCII"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>SampleWPA2PSK</name>
    <SSIDConfig>
        <SSID>
            <name>SampleWPA2PSK</name>
        </SSID>
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>auto</connectionMode>
    <autoSwitch>false</autoSwitch>
    <MSM>
        <security>
            <authEncryption>
                <authentication>WPA2PSK</authentication>
                <encryption>AES</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
        </security>
    </MSM>
</WLANProfile>

标签<name>就是存放ssid,那么key在哪里呢,官网解释如下:

The shared key has been omitted from this sample profile. If you try to use this sample profile to connect to a network, you will be prompted to enter a shared key. You can avoid this prompt by adding a sharedKey child element to the security element immediately following the authEncryption element.

The following snippet shows a sharedKey element that contains an unencrypted key. You must replace the comment <!-- insert key here --> with the actual unencrypted key before using this snippet in a profile.

大概意思是,如果提供密码,就在<security>同级标签处添加<sharekey>标签,格式如下:

<sharedKey>
    <keyType>passPhrase</keyType>
    <protected>false</protected>
    <keyMaterial> <!-- insert key here --> </keyMaterial>
</sharedKey>

注意:

<protected>标签里的布尔值表示了密码是否是加密后的ascii串,如果是纯文本就是false好了。

小提示:

命令行输入> netsh wlan show profile

能够显示当前主机存储那些网络的profile,那么我们就能够通过API获取到此profile.


知道了profile 设置,下面就是连接了。

直接上代码,针对wpa2-psk类型网络,我定义了如下宏方便字符串处理:

#define WPA2_PSK_ELEM1 L"WPA2_PSK1"
#define WPA2_PSK_ELEM2 L"WPA2_PSK2"
#define WPA2_PSK_ELEM3 L"WPA2_PSK3"
#define WPA2_PSK_REPLACE_LEN wcslen(L"WPA2_PSKX")
#define WPA2_PSK L"\
<?xml version='1.0'?>\
<WLANProfile xmlns = 'http://www.microsoft.com/networking/WLAN/profile/v1'> \
<name>WPA2_PSK1</name>\
<SSIDConfig>\
<SSID>\
<name>WPA2_PSK2</name>\
</SSID>\
</SSIDConfig>\
<connectionType>ESS</connectionType>\
<connectionMode>auto</connectionMode>\
<MSM>\
<security>\
<authEncryption>\
<authentication>WPA2PSK</authentication>\
<encryption>AES</encryption>\
<useOneX>false</useOneX>\
</authEncryption>\
<sharedKey>\
<keyType>passPhrase</keyType>\
<protected>false</protected>\
<keyMaterial>WPA2_PSK3</keyMaterial>\
</sharedKey>\
</security>\
</MSM>\
</WLANProfile>"

实在的连接代码:


	DWORD dwError = ERROR_SUCCESS;
	DWORD dwActualVresion;

	dwError = WlanOpenHandle(1, NULL, &dwActualVresion, &wlanHandle);
	if (dwError != ERROR_SUCCESS)
	{
		if (m_pConnecter)
			m_pConnecter->Connect(false);
		return ;
	}

	PWLAN_INTERFACE_INFO_LIST pInterfaceList = NULL;
	dwError = WlanEnumInterfaces(wlanHandle, NULL, &pInterfaceList);
	if (dwError != ERROR_SUCCESS)
	{
		if (m_pConnecter)
			m_pConnecter->Connect(false);
		return ;
	}

	if (pInterfaceList->dwNumberOfItems == 0)
	{
		if (m_pConnecter)
			m_pConnecter->Connect(false);
		return ;
	}

	bool findSsid = false;
	PWLAN_AVAILABLE_NETWORK_LIST pAvailableNetworkList = NULL;
	for (DWORD cur = 0; cur < pInterfaceList->dwNumberOfItems; ++cur)
	{
		GUID &guid = pInterfaceList->InterfaceInfo[cur].InterfaceGuid;
		dwError = WlanGetAvailableNetworkList(wlanHandle, &guid, 2, NULL, &pAvailableNetworkList);

		for (DWORD idx = 0; idx < pAvailableNetworkList->dwNumberOfItems; ++idx)
		{
			std::wstring wstr;
			int nLen = pAvailableNetworkList->Network[idx].dot11Ssid.uSSIDLength;
			wstr.resize(nLen, L' ');
			
			int nResult = MultiByteToWideChar(CP_ACP, 0,
				(char *)(pAvailableNetworkList->Network[idx].dot11Ssid.ucSSID), nLen,
				(LPWSTR)wstr.c_str(), nLen);
			
			if (nResult)
			{
				if (m_ssid == wstr)
				{// find network
					findSsid = true;

					WlanDisconnect(wlanHandle, &guid, NULL);
					wchar_t *xml = NULL;
					DWORD access = 0;
					DWORD flag = WLAN_PROFILE_GET_PLAINTEXT_KEY;
					DWORD ret = WlanGetProfile(wlanHandle, &guid, wstr.c_str(), NULL, &xml, &flag, &access);
					if (xml && access == WLAN_WRITE_ACCESS)
						WlanDeleteProfile(wlanHandle, &guid, wstr.c_str(), NULL);
					else
					{// 没有删除权限
						if (xml && m_pConnecter)
							m_pConnecter->Connect(false);
					}
					DWORD reason;
					std::wstring profile(WPA2_PSK);
					size_t idx = profile.find(WPA2_PSK_ELEM1);
					profile.replace(idx, WPA2_PSK_REPLACE_LEN, m_ssid.c_str());
					idx = profile.find(WPA2_PSK_ELEM2);
					profile.replace(idx, WPA2_PSK_REPLACE_LEN, m_ssid.c_str());
					idx = profile.find(WPA2_PSK_ELEM3);
					profile.replace(idx, WPA2_PSK_REPLACE_LEN, m_passwd.c_str());

					ret = WlanSetProfile(wlanHandle, &guid, WLAN_PROFILE_USER, profile.c_str(), NULL, true, NULL, &reason);
					if (ret == 0)
					{
						WLAN_CONNECTION_PARAMETERS param;
						param.pDot11Ssid = NULL;
						param.wlanConnectionMode = wlan_connection_mode_discovery_secure;
						param.strProfile = profile.c_str();
						param.pDesiredBssidList = NULL;
						param.dot11BssType = dot11_BSS_type_infrastructure;
						param.dwFlags = WLAN_CONNECTION_HIDDEN_NETWORK;

						//call connect for waiting NotificationCallBack
						WlanConnect(wlanHandle, &guid, ¶m, NULL);

						ret = WlanRegisterNotification(wlanHandle, WLAN_NOTIFICATION_SOURCE_ACM,
							true, this->INotificationCallBack, this, NULL, NULL);
						if (ret != 0)
							system("pause");
					}
					else if (ret == ERROR_ACCESS_DENIED)
					{
						if (m_pConnecter)
							m_pConnecter->Connect(false);
					}
					break;
				}
				
			}

		}
		if (findSsid)
			break;
	}
	if (!findSsid && m_pConnecter)
		m_pConnecter->Connect(false);

大概过程是,首先枚举主机的网卡,然后对每个网卡进行获取网络列表,直到找到指定的ssid网络。

上面值得注意的是,调用函数WlanSetProfile时,主机就开始连接网络,调用WlanConnect对于此代码来说只是为了注册回调函数,通知我什么时候网络连接完成

试想,如果你要开发界面应用,连接过程绝对是不能再界面线程完成的,不然界面会出现几面的卡顿,那么只能在子线程了,通过回调函数通知界面更新。


注册回调函数:

ret = WlanRegisterNotification(wlanHandle, WLAN_NOTIFICATION_SOURCE_ACM,
				true, this->INotificationCallBack, this, NULL, NULL);

回调函数如下:

void __stdcall INotificationCallBack(
	PWLAN_NOTIFICATION_DATA data,
	PVOID context)
{
	// 连接过程,该函数被回调多次,最后一次是连接成功则是成功
	bool success = false;
	WlanConnecter *connecter = static_cast<WlanConnecter*>(context);
	if (data->NotificationSource == WLAN_NOTIFICATION_SOURCE_ACM)
	{
		if (data->NotificationCode == wlan_notification_acm_connection_complete)
		{
			PWLAN_CONNECTION_NOTIFICATION_DATA pData = 
				static_cast<PWLAN_CONNECTION_NOTIFICATION_DATA>(data->pData);
			if (pData->wlanReasonCode == WLAN_REASON_CODE_SUCCESS)
			{
				success = true;
			}
			if (connecter && connecter->m_pConnecter)
				connecter->m_pConnecter->Connect(success);
		}
	}

}

connecter 是需要此服务类的基类:

class IWlanConnecter
{
public:
	virtual void Connect(bool success) = 0;
};

源码链接






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值