C++ 实现Windows WIFI管理器

文章目录

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


前言

出于项目需要,需要用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 ~ 100bool 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 infom_parentHwnd = hwnd;m_msgID = msgID;// 2. Get WLAN client handleDWORD dwNegotiatedVersion;m_dwResult = WlanOpenHandle(1, nullptr, &dwNegotiatedVersion, &m_clientHandle);if (m_dwResult != ERROR_SUCCESS){CloseWLAN();return false;}// 3. Register the message notificationif ((m_dwResult = WlanRegisterNotification(m_clientHandle, WLAN_NOTIFICATION_SOURCE_ALL, TRUE, WLAN_NOTIFICATION_CALLBACK(onNotificationCallback), this, nullptr, nullptr)) != ERROR_SUCCESS){CloseWLAN();return false;}// 4. First scanningif (!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 cardPWLAN_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 cardfor (auto& guid : m_vGuid){m_dwResult = WlanScan(m_clientHandle, &guid, NULL, NULL, NULL);if (m_dwResult != ERROR_SUCCESS){return false;}}
#pragma endregionreturn 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时,会去配置有的ssidwlanConnPara.dot11BssType = dot11_BSS_type_infrastructure;						//dot11_BSS_type_any, I do not need it this time.wlanConnPara.pDesiredBssidList = NULL;											// the desired BSSID list is emptywlanConnPara.dwFlags = WLAN_CONNECTION_HIDDEN_NETWORK;							//it works on my WIN7\8m_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 systemif (!setProfile(wifiInfo, password))return false;// 2. Connect wifiif (!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 handleHANDLE m_clientHandle = NULL;/// Error codeDWORD m_dwResult = 0;/// GUID vectorstd::vector<GUID> m_vGuid;/// Mutex for locking m_vWlanInfostd::shared_mutex m_mutex;/// Wifi info vectorstd::vector<WifiInfo> m_vWlanInfo;/// Current wifi infoWifiInfo 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.mzph.cn/news/594431.shtml

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

相关文章

我在CSDN的2023年

一、引言 在2023年的这一年当中&#xff0c;在CSDN的生活让我得到许多知识与启发&#xff0c;也让我获得一些快乐和成就 二、自己的收获 在这一年当中&#xff0c;我从一个只会看别人写的文章解决问题到&#xff0c;可以自己写文章帮别人解决问题&#xff0c;这种成就感是极大…

好用免费的WAF---如何安装雷池社区版

什么是雷池​ 雷池&#xff08;SafeLine&#xff09;是长亭科技耗时近 10 年倾情打造的 WAF&#xff0c;核心检测能力由智能语义分析算法驱动。 Slogan: 不让黑客越雷池半步。 什么是 WAF​ WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。 …

基于VS2019的C++动态链接库DLL生成与调用

一、理论知识及实践经验 实验注意事项及部分程序编写规范&#xff08;部分源自ChatGPT-3.5&#xff09;&#xff1a; Ⅰ __declspec(dllexport)和__declspec(dllimport)是用于在C中定义动态链接库&#xff08;DLL&#xff09;的关键字。在编写动态链接库时&#xff0c;__declsp…

AIGC年度回顾!2024向量数据库是否还是AI发展方向之一?

引言 2023 年&#xff0c;是 AI 技术大爆发的一年&#xff0c;从年初到年末&#xff0c;全球关心技术发展的人们见证了一次次的 AI 技术升级&#xff0c;也逐步加深着对 AGI 发展的畅想。而伴随着生成式人工智能的飞速发展&#xff0c;向量数据库以其独特的技术优势逐渐崭露头角…

修复移动硬盘显示盘符但打不开问题

问题&#xff1a; 移动硬盘显示盘符&#xff0c;但无法打开。点击属性不显示磁盘使用信息。 分析解决&#xff1a; 这是由于硬盘存在损坏导致的&#xff0c;可以通过系统自带的磁盘检查修复解决&#xff0c;而无需额外工具。 假设损坏的盘符是E&#xff0c;在命令行运行以下命令…

流媒体学习之路(WebRTC)——Pacer与GCC(5)

流媒体学习之路(WebRTC)——Pacer与GCC&#xff08;5&#xff09; —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全…

数据库-MySQL 启动方式

以管理员身份运行命令行 或者Shell net start //查看所有服务 net start MYSQL80 //启动服务 net stop MYSQL80 //停止服务完整安装MySQL社区版本的 会有这个 启动服务 停止服务 重启服务

C# 进阶语法,Linq入门到详解

什么是Linq LINQ (Language Integrated Query) 即语言集成查询–用来作查询一些操作类库主要负责对象的查询。 1、LINQ to Objects 主要负责对象的查询 2、LINQ to XML 主要负责XML的查询。 3、LINQ to ADO.NET 主要负责数据库的查询。 linq核心就是对数据源的操作 学linq另外…

15、Kubernetes核心技术 - 探针

目录 一、概述 二、探针类型 2.1、就绪探针&#xff08;Readiness Probe&#xff09; 2.2、存活探针&#xff08;Liveness Probe&#xff09; 三、探针探测方法 3.1、exec 3.2、httpGet 3.3、tcpSocket 四、探针配置项 五、探针使用 5.1、就绪探针&#xff08;Readin…

Java框架相关高频面试题

一&#xff0c;Spring 1&#xff0c;Spring框架中单例bean是线程安全的吗&#xff1f; 2&#xff0c;什么是AOP&#xff1f;你项目有用过吗&#xff1f; 3&#xff0c;Spring事务的失效场景有哪些&#xff1f; 发生自身调用&#xff08;类中使用this调用本类的方法&#xff0…

这货能大大增强ChatGpt的战斗力

今天我给你介绍一个能大大增强ChatGpt的战斗力的工具&#xff1a; gapier。 注册gapier ChatGpt推出了GPTs的功能&#xff0c;在创建GPTs的时候有个Actions的选项&#xff0c;是给我们调用第三方接口用的&#xff0c;以前一直不知道这么用。 直到我发现了一个网站&#xff1a…

11.盛水最多的容器(双指针,C解法)

题目描述&#xff1a; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;…

FX3U-1PG使用

作为扩展模块的安装 伺服驱动器的参数设置 1.设置为0&#xff0c;为位置模式&#xff0c;发送脉冲控制&#xff1b; 2. 设置旋转方向&#xff0c;以及脉冲方式&#xff0c;通常设置为01&#xff0c;因为FX3U-1PG只支持正方向脉冲负方向脉冲方式&#xff1b; 当然想改变电机运…

docker如何配置阿里云镜像加速?

登录阿里云后&#xff0c;我们点击右上角的控制台&#xff0c;控制台中搜索镜像加速服务&#xff0c;然后点击帮助文档的官方镜像加速&#xff1a; 点击容器镜像服务控制台&#xff1a; 在镜像工具里面的镜像加速器中就可以看到&#xff1a; 分别执行即可&#xff1a; 之后我们…

Docker与虚拟机的比对

在Windows操作系统上的对比&#xff1a; 但是官方还是建议我们尽量不要将Docker直接安装到Windows操作系统上。

k8s---声明式资源管理(yml文件)

在k8s当中支持两种声明资源的方式&#xff1a; 1、 yaml格式&#xff1a;主要用于和管理资源对象 2、 json格式&#xff1a;主要用于在API接口之间进行消息传递 声明式管理方法(yaml)文件 1、 适合对资源的修改操作 2、 声明式管理依赖于yaml文件&#xff0c;所有的内容都在y…

5大自动化测试的Python框架,看完就能涨薪5k 【实用干货】

目前&#xff0c;它在Tiobe指数中排名第三个&#xff0c;仅次于Java和C。随着该编程语言的广泛使用&#xff0c;基于Python的自动化测试框架也应运而生&#xff0c;且不断发展与丰富。 因此&#xff0c;开发与测试人员在为手头的项目选择测试框架时&#xff0c;需要考虑许多方…

《小学生》知网期刊投稿方式、投稿邮箱

《小学生》是国家新闻出版总署批准的正规期刊&#xff0c;杂志立足教育&#xff0c;服务全国&#xff0c;致力于为广大基础教育工作者搭建一个展示基础教育理论研究成果&#xff0c;交流经验、合作共进的学术平台。是广大专家、学者、教师、学子发表论文、交流信息的重要平台。…

【EI会议征稿通知】第三届艺术设计与数字化技术国际学术会议( ADDT 2024)

第三届艺术设计与数字化技术国际学术会议( ADDT 2024&#xff09; 2024 3rd International Conference on Art Design and Digital Technology 所谓艺术设计&#xff0c;就是将艺术的审美感应用到与日常生活密切相关的设计中&#xff0c;使其不仅具有审美功能&#xff0c;而且…

电风扇目标检测数据集VOC格式1100张

电风扇的全方位介绍 一、功能特性 电风扇作为一种晋及化的家用电器&#xff0c;其主要功能是利用电机驱动扇叶旋转&#xff0c;从而产生风力&#xff0c;用干调节室内空气流通&#xff0c;达至降温、通风和改善室内环境的目的。此外&#xff0c;现代电风扇还具备定时、遥控、…