C++ 实现Windows WIFI管理器

news2024/11/15 13:03:36

文章目录

  • 前言
  • 一、代码
  • 二、补充知识
  • 三、遇到的问题
    • 字符集转换
  • 四、剩余问题
  • 总结


前言

出于项目需要,需要用C++开发一个wifi界面,实现wifi扫描、wifi连接与断开、wifi密码记住的基础功能。


一、代码

话不多说,直接上代码。

#pragma once
#include <wlanapi.h>
#include <vector>
#include <string>
#include <shared_mutex>

#include "Utility/EncodingFormat/EncodingConversion.h"

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

/// <summary>
/// brief	Record the wifi info that could be used
/// 
/// author	Canliang Wu
/// Day		2023/12/20
/// </summary>
struct WifiInfo
{
	GUID m_guid;							// GUID(全局唯一标识符)是一个128位的唯一标识符,通常表示为32个十六进制数字的字符串。用于标识无线网络接口。每个接口都有一个唯一的 GUID,它可以用来区分计算机上的不同无线网络接口。
	DOT11_BSS_TYPE m_dot11BssType;			// WiFi网络的基本服务集类型
	DOT11_AUTH_ALGORITHM m_authAlgo;		// 默认的身份验证算法
	DOT11_CIPHER_ALGORITHM m_cipherAlgo;	// 默认的加密算法
	std::wstring m_wifiName;				// Wifi名称(Unicode 编码)
	std::string m_wifiSSID;					// 无线网络的 SSID (OEM 编码页)
	int m_wifiIntensity = 0;				// 信号强度,0 ~ 100
	bool m_wifiConnected = false;			// 目前是否连接
	bool m_bHaveProfile = false;			// 是否存在profile
};

enum class WifiMsg : char
{
	Default = 0,

	ScanComplete,
	ScanFailed,
	ConnectionSuccessful,
	ConnectionFailed,
	DisconnectionSuccessful,
	DisconnectionFailed
};

class WifiManager
{
public:
	WifiManager(){}
	~WifiManager() { CloseWLAN(); }

public:

	/*
	* @brief	Open the WLAN for getting client handle and registering notification
	* @param	hwnd - the parent handle for posting the message
	* @param	msgID - the message ID which parent window wants to receive   	
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool OpenWLAN(HWND hwnd, UINT msgID)
	{
		// 1. Prepare the parent info
		m_parentHwnd = hwnd;
		m_msgID = msgID;

		// 2. Get WLAN client handle
		DWORD dwNegotiatedVersion;
		m_dwResult = WlanOpenHandle(1, nullptr, &dwNegotiatedVersion, &m_clientHandle);
		if (m_dwResult != ERROR_SUCCESS)
		{
			CloseWLAN();
			return false;
		}

		// 3. Register the message notification
		if ((m_dwResult = WlanRegisterNotification(m_clientHandle, WLAN_NOTIFICATION_SOURCE_ALL, TRUE, WLAN_NOTIFICATION_CALLBACK(onNotificationCallback), this, nullptr, nullptr)) != ERROR_SUCCESS)
		{
			CloseWLAN();
			return false;
		}

		// 4. First scanning
		if (!ScanWLAN())
			return false;

		return true;
	}

	/*
	* @brief	Close the WLAN 
	* @param	hwnd - the parent handle for posting the message
	* @param	msgID - the message ID which parent window wants to receive
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool CloseWLAN()
	{
		if (m_clientHandle)
		{
			m_dwResult = WlanCloseHandle(m_clientHandle, nullptr);

			if (m_dwResult == ERROR_SUCCESS)
			{
				m_clientHandle = NULL;

				return true;
			}
			else
			{
				return false;
			}
		}

		return true;
	}

	/*
	* @brief	Just scan the PC card and start scanning the avilable wifi list (We will receive the 'wlan_notification_acm_scan_complete' message in the next four seconds)
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool ScanWLAN()
	{
#pragma region 1. Scan the PC card
		PWLAN_INTERFACE_INFO_LIST pInterfaceList = NULL;
		m_dwResult = WlanEnumInterfaces(m_clientHandle, nullptr, &pInterfaceList);
		if (m_dwResult != ERROR_SUCCESS)
		{
			WlanFreeMemory(pInterfaceList);
			CloseWLAN();
			return false;
		}

		m_vGuid.clear();
		for (unsigned int i = 0; i < (int)pInterfaceList->dwNumberOfItems; i++)
		{
			auto pIfInfo = (WLAN_INTERFACE_INFO*)&pInterfaceList->InterfaceInfo[i];
			m_vGuid.push_back(pIfInfo->InterfaceGuid);
		}

		WlanFreeMemory(pInterfaceList);
#pragma endregion

#pragma region 2. Scan the avilable wifi list for each PC card
		for (auto& guid : m_vGuid)
		{
			m_dwResult = WlanScan(m_clientHandle, &guid, NULL, NULL, NULL);
			if (m_dwResult != ERROR_SUCCESS)
			{
				return false;
			}
		}
#pragma endregion

		return true;
	}

	/*
	* @brief	Connect the wifi without password
	* @param	wifiInfo - current wifi info
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool ConnectWLANWithoutPassword(const WifiInfo& wifiInfo)
	{
		m_curWifiInfo = wifiInfo;

		WLAN_CONNECTION_PARAMETERS wlanConnPara;

		/*wlan_connection_mode_profile			将使用配置文件进行连接。
		wlan_connection_mode_temporary_profile	将使用临时配置文件进行连接。
		wlan_connection_mode_discovery_secure	安全发现将用于建立连接。
		wlan_connection_mode_discovery_unsecure	将使用不安全的发现来建立连接。
		wlan_connection_mode_auto				无线服务使用持久性配置文件自动启动连接。
		wlan_connection_mode_invalid*/
		wlanConnPara.wlanConnectionMode = wlan_connection_mode_profile;
		wlanConnPara.strProfile = wifiInfo.m_wifiName.c_str();
		wlanConnPara.pDot11Ssid = NULL;													//设置为NULL时,会去配置有的ssid
		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
		
		m_dwResult = WlanConnect(m_clientHandle, &wifiInfo.m_guid, &wlanConnPara, NULL);

		return m_dwResult == ERROR_SUCCESS;
	}

	/*
	* @brief	Connect the wifi with password
	* @param	wifiInfo - current wifi info
	* @param	password - the password
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool ConnectWLANWithPassword(const WifiInfo& wifiInfo, const std::wstring password)
	{
		// 1. Set the profile into system
		if (!setProfile(wifiInfo, password))
			return false;

		// 2. Connect wifi
		if (!ConnectWLANWithoutPassword(wifiInfo))
			return false;

		return true;
	}

	/*
	* @brief	Disconnect the wifi
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool Disconnect(const WifiInfo& wifiInfo)
	{
		m_dwResult = WlanDisconnect(m_clientHandle, &wifiInfo.m_guid, NULL);
		return m_dwResult == ERROR_SUCCESS;
	}

	/*
	* @brief	Called from paranet window to get the wifi info list when it received the 'wlan_notification_acm_scan_complete' message
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	void GetWifiInfoVec(std::vector<WifiInfo>& vWlanInfo) 
	{
		std::shared_lock<std::shared_mutex> lock(m_mutex);
		vWlanInfo = m_vWlanInfo; 
	}

	/*
	* @brief	Get the error info
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	std::string GetError()
	{
		std::string str_error = "Various error codes.";
		switch (m_dwResult)
		{
		case ERROR_SUCCESS:
			str_error = "SUCCESS.";
			break;
		case ERROR_INVALID_PARAMETER:
			str_error = "One of the following conditions occurred:\n"
				"hClientHandle is NULL or invalid.\n"
				"pInterfaceGuid is NULL.\n"
				"pConnectionParameters is NULL.\n"
				"more:https://docs.microsoft.com/zh-cn/windows/win32/api/wlanapi/nf-wlanapi-wlanconnect.";
			break;
		case ERROR_NOT_ENOUGH_MEMORY:
			str_error = "Failed to allocate memory to create the client context.";
			break;
		case ERROR_REMOTE_SESSION_LIMIT_EXCEEDED:
			str_error = "Too many handles have been issued by the server.";
			break;
		case ERROR_INVALID_HANDLE:
			str_error = "The handle hClientHandle was not found in the handle table.";
			break;
		case ERROR_NDIS_DOT11_POWER_STATE_INVALID:
			str_error = "The radio associated with the interface is turned off. There are no available networks when the radio is off.";
			break;
		case ERROR_ACCESS_DENIED:
			str_error = "The caller does not have sufficient permissions.";
			break;
		case ERROR_ALREADY_EXISTS:
			str_error = "strProfileXml specifies a network that already exists.";
			break;
		case ERROR_BAD_PROFILE:
			str_error = "The profile specified by strProfileXml is not valid. If this value is returned, pdwReasonCode specifies the reason the profile is invalid.";
			break;
		case ERROR_NO_MATCH:
			str_error = "The interface does not support one or more of the capabilities specified in the profile.";
			break;
		default:
			str_error = "Various error codes.";
			break;
		}
		return str_error;
	}

private:
	/*
	* @brief	消息回调,获取WLAN连接的各种状态提示
	* @param	data - NotificationSource 字段 对WLAN系统消息进行分类,NotificationCode表示系统消息的具体消息类型
	* @param	context - the param passed from the dialog register this function
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	static void onNotificationCallback(PWLAN_NOTIFICATION_DATA data, PVOID context)
	{
		if (data != NULL && data->NotificationSource == WLAN_NOTIFICATION_SOURCE_ACM)
		{
			WifiManager* pWifiContext = reinterpret_cast<WifiManager*>(context);
			if (!pWifiContext) return;

			switch (data->NotificationCode)
			{
			case wlan_notification_acm_scan_complete:
			{
				pWifiContext->getAvailableNetworkList();
				PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::ScanComplete), 0);
				OutputDebugString(_T("Scan Successful\n"));
				break;
			}
			case wlan_notification_acm_scan_fail:
			{
				DWORD* pScanFailReason = reinterpret_cast<DWORD*>(data->pData);
				OutputDebugString(_T("Scan Failed\n"));
				PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::ScanFailed), 0);
				break;
			}
			case wlan_notification_acm_connection_start: break;
			case wlan_notification_acm_connection_complete:
			{
				pWifiContext->ScanWLAN();
				
				if (pWifiContext->queryConnectionStatus(pWifiContext->m_curWifiInfo.m_guid))
				{
					OutputDebugString(_T("Connection Successful\n"));
					PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::ConnectionSuccessful), 0);
				}
				else
				{
					OutputDebugString(_T("Connection Failed\n"));
					PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::ConnectionFailed), 0);
				}
				break;
			}
			case wlan_notification_acm_connection_attempt_fail:
			{
				//PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::ConnectionFailed), 0);
				OutputDebugString(_T("Connection Attemp Failed\n"));
				break;
			}
			case wlan_notification_acm_disconnecting: break;
			case wlan_notification_acm_disconnected:
			{
				pWifiContext->ScanWLAN();

				if (pWifiContext->queryConnectionStatus(pWifiContext->m_curWifiInfo.m_guid))
				{
					OutputDebugString(_T("Disconnection Failed\n"));
					PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::DisconnectionFailed), 0);
				}
				else
				{
					OutputDebugString(_T("Disconnection Successful\n"));
					PostMessage(pWifiContext->m_parentHwnd, pWifiContext->m_msgID, WPARAM(WifiMsg::DisconnectionSuccessful), 0);
				}

				break;
			}
			default:
				break;
			}
		}
	}

	/*
	* @brief	Get avaliable network list after receiving the 'wlan_notification_acm_scan_complete' message
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool getAvailableNetworkList()
	{
		std::unique_lock<std::shared_mutex> lock(m_mutex);
		m_vWlanInfo.clear();

		for (auto& guid : m_vGuid)
		{
			PWLAN_AVAILABLE_NETWORK_LIST pWLAN_AVAILABLE_NETWORK_LIST = nullptr;
			m_dwResult = WlanGetAvailableNetworkList(m_clientHandle, &guid, 2, nullptr, &pWLAN_AVAILABLE_NETWORK_LIST);
			if (m_dwResult != ERROR_SUCCESS)
			{
				WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);
				CloseWLAN();

				return false;
			}

			for (DWORD i = 0; i < pWLAN_AVAILABLE_NETWORK_LIST->dwNumberOfItems; i++)
			{
				WLAN_AVAILABLE_NETWORK& network = pWLAN_AVAILABLE_NETWORK_LIST->Network[i];

				if (network.dot11Ssid.uSSIDLength != 0 )
				{
					std::string str_ssid((char*)network.dot11Ssid.ucSSID);

					WifiInfo info;
					info.m_guid = guid;
					info.m_wifiSSID = str_ssid;
					info.m_wifiName = EncodingConversion::UTF8ToUnicode(str_ssid);
					info.m_wifiIntensity = (int)network.wlanSignalQuality;
					info.m_wifiConnected = network.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED;
					info.m_bHaveProfile = network.dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE;
					info.m_dot11BssType = network.dot11BssType;
					info.m_authAlgo = network.dot11DefaultAuthAlgorithm;
					info.m_cipherAlgo = network.dot11DefaultCipherAlgorithm;

					// To fix the problem that have two same SSID when this wifi have profile temporarily(Canliang Wu, 2023/12/20)
					std::wstring targetWifiName = info.m_wifiName;
					auto it = std::find_if(m_vWlanInfo.begin(), m_vWlanInfo.end(), [targetWifiName](const WifiInfo& wifi) {
						return wifi.m_wifiName == targetWifiName;
					});

					if (it == m_vWlanInfo.end())
					{
						m_vWlanInfo.push_back(info);
					}
					else
					{
						if (!it->m_bHaveProfile && info.m_bHaveProfile)
						{
							m_vWlanInfo.erase(it);
							m_vWlanInfo.push_back(info);
						}
					}

				}
			}

			WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);

			CString guidStr = guidToWstring(guid).c_str();
			CString sizeStr;
			sizeStr.Format(L"size = %d\n", m_vWlanInfo.size());
			OutputDebugString(guidStr + _T(", ") + sizeStr);
		}

		return true;
	}

	/*
	* @brief	Convert to guid into wstring
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	std::wstring guidToWstring(const GUID& guid) {
		WCHAR buffer[39];
		swprintf(buffer, L"%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",
			guid.Data1, guid.Data2, guid.Data3,
			guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
			guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
		return buffer;
	}

	/*
	* @brief	Set the new wifi profile into system
	* @param	wifiInfo - wifi info
	* @param	password - the password   	
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	bool setProfile(const WifiInfo& wifiInfo, const std::wstring& password)
	{
		auto connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES);
		auto opCode = wlan_opcode_value_type_invalid;

		auto STR_NAME = L"WIFINAME";
		auto STR_SSID = L"WIFISSID";
		auto STR_PASSWORD = L"PASSWORD";
		auto STR_CONNECTIONTYPE = L"CONNECTIONTYPE";
		auto STR_AUTHENTICATION = L"AUTHENTICATION";
		auto STR_ENCRYPTION = L"ENCRYPTION";

		auto STR_PROFILE_DEMO =
		L"<?xml version=\"1.0\"?> \
		<WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">\
			<name>WIFINAME</name>\
			<SSIDConfig>\
				<SSID>\
					<name>WIFISSID</name>\
				</SSID>\
			</SSIDConfig>\
			<connectionType>CONNECTIONTYPE</connectionType>\
			<connectionMode>auto</connectionMode>\
			<MSM>\
				<security>\
					<authEncryption>\
						<authentication>AUTHENTICATION</authentication>\
						<encryption>ENCRYPTION</encryption>\
						<useOneX>false</useOneX>\
					</authEncryption>\
					<sharedKey>\
						<keyType>passPhrase</keyType>\
						<protected>false</protected>\
						<keyMaterial>PASSWORD</keyMaterial>\
					</sharedKey>\
				</security>\
			</MSM>\
		</WLANProfile>";

		std::wstring strProfile = STR_PROFILE_DEMO;
		stringReplace(strProfile, STR_NAME, wifiInfo.m_wifiName);
		stringReplace(strProfile, STR_SSID, stringToWideChar(wifiInfo.m_wifiSSID));
		stringReplace(strProfile, STR_PASSWORD, password);
		
		switch (wifiInfo.m_dot11BssType)
		{
		case dot11_BSS_type_infrastructure:
			stringReplace(strProfile, STR_CONNECTIONTYPE, L"ESS");
			break;
		case dot11_BSS_type_independent:
			stringReplace(strProfile, STR_CONNECTIONTYPE, L"IBSS");
			break;
		case dot11_BSS_type_any:
			stringReplace(strProfile, STR_CONNECTIONTYPE, L"ANY");
			break;
		default:
			OutputDebugString(L"Unknown BSS Type");
			return false;
		}

		switch (wifiInfo.m_authAlgo)
		{
		case DOT11_AUTH_ALGO_80211_OPEN:
			stringReplace(strProfile, STR_AUTHENTICATION, L"open");
			OutputDebugString(L"Open 802.11 authentication\n");
			break;
		case DOT11_AUTH_ALGO_80211_SHARED_KEY:
			stringReplace(strProfile, STR_AUTHENTICATION, L"shared");
			OutputDebugString(L"Shared 802.11 authentication");
			break;
		case DOT11_AUTH_ALGO_WPA:
			stringReplace(strProfile, STR_AUTHENTICATION, L"WPA");
			OutputDebugString(L"WPA-Enterprise 802.11 authentication\n");
			break;
		case DOT11_AUTH_ALGO_WPA_PSK:
			stringReplace(strProfile, STR_AUTHENTICATION, L"WPAPSK");
			OutputDebugString(L"WPA-Personal 802.11 authentication\n");
			break;
		case DOT11_AUTH_ALGO_WPA_NONE:
			stringReplace(strProfile, STR_AUTHENTICATION, L"none");
			OutputDebugString(L"WPA-NONE,not exist in MSDN\n");
			break;
		case DOT11_AUTH_ALGO_RSNA:
			stringReplace(strProfile, STR_AUTHENTICATION, L"WPA2");
			OutputDebugString(L"WPA2-Enterprise 802.11 authentication\n");
			break;
		case DOT11_AUTH_ALGO_RSNA_PSK:
			stringReplace(strProfile, STR_AUTHENTICATION, L"WPA2PSK");
			OutputDebugString(L"WPA2-Personal 802.11 authentication\n");
			break;
		case DOT11_AUTH_ALGO_WPA3:
			stringReplace(strProfile, STR_AUTHENTICATION, L"WPA3");
			break;
		case DOT11_AUTH_ALGO_WPA3_SAE:
			stringReplace(strProfile, STR_AUTHENTICATION, L"WPA3SAE");
			break;
		default:
			OutputDebugString(L"Unknown authentication");
			return false;
		}

		switch (wifiInfo.m_cipherAlgo)
		{
		case DOT11_CIPHER_ALGO_NONE:
			stringReplace(strProfile, STR_ENCRYPTION, L"none");
			break;
		case DOT11_CIPHER_ALGO_WEP40:
			stringReplace(strProfile, STR_ENCRYPTION, L"WEP");
			break;
		case DOT11_CIPHER_ALGO_TKIP:
			stringReplace(strProfile, STR_ENCRYPTION, L"TKIP");
			break;
		case DOT11_CIPHER_ALGO_CCMP:
			stringReplace(strProfile, STR_ENCRYPTION, L"AES");
			break;
		case DOT11_CIPHER_ALGO_WEP104:
			stringReplace(strProfile, STR_ENCRYPTION, L"WEP");
			break;
		case DOT11_CIPHER_ALGO_WEP:
			stringReplace(strProfile, STR_ENCRYPTION, L"WEP");
			break;
		case DOT11_CIPHER_ALGO_WPA_USE_GROUP:
			OutputDebugString(L"USE-GROUP not exist in MSDN");
			break;
		default:
			OutputDebugString(L"Unknown encryption");
			return false;
		}

		WLAN_REASON_CODE Wlanreason;
		m_dwResult = WlanSetProfile(m_clientHandle, &wifiInfo.m_guid, 0, strProfile.c_str(), NULL, true, NULL, &Wlanreason);

		return m_dwResult == ERROR_SUCCESS;
	}

	/*
	* @brief	Replace specified characters in the string
	* @param	inputoutopt - the whole string 
	* @param	oldStr - the characters in inputoutopt to be replaced
	* @param	newStr - the characters for replacing
	*
	* @author 	Canliang Wu
	* @day		2023/12/19
	*/
	void stringReplace(std::wstring& inputoutopt, const std::wstring oldStr, const std::wstring newStr)
	{
		std::wstring::size_type pos = 0;
		std::wstring::size_type a = oldStr.size();
		std::wstring::size_type b = newStr.size();
		while ((pos = inputoutopt.find(oldStr, pos)) != std::string::npos)
		{
			inputoutopt.replace(pos, a, newStr);
			pos += b;
		}
	}

	/*
	* @brief	Query the wifi connection state after receive the 'wlan_notification_acm_connection_complete ' message
	* @param	interfaceGuid - guid
	*
	* @author 	Canliang Wu
	* @day		2023/12/20
	*/
	bool queryConnectionStatus(const GUID& interfaceGuid) 
	{
		PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL;
		DWORD connectInfoSize;
		WLAN_OPCODE_VALUE_TYPE opCode;

		m_dwResult = WlanQueryInterface(m_clientHandle, &interfaceGuid, wlan_intf_opcode_current_connection, nullptr, &connectInfoSize, (PVOID*)&pConnectInfo, &opCode);

		bool bConnected = false;
		if (m_dwResult == ERROR_SUCCESS) 
		{
			if (pConnectInfo->isState == wlan_interface_state_connected) 
			{
				bConnected = true;
			}
		}
		
		if (pConnectInfo)
		{
			WlanFreeMemory(pConnectInfo);
		}
	
		return bConnected;
	}

	/*
	* @brief	Convert the multi byte string to wide byte string
	* @note   	CP_OEMCP means keeping the string same code page with the system
	*
	* @author 	Canliang Wu
	* @day		2023/12/21
	*/
	std::wstring stringToWideChar(const std::string pKey)
	{
		char* pCStrKey = const_cast<char*>(pKey.c_str());
		//第一次调用返回转换后的字符串长度,用于确认为wchar_t*开辟多大的内存空间
		int pSize = MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, NULL, 0);
		wchar_t* pWCStrKey = new wchar_t[pSize];
		//第二次调用将单字节字符串转换成双字节字符串
		MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, pWCStrKey, pSize);

		std::wstring res(pWCStrKey);
		
		delete[] pWCStrKey;
		pWCStrKey = nullptr;

		return res;
	}

private:
	/// Client handle
	HANDLE m_clientHandle = NULL;

	/// Error code
	DWORD m_dwResult = 0;

	/// GUID vector
	std::vector<GUID> m_vGuid;

	/// Mutex for locking m_vWlanInfo
	std::shared_mutex m_mutex;

	/// Wifi info vector
	std::vector<WifiInfo> m_vWlanInfo;

	/// Current wifi info
	WifiInfo m_curWifiInfo;

	/// Post message 
	HWND m_parentHwnd;
	UINT m_msgID;
};

二、补充知识

  1. 对于未连接过的wifi,系统的做法是,先写入配置文件,然后再根据配置文件去连接。这也是我代码中的做法。
  2. 配置文件路径在:C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces\{网卡GUID}\{Wifi GUID}.xml,再通过Notepad++打开可以看到明文信息。
  3. 通过管理员权限打开cmd,输入下面命令可以查看相关信息。
netsh wlan show profile							// 列出所有的wifi profile
netsh wlan delete profile name="xxx"			// 删除wifi名=xxx的profile
netsh wlan show profile name="xxx" key=clear	// 显示wifi名=xxx的profile详细信息

三、遇到的问题

字符集转换

  1. 我们通过std::string str_ssid((char*)network.dot11Ssid.ucSSID)得到wifi名称,但此时是多字节字符串,项目需要的是宽字节字符串,所以通过EncodingConversion::UTF8ToUnicode(str_ssid)转换为Unicode编码。
  2. 重新写到配置文件中,因为系统API需要的是宽字节字符串,但是如果直接使用wifiInfo.m_wifiSSID是错误的。因为它现在虽然是宽字节,但是却是Unicode字符集。而系统API需要的字符集则不确定,所以使用CP_OEMCP将从系统出来的ssid转回系统需要的宽字节字符串。(说实话,这里我也不太理解,有更懂的小伙伴欢迎留言区评论!)

四、剩余问题

目前代码还剩下这些问题:

  1. Wifi连接时间要比直接通过系统连接来的长;
  2. 存在wifi名全为空白的现象(但是SSID len肯定不为0,因为上面有筛选。但是由于无法稳定找到那个空白wifi,所以也没去调试);
  3. 对于iphone手机热点,需要开启“最大兼容性”才可以连接成功,安卓手机则不需要。
  4. Wifi的总开关仍在系统,也就是需要桌面右下角开启WLAN才可以扫描wifi等操作。
    在这里插入图片描述

总结

总之,我还是希望有人能够在我的基础上,修改存在的问题,并通知我!感谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1353627.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Godot4.2——爬虫小游戏简单制作

目录 一、项目 二、项目功能 怪物 人物 快捷键 分数 游戏说明 提示信息 三、学习视频 UI制作 游戏教程 四、总结 一、项目 视频演示&#xff1a;Godot4爬虫小游戏简单制作_哔哩哔哩bilibili 游戏教程&#xff1a;【小猫godot4入门教程 C#版 已完结】官方入门案例 第…

利用MATLAB绘制折线图

x20:20:140;%x轴上的数据&#xff0c;第一个值代表数据开始&#xff0c;第二个值代表间隔&#xff0c;第三个值代表终止a[0.85, 2.2, 3.45, 2.65, 1.5, 1.9, 1.25]; %a数据y值plot(x,a,-*b); %线性&#xff0c;颜色&#xff0c;标记 axis([0,160,0,4]) %确定x轴与y轴框图大小 …

Redis - 挖矿病毒 db0 库 backup 反复出现解决方案

问题描述 腾讯云的服务器&#xff0c;使用 Docker 部署了 Redis 之后&#xff0c;发现 DB0 中总是出现 4 条 key&#xff0c;分别是 backup01backup02backup03backup04 而自己每次存入 db0 中的数据过一会就会被无缘无故删除掉。 原因分析 挖矿病毒 解决方案 在启动的时候…

Android测试——(下篇)

Android测试&#xff08;五&#xff09;&#xff1a;Instrumented 单元测试 Instrumented 单元测试是在真机并且可以上运行的测试&#xff0c;它利用Android框架API和支持的API&#xff08;如Android测试支持库&#xff09;。如果你的测试需要访问工具信息&#xff08;例如目标…

基于果蝇算法优化的Elman神经网络数据预测 - 附代码

基于果蝇算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于果蝇算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于果蝇优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针…

Jmeter的安装与快速使用(做并发测试)

1、了解 JMeter是一款开源的性能测试工具&#xff0c;它主要用于模拟多种负载条件下的应用程序或服务器的性能和功能。JMeter可以发送不同类型的请求&#xff0c;如HTTP、HTTPS、FTP、SOAP、REST等&#xff0c;并且可以模拟多种负载类型&#xff0c;例如并发用户、线程组、定时…

洛谷普及组P1044栈,题目讲解(无数论基础,纯打表找规律)

[NOIP2003 普及组] 栈 - 洛谷 我先写了个打表的代码&#xff0c;写了一个小时&#xff0c;o(╥﹏╥)o只能说我真不擅长dfs。 int n; std::unordered_map<std::string, int>map; void dfs(std::vector<int>&a, int step,std::stack<int>p, std::string …

【K8S 资源管理】声明式资源管理

目录 一、常用的发布方式 1、蓝绿发布&#xff1a; 2、金丝雀发布&#xff08;灰度发布&#xff09;&#xff1a; 3、滚动更新&#xff08;deployment的默认更新方式&#xff09;&#xff1a; 二、声明式管理方法&#xff08;yaml文件&#xff09; 1、三种发布命令&#x…

C语言中灵活多变的动态内存,malloc函数 free函数 calloc函数 realloc函数

文章目录 &#x1f680;前言&#x1f680;管理动态内存的函数✈️malloc函数✈️free函数✈️calloc函数✈️realloc函数 &#x1f680;在使用动态内存函数时的常见错误✈️对NULL指针的解引用✈️ 对动态开辟空间的越界访问✈️对非动态开辟内存使用free释放✈️使用free释放一…

三、C语言中的分支与循环—for循环 (6)

本章分支结构的学习内容如下&#xff1a; 三、C语言中的分支与循环—if语句 (1) 三、C语言中的分支与循环—关系操作符 (2) 三、C语言中的分支与循环—条件操作符 与逻辑操作符(3) 三、C语言中的分支与循环—switch语句&#xff08;4&#xff09;分支结构 完 本章循环结构的…

【SpringBoot框架篇】34.使用Spring Retry完成任务的重试

文章目录 简要1.为什么需要重试&#xff1f;2.添加maven依赖3.使用Retryable注解实现重试4.基于RetryTemplate模板实现重试 简要 Spring实现了一套重试机制&#xff0c;功能简单实用。Spring Retry是从Spring Batch独立出来的一个功能&#xff0c;已经广泛应用于Spring Batch,…

Linux 进程和计划任务管理

一 内核功用&#xff1a;进程管理、内存管理、文件系统、网络功能、驱动程序、安全功能等 1 程序 是一组计算机能识别和执行的指令&#xff0c;运行于电子计算机上&#xff0c;满足人们某种需求的信息化工具 用于描述进程要完成的功能&#xff0c;是控制进程执行的指令集 2…

LeetCode 82:删除排序链表中的重复元素 II

一、题目描述 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1a; 输入&#xff1a…

B01、类加载子系统-02

JVM架构图-英文版 中文版见下图&#xff1a; 1、概述类的加载器及类加载过程 1.1、类加载子系统的作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识。ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engi…

炫酷按钮制作(HTML+CSS+Javascript)

实现效果&#xff1a; 当鼠标点击按钮时&#xff1a; 实现代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>div{margin-top: 20px;margin-left: 20px;}.button{border: soli…

力扣热题100道-矩阵篇

矩阵 73.矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。** 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例…

CSS 顶部位置翻转动画

<template><div class"container" mouseenter"startAnimation" mouseleave"stopAnimation"><!-- 旋方块 --><div class"box" :class"{ rotate-hor-top: isAnimating }"><!-- 元素内容 --><…

stable diffusion 基础教程-图生图

界面 图生图大概有以下几个功能: 图生图涂鸦绘制局部绘制局部绘制(涂鸦蒙版)其常用的也就上面四个,接下来逐步讲解。 以图反推提示词 图生图可以根据反推提示词来获取相应图片的提示词,目前3种主流方式,如下: CLIP反推提示词:推导出的文本倾向于自然语言的描述方式,…

Hive详解、配置、数据结构、Hive CLI

一、Hive 认识 1. Hive 应用 问题&#xff1a;公司的经营状况&#xff1f; 主题一&#xff1a;财务现金流指标1.1&#xff1a;净现金流入/流出量指标1.2&#xff1a;现金转换周期预算执行状况指标2.1&#xff1a;预算内成本控制指标2.2&#xff1a;预算与实际支出的差异 主题…

电路笔记 :自激振荡电路笔记 电弧打火机

三极管相关 三极管的形象描述 二极管 简单求解&#xff08;理想&#xff09; 优先导通&#xff08;理想&#xff09; 恒压降 稳压管&#xff08;二极管plus&#xff09; 基础工作模块 理想稳压管的工作特性 晶体管之三极管(“两个二极管的组合” ) 电弧打火机电路 1.闭合开…