Qt :使用Win32 API进行wifi通讯
目录
1:WlanOpenHandle
2:WlanEnumInterfaces
3:WlanGetAvailableNetworkList
4:WlanSetProfile
5:WlanConnect
6:清空和关闭WIFI
wlanapi.h header使用说明:https://docs.microsoft.com/zh-cn/windows/win32/api/wlanapi/
wlanapi.h 论坛中文说明:https://blog.csdn.net/softwave/article/details/41389907
参考内容:
QT---Native Wifi functions 应用(WiFi有密码连接):https://www.cnblogs.com/findumars/p/6294156.html
Native wifi API使用(有涉及XML配置文件,非常详细):https://blog.csdn.net/hgy413/article/details/20784277
Native Wifi functions 应用(WiFi有密码连接)(有涉及XML配置文件):https://blog.csdn.net/xiapang009/article/details/73649383
Windows Native WIFI 编程:https://blog.csdn.net/mafujunfang/article/details/77747444
VC++玩转Native Wifi API:https://blog.csdn.net/lincyang/article/details/34430939
界面设计部分参考,左下角的电池和wifi资源来自于下文
Qt Windows 7(Win7)下获取并显示电池电量和WIFI信号强度:https://blog.csdn.net/caoshangpa/article/details/51062351
非常感谢以上优秀的博文
WIFI相关文章:
wifi网络结构(上):https://blog.csdn.net/peixiuhui/article/details/45674859
wifi网络结构(下):https://blog.csdn.net/peixiuhui/article/details/45674867
wifi驱动的理解(1)——驱动架构:https://blog.csdn.net/peixiuhui/article/details/45674931
wifi驱动的理解(2)——usb接口在wifi模块中的角色:https://blog.csdn.net/peixiuhui/article/details/45674955
wifi驱动的理解(3)——usb接口在wifi模块中的角色:https://blog.csdn.net/peixiuhui/article/details/45674971
wifi网络接入原理(上)——扫描Scanning:https://blog.csdn.net/peixiuhui/article/details/45674881
wifi网络接入原理(中)——认证Authentication:https://blog.csdn.net/peixiuhui/article/details/45674893
wifi网络接入原理(下)——关联Association:https://blog.csdn.net/peixiuhui/article/details/45674917
主要使用的函数如下:(后面链接为MSDN说明)
WlanOpenHandle:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanopenhandle
WlanEnumInterfaces:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanenuminterfaces
WlanGetAvailableNeiworkList:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlangetavailablenetworklist
WlanSetProfile:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlansetprofile
WlanConnect:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanconnect
WlanFreeMemory:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanfreememory
WlanClosehandle:https://docs.microsoft.com/en-us/windows/win32/api/wlanapi/nf-wlanapi-wlanclosehandle
主要是对上述文章内容进行了整理和优化,具体的细节文字说明还请以Win 32 API介绍为主
配置文件定义:
static const QString STR_PROFILE_DEMO =
" \
\
Home \
\
\
Home \
\
\
ESS \
auto \
\
\
\
WPA2PSK \
AES \
false \
\
\
passPhrase \
false \
AAAAAA \
\
\
\
";
以上配置文件,STR_PROFILE_DEMO,可以使用函数WlanGetProfile进行打印,或者访问
配置文件样本,进行设置https://docs.microsoft.com/zh-cn/windows/win32/nativewifi/wireless-profile-samples
在具体的认证与关联过程中,需要XML配置文件
整体思路如下:粉红色为调用的参数
连接WIFI需要按照目录的函数的顺序进行调用,下面将对以下函数进行解释:
DWORD WlanOpenHandle(
DWORD dwClientVersion,
PVOID pReserved,
PDWORD pdwNegotiatedVersion,
PHANDLE phClientHandle
);
关于函数功能的具体要求,可以访问MSDN,使用WlanOpenHandle函数主要是为了得到hClientHandle,往后的其他函数会需要这个句柄
函数需要四个参数:
①1或者2
值 | 含义 |
---|---|
1 |
适用于Windows XP的Windows XP客户端版本和适用于Windows XP SP2的无线LAN API。 |
2 |
适用于Windows Vista和Windows Server 2008的客户端版本 |
②第二个参数保留供将来使用,为nullptr
③DWORD类型
④HANDLE类型
//HANDLE hClientHandle;//万能指针
DWORD dwError = ERROR_SUCCESS;//原来我DWORD MFC的数据类型,相当于unsigned long
//DWORD dwNegotiatedVersion;
//WlanOpenHandle以下函数功能:打开wifi连接
// qDebug()<<"dwError1:"<
函数会更改 HANDLE hClientHandle的值,这个值在后面的函数中将会被反复调用
DWORD WlanEnumInterfaces(
HANDLE hClientHandle,
PVOID pReserved,
PWLAN_INTERFACE_INFO_LIST *ppInterfaceList
);
函数需要三个参数
①hClientHandle,通过先前调用WlanOpenHandle函数获得。
②第二个参数保留供将来使用,为nullptr
③类成员:PWLAN_INTERFACE_INFO_LIST pInterfaceList;
dwError = WlanEnumInterfaces(hClientHandle, nullptr,&pInterfaceList);
//返回值还是DWRD类型的,成功的话是ERROR_SUCCESS
if ( dwError != ERROR_SUCCESS )
{
qDebug()<<"未发现wifi相关列表!";
WlanFreeMemory(pInterfaceList);//释放列表
//释放内存,从Native Wifi函数返回的任何内存必须释放
WlanCloseHandle(hClientHandle,nullptr);//关闭wlan
return;
}
函数调用成功后,会更改pInterfaceList的指向,后面函数会用到这个指针
DWORD WlanGetAvailableNetworkList(
HANDLE hClientHandle,
const GUID *pInterfaceGuid,
DWORD dwFlags,
PVOID pReserved,
PWLAN_AVAILABLE_NETWORK_LIST *ppAvailableNetworkList
);
函数需要五个参数
①hClientHandle,通过先前调用WlanOpenHandle函数获得。
②全局唯一标识符(globally unique identifier)
指向要查询的无线LAN接口的GUID的指针。
可以使用WlanEnumInterfaces函数确定在本地计算机上启用的每个无线LAN接口的GUID 。
第二个参数从pInterfaceList中获得,而pInterfaceList需要调用WlanEnumInterfaces得到。
GUID &guid = pInterfaceList->InterfaceInfo[0].InterfaceGuid;
③2
④参数保留供将来使用,为nullptr
⑤指向存储的指针,用于接收WLAN_AVAILABLE_NETWORK_LIST结构中返回的可见网络列表。
//GUID全局唯一标识符
GUID &guid = pInterfaceList->InterfaceInfo[0].InterfaceGuid;
//检测可用的网络
PWLAN_AVAILABLE_NETWORK_LIST pWLAN_AVAILABLE_NETWORK_LIST = nullptr;
//获取无线LAN接口上的可用网络列表
// hClientHandle客户端的会话句柄,通过先前调用WlanOpenHandle函数获得。
// pInterfaceGuid指向要查询的无线LAN接口的GUID的指针。
// 可以使用WlanEnumInterfaces函数确定在本地计算机上启用的每个无线LAN接口的GUID 。
// dwFlags 一组控制列表中返回的网络类型的标志。此参数可以是这些可能值的组合。2
// ppAvailableNetworkList指向存储的指针,用于接收WLAN_AVAILABLE_NETWORK_LIST结构中返回的可见网络列表。
dwError = WlanGetAvailableNetworkList(hClientHandle, &guid,2,nullptr, &pWLAN_AVAILABLE_NETWORK_LIST);
if (dwError != ERROR_SUCCESS)
{
qDebug()<<"未发现可用的网络!";
WlanFreeMemory(pInterfaceList);
WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);
WlanCloseHandle(hClientHandle,nullptr);
QPixmap pixmapWireless(":/icons/WirelessIcon4.png");
labelForWIFI->setPixmap(pixmapWireless);
qTextBrowerNumberAvailWIFI->setText("0");
return;
}
具体的wifi列表打印部分
WLAN_AVAILABLE_NETWORK wlanAN;
//结构包含可用的无线网络信息
bool isConnected=false;
//访问的是_WLAN_AUTH_CIPHER_PAIR_LIST结构体中的成员,表示可用网络的数量
DWORD numberOfItems = pWLAN_AVAILABLE_NETWORK_LIST->dwNumberOfItems;
//储存可用网络的个数
qDebug()<<"numberOf——AVAILABLE_NETWORK——Items:"<setText(NumberAvailWIFI);
if (numberOfItems > 0)//遍历所有的
{
for(DWORD i = 0; i Network[i];
char *p;
QString str1;
//下面这段才是最精髓的
if (wlanAN.dot11Ssid.uSSIDLength != 0)
{
p = (char*) wlanAN.dot11Ssid.ucSSID;//
str1.append(p);
// qDebug()<<"str1:"<setText(str1); //设置列表项的文本
listWidgetAvailWIFI->addItem(ListitemJustForNOW); //加载列表项到列表框
//下面这段才是最精髓的
if(wlanAN.dwFlags & 1)
{
isConnected=true;
int wifiQuality=(int)wlanAN.wlanSignalQuality;//返回信号的强度
if(wifiQuality>75)
{
QPixmap pixmapWireless("://icons/WirelessIcon.png");
labelForWIFI->setPixmap(pixmapWireless);
}
else if(wifiQuality>50&&wifiQuality<=75)
{
QPixmap pixmapWireless("://icons/WirelessIcon1.png");
labelForWIFI->setPixmap(pixmapWireless);
}
else if(wifiQuality>25&&wifiQuality<=50)
{
QPixmap pixmapWireless("://icons/WirelessIcon2.png");
labelForWIFI->setPixmap(pixmapWireless);
}
else if(wifiQuality>0&&wifiQuality<=25)
{
QPixmap pixmapWireless("://icons/WirelessIcon3.png");
labelForWIFI->setPixmap(pixmapWireless);
}
}
}
}
以下代码是修改配置文件的内容,将WIFI的名称与密码进行配置,
之后调用WlanSetProfile函数,需要八个参数:
①hClientHandle,通过先前调用WlanOpenHandle函数获得。
②全局唯一标识符(globally unique identifier)
指向要查询的无线LAN接口的GUID的指针。
可以使用WlanEnumInterfaces函数确定在本地计算机上启用的每个无线LAN接口的GUID 。
第二个参数从pInterfaceList中获得,而pInterfaceList需要调用WlanEnumInterfaces得到。
GUID &guid = pInterfaceList->InterfaceInfo[0].InterfaceGuid;
③
要在配置文件上设置的标志。
带有SP3的Windows XP和用于Windows XP SP2的无线LAN API: dwFlags必须为0.不支持每用户配置文件。
值 | 含义 |
---|---|
0 |
该配置文件是一个全用户配置文件。 |
WLAN_PROFILE_GROUP_POLICY 00000001 |
配置文件是组策略配置文件。 |
WLAN_PROFILE_USER 0x00000002 |
该配置文件是每用户配置文件。 |
本程序中为0
④配置文件
调用静态变量
static const QString STR_PROFILE_DEMO =
" \
\
Home \
\
\
Home \
\
\
ESS \
auto \
\
\
\
WPA2PSK \
AES \
false \
\
\
passPhrase \
false \
AAAAAA \
\
\
\
";
以上配置文件可以使用函数WlanGetProfile进行打印,或者访问
配置文件样本,进行设置https://docs.microsoft.com/zh-cn/windows/win32/nativewifi/wireless-profile-samples
⑤如果为新的所有用户配置文件将此参数设置为NULL
⑥指定此配置文件是否覆盖现有配置文件。如果此参数为FALSE且配置文件已存在,则不会覆盖现有配置文件,并将返回错误。
⑦参数保留供将来使用,为nullptr
⑧一个WLAN_REASON_CODE值
// 先决条件
if (NameForWIFI.isEmpty())
{
return;
}
wchar_t profile[1024] = {};
// 生成profile这个是关键,有了这个,全部都能解决
QString strProfile = STR_PROFILE_DEMO;
QRegExp regName(".* ");
regName.setMinimal(true);
strProfile.replace(regName, QString("%1 ").arg(NameForWIFI));
QRegExp regPassword(".* ");
regPassword.setMinimal(true);
strProfile.replace(regPassword, QString("%1 ").arg(PassNumber));
strProfile.toWCharArray(profile);
qDebug()<<"strProfile:"<InterfaceInfo[0].InterfaceGuid,
0,
profile,
nullptr,
TRUE,
nullptr,
&dwReasonCode
);
// 密码错误返回 ERROR_BAD_PROFILE
if (dwResult == ERROR_SUCCESS)
{
qDebug()<<"设置成功";
}
else if(dwResult == ERROR_BAD_PROFILE)
{
qDebug()<<"设置失败";
}
这是WIFI连接的关键一步,需要四个参数:
①hClientHandle,通过先前调用WlanOpenHandle函数获得。
②全局唯一标识符(globally unique identifier)
指向要查询的无线LAN接口的GUID的指针。
可以使用WlanEnumInterfaces函数确定在本地计算机上启用的每个无线LAN接口的GUID 。
第二个参数从pInterfaceList中获得,而pInterfaceList需要调用WlanEnumInterfaces得到。
GUID &guid = pInterfaceList->InterfaceInfo[0].InterfaceGuid;
③&wlanConnPara ,WLAN_CONNECTION_PARAMETERS结构体,需要对内容进行赋值
④NULL第四个参数为保留内容,直接设置成NULL即可
//密码连接
//具体参数的配置
wchar_t wifi[MAX_PATH] = {};
NameForWIFI.toWCharArray(wifi);
qDebug()<<"ConnectWIFI_wifi:"<InterfaceInfo[0].InterfaceGuid,&wlanConnPara ,NULL);
if (dwResult==ERROR_SUCCESS)
{
qDebug()<<"连接成功!";
}
else
{
qDebug()<<"连接失败:错误信息为:"<
void Widget::closeEvent(QCloseEvent *event)
{
int ret = QMessageBox::question(this,"close","Are you sure?",QMessageBox::Yes,QMessageBox::No);
if(ret == QMessageBox::Yes)
{
WlanFreeMemory(pInterfaceList);
// WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);
WlanCloseHandle(hClientHandle,nullptr);
event->accept();//接受
}
else if(ret == QMessageBox::No)
{
qDebug()<<"继续";
event->ignore();//忽视
}
}
部分代码:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent)
// ui(new Ui::Widget)
{
// ui->setupUi(this);
labelForWIFI = new QLabel(this);//wifi显示
labelForBattery = new QLabel(this);//battery显示
labelForNumberAvailWIFI = new QLabel("可用WIFI数量:",this);
FreshPushButton = new QPushButton("刷新",this);//刷新界面
qTextBrowerNumberAvailWIFI = new QTextBrowser(this);//显示可以连接的wifi数量
listWidgetAvailWIFI = new QListWidget(this);
Listitem = new QListWidgetItem(listWidgetAvailWIFI);//注意父对象
Pass = new PassNumberDialog(this);//输入密码的窗口
connect(listWidgetAvailWIFI,&QListWidget::itemDoubleClicked,this,&Widget::OpenThePassDialog);
connect(Pass,&PassNumberDialog::GiveWidgetPassWord,this,&Widget::GetPassWord);
connect(FreshPushButton,&QPushButton::clicked,this,&Widget::slotTimeout);
hClientHandle = nullptr;//万能指针
pInterfaceList = nullptr;
UisetForWifi();
// timer=new QTimer(this);
// connect(timer,SIGNAL(timeout()),this,SLOT(slotTimeout()));
// timer->start(3000);
showBattery();
showWIFI();
}
Widget::~Widget()
{
// delete ui;
}
void Widget::UisetForWifi()
{
this->setFixedSize(400,600);
labelForWIFI->setGeometry(280,520,100,100);
labelForBattery->setGeometry(320,520,100,100);
FreshPushButton->setGeometry(40,500,80,30);
QFont FontForQTextBrower;
FontForQTextBrower.setFamily("Segoe UI");
FontForQTextBrower.setBold(true);
FontForQTextBrower.setPixelSize(14);
labelForNumberAvailWIFI->setGeometry(40,30,100,30);
labelForNumberAvailWIFI->setFont(FontForQTextBrower);
qTextBrowerNumberAvailWIFI->setGeometry(140,30,100,30);
qTextBrowerNumberAvailWIFI->setFont(FontForQTextBrower);
listWidgetAvailWIFI->setGeometry(40,80,200,400);
}
void Widget::slotTimeout()
{
listWidgetAvailWIFI->clear();
showBattery();
showWIFI();
}
void Widget::showBattery()
{
SYSTEM_POWER_STATUS systemPowerSatus;
GetSystemPowerStatus(&systemPowerSatus);
int remaindPower=(int)systemPowerSatus.BatteryLifePercent;
if(remaindPower>75)
{
QPixmap pixmapBattery("://icons/BatteryIcon.png");
labelForBattery->setPixmap(pixmapBattery);
}
else if(remaindPower>50&&remaindPower<=75)
{
QPixmap pixmapBattery("://icons/BatteryIcon1.png");
labelForBattery->setPixmap(pixmapBattery);
}
else if(remaindPower>25&&remaindPower<=50)
{
QPixmap pixmapBattery("://icons/BatteryIcon2.png");
labelForBattery->setPixmap(pixmapBattery);
}
else if(remaindPower>0&&remaindPower<=25)
{
QPixmap pixmapBattery("://icons/BatteryIcon3.png");
labelForBattery->setPixmap(pixmapBattery);
}
else
{
QPixmap pixmapBattery("://icons/BatteryIcon4.png");
labelForBattery->setPixmap(pixmapBattery);
}
}
//一下内容均使用了win 32api,所以有大量的句柄是MFC的
//还是保留其中的MFC句柄,这样在window官网上查找相关的句柄,函数返回值也知道该怎么做
void Widget::showWIFI()
{
DWORD dwError = ERROR_SUCCESS;//原来我DWORD MFC的数据类型,相当于unsigned long
DWORD dwNegotiatedVersion;
//WlanOpenHandle以下函数功能:打开wifi连接
// qDebug()<<"dwError1:"<InterfaceInfo[0].InterfaceGuid;
//检测可用的网络
PWLAN_AVAILABLE_NETWORK_LIST pWLAN_AVAILABLE_NETWORK_LIST = nullptr;
//获取无线LAN接口上的可用网络列表
// hClientHandle客户端的会话句柄,通过先前调用WlanOpenHandle函数获得。
// pInterfaceGuid指向要查询的无线LAN接口的GUID的指针。
// 可以使用WlanEnumInterfaces函数确定在本地计算机上启用的每个无线LAN接口的GUID 。
// dwFlags 一组控制列表中返回的网络类型的标志。此参数可以是这些可能值的组合。2
// ppAvailableNetworkList指向存储的指针,用于接收WLAN_AVAILABLE_NETWORK_LIST结构中返回的可见网络列表。
dwError = WlanGetAvailableNetworkList(hClientHandle, &guid,2,nullptr, &pWLAN_AVAILABLE_NETWORK_LIST);
if (dwError != ERROR_SUCCESS)
{
qDebug()<<"未发现可用的网络!";
WlanFreeMemory(pInterfaceList);
WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);
WlanCloseHandle(hClientHandle,nullptr);
QPixmap pixmapWireless(":/icons/WirelessIcon4.png");
labelForWIFI->setPixmap(pixmapWireless);
qTextBrowerNumberAvailWIFI->setText("0");
return;
}
WLAN_AVAILABLE_NETWORK wlanAN;
//结构包含可用的无线网络信息
bool isConnected=false;
//访问的是_WLAN_AUTH_CIPHER_PAIR_LIST结构体中的成员,表示可用网络的数量
DWORD numberOfItems = pWLAN_AVAILABLE_NETWORK_LIST->dwNumberOfItems;
//储存可用网络的个数
qDebug()<<"numberOf——AVAILABLE_NETWORK——Items:"<setText(NumberAvailWIFI);
if (numberOfItems > 0)//遍历所有的
{
for(DWORD i = 0; i Network[i];
char *p;
QString str1;
//下面这段才是最精髓的
if (wlanAN.dot11Ssid.uSSIDLength != 0)
{
p = (char*) wlanAN.dot11Ssid.ucSSID;//
str1.append(p);
// qDebug()<<"str1:"<setText(str1); //设置列表项的文本
listWidgetAvailWIFI->addItem(ListitemJustForNOW); //加载列表项到列表框
//下面这段才是最精髓的
if(wlanAN.dwFlags & 1)
{
isConnected=true;
int wifiQuality=(int)wlanAN.wlanSignalQuality;//返回信号的强度
if(wifiQuality>75)
{
QPixmap pixmapWireless("://icons/WirelessIcon.png");
labelForWIFI->setPixmap(pixmapWireless);
}
else if(wifiQuality>50&&wifiQuality<=75)
{
QPixmap pixmapWireless("://icons/WirelessIcon1.png");
labelForWIFI->setPixmap(pixmapWireless);
}
else if(wifiQuality>25&&wifiQuality<=50)
{
QPixmap pixmapWireless("://icons/WirelessIcon2.png");
labelForWIFI->setPixmap(pixmapWireless);
}
else if(wifiQuality>0&&wifiQuality<=25)
{
QPixmap pixmapWireless("://icons/WirelessIcon3.png");
labelForWIFI->setPixmap(pixmapWireless);
}
}
}
}
//释放工作
// WlanFreeMemory(pWLAN_AVAILABLE_NETWORK_LIST);
}
void Widget::OpenThePassDialog(QListWidgetItem *item)
{
NameForWIFI = item->text();
qDebug()<<"选择WIFI为:"<text();
Pass->exec();//打开输入密码的窗口
}
void Widget::wlanSetProfile()
{
// 先决条件
if (NameForWIFI.isEmpty())
{
return;
}
wchar_t profile[1024] = {};
// 生成profile这个是关键,有了这个,全部都能解决
QString strProfile = STR_PROFILE_DEMO;
QRegExp regName(".* ");
regName.setMinimal(true);
strProfile.replace(regName, QString("%1 ").arg(NameForWIFI));
QRegExp regPassword(".* ");
regPassword.setMinimal(true);
strProfile.replace(regPassword, QString("%1 ").arg(PassNumber));
strProfile.toWCharArray(profile);
qDebug()<<"strProfile:"<InterfaceInfo[0].InterfaceGuid,
0,
profile,
NULL,
TRUE,
NULL,
&dwReasonCode
);
// 密码错误返回 ERROR_BAD_PROFILE
if (dwResult == ERROR_SUCCESS)
{
qDebug()<<"设置成功";
}
else if(dwResult == ERROR_BAD_PROFILE)
{
qDebug()<<"设置失败";
}
}
void Widget::ConnectWIFI()
{
//密码连接
//具体参数的配置
wchar_t wifi[MAX_PATH] = {};
NameForWIFI.toWCharArray(wifi);
qDebug()<<"ConnectWIFI_wifi:"<InterfaceInfo[0].InterfaceGuid,&wlanConnPara ,NULL);
if (dwResult==ERROR_SUCCESS)
{
qDebug()<<"连接成功!";
}
else
{
qDebug()<<"连接失败:错误信息为:"<accept();//接受
}
else if(ret == QMessageBox::No)
{
qDebug()<<"继续";
event->ignore();//忽视
}
}
界面展示:
源文件下载地址:https://download.csdn.net/download/qq_41605114/11716245