DT
April 14, 2025, 3:38 p.m.

Qt 6.8 MinGW - управление подключением WiFi из программы

C++, MinGW, windows, WiFi

Всем привет!
На Qt 6.8 MinGW пытаюсь сделать управление подключением WiFi из программы. Пока делаю поддержку Windows, но так же хочу в дальнейшем внедрить и поддержку Linux/MacOS.

Для управления пробрасываю команды в netsh и разбираю результат.
Пока загвоздка в разных локализациях системы, из-за чего или некорректно отображаются названия на русском языке (одни ??????????????), либо ничего не выдается из-за того, что не удается разобрать результат. Хотел бы библиотеку унифицировать.

Есть идеи?

Пока выглядит следующим образом:

wifimanager.h

#ifndef WIFIMANAGER_H
#define WIFIMANAGER_H

#include <QtNetwork/QtNetwork>
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <list>


/**
 * Open - открытая сеть без шифрования
 * Shared - устаревший режим WEP с общей аутентификацией
 * WPA-Personal - WPA с использованием PSK (Pre-Shared Key)
 * WPA-Enterprise - WPA с использованием RADIUS-сервера для аутентификации
 * WPA2-Personal - WPA2 с PSK (наиболее распространенный сейчас режим)
 * WPA2-Enterprise - WPA2 с RADIUS-сервером
 * WPA3-Personal - современный WPA3 с PSK
 * WPA3-Enterprise - WPA3 с RADIUS-сервером
 */
typedef enum {
    AUTH_TYPE_UNKNOWN = 0,
    AUTH_TYPE_OPEN,
    AUTH_TYPE_SHARED,
    AUTH_TYPE_WPA_PERSONAL,
    AUTH_TYPE_WPA_ENTERPRISE,
    AUTH_TYPE_WPA2_PERSONAL,
    AUTH_TYPE_WPA2_ENTERPRISE,
    AUTH_TYPE_WPA3_PERSONAL,
    AUTH_TYPE_WPA3_ENTERPRISE,
    AUTH_TYPE_MAX
} auth_type_t;

typedef enum {
    WIFI_BAND_UNKNOWN = 0,
    WIFI_BAND_2_4_GHZ,
    WIFI_BAND_5_GHZ,
    WIFI_BAND_MAX
} wifi_band_t;

typedef enum {
    WIFI_STATE_DISCONNECTED = 0,
    WIFI_STATE_CONNECTED
} wifi_state_t;

typedef enum {
    INTERFACE_TYPE_PRIMARY = 0,
    INTERFACE_TYPE_SECONDARY
} interface_type_t;

/**
 * Name                   : Беспроводная сеть
 * Description            : Intel(R) Wireless-AC 9560 160MHz
 * GUID                   : 082bf832-bae3-4933-a678-16e21e1dfb75
 * Physical address       : 5c:87:9c:94:c2:76
 * Interface type         : Primary
 * State                  : connected
 * SSID                   : tp-link
 * AP BSSID               : 78:8c:b5:b7:27:a6
 * Band                   : 5 GHz
 * Channel                : 48
 * Network type           : Infrastructure
 * Radio type             : 802.11ac
 * Authentication         : WPA2-Personal
 * Cipher                 : CCMP
 * Connection mode        : Profile
 * Receive rate (Mbps)    : 780
 * Transmit rate (Mbps)   : 780
 * Signal                 : 87%
 * Profile                : tp-link
 */
typedef struct {
    QString interfaceName;
    QString interfaceDesc;
    QString interfaceMAC;
    interface_type_t interfaceType;
    wifi_state_t wifiConnected;
    auth_type_t authType;
    int signal;
    QString currentProfileUsed;
    QString wifiSSIDConnected;
    wifi_band_t currentBand;
} interface_desc_t;

typedef struct {
    QString wifiSSID;
    QString wifiBSSID;
    int signal;
    wifi_band_t band;
} wifi_network_t;

inline QString wifiBandToStr(wifi_band_t band) {
    switch (band) {
        case WIFI_BAND_2_4_GHZ:
            return "2.4 GHz";
        case WIFI_BAND_5_GHZ:
            return "5 GHz";
        default:
            return "Unknown";
    }
}

class WiFiManager
{
    public:
        WiFiManager();
        ~WiFiManager();
        QList<wifi_network_t> scanNetworks(interface_desc_t wifi_interface);
        QList<wifi_network_t> getLastScannedNetworks() { return wifiNetworks; }
        interface_desc_t getLastScannedWifiInterface() { return wifiScannedInterface; }

        QList<interface_desc_t> getWifiInterfaces();
        QList<interface_desc_t> getLastCheckedWifiInterfaces() { return interfacesDescriptions; }

    private:
        QList<interface_desc_t> interfacesDescriptions;
        QList<wifi_network_t> wifiNetworks;
        interface_desc_t wifiScannedInterface;

        QString tempFolderPath;
};

#endif // WIFIMANAGER_H

wifimanager.cpp

#include "wifimanager.h"


WiFiManager::WiFiManager() {

}

WiFiManager::~WiFiManager() {
    interfacesDescriptions.clear();
}

QList<interface_desc_t> WiFiManager::getWifiInterfaces() {
    QProcess process;

    qDebug() << "WiFi interfaces getting...";

    // Выполняем команду netsh
    QString cmd = QString("cmd /U /C chcp 437 > nul && netsh wlan show interfaces");
    // process.start("netsh", QStringList() << "wlan" << "show" << "interfaces");
    process.startCommand(cmd);
    process.waitForFinished();

    QString output = QString::fromLocal8Bit(process.readAllStandardOutput());
    QStringList lines = output.split("\n");

    interface_desc_t currentInterface;
    bool parsingInterface = false;

    // Регулярные выражения для разбора
    QRegularExpression nameRe("^Name\\s*:\\s*(.+)$");
    QRegularExpression descRe("^Description\\s*:\\s*(.+)$");
    QRegularExpression macRe("^Physical address\\s*:\\s*([0-9a-fA-F:]+)$");
    QRegularExpression typeRe("^Interface type\\s*:\\s*(.+)$");
    QRegularExpression stateRe("^State\\s*:\\s*(.+)$");
    QRegularExpression ssidRe("^SSID\\s*:\\s*(.+)$");
    QRegularExpression bandRe("^Band\\s*:\\s*(.+)$");
    QRegularExpression authRe("^Authentication\\s*:\\s*(.+)$");
    QRegularExpression signalRe("^Signal\\s*:\\s*(\\d+)%$");
    QRegularExpression profileRe("^Profile\\s*:\\s*(.+)$");

    for (const QString& line : lines) {
        QString trimmedLine = line.trimmed();
        qDebug() << "Line to be parsed: " << line << ", trimmed: " << trimmedLine;
        if (trimmedLine.isEmpty()) {
            if (parsingInterface && currentInterface.interfaceName.length() > 0) {
                qDebug() << "Interface name to be added: " + currentInterface.interfaceName;
                interfacesDescriptions.append(currentInterface);
                parsingInterface = false;
            }
            continue;
        }


        QRegularExpressionMatch match;

        if (!parsingInterface) {
            currentInterface = interface_desc_t();
            currentInterface.currentBand = WIFI_BAND_UNKNOWN;
            currentInterface.signal = 0;
            currentInterface.authType = AUTH_TYPE_UNKNOWN;
            parsingInterface = true;
            qDebug() << "Starting new interface parsing";
        }

        // Парсинг полей
        if ((match = nameRe.match(trimmedLine)).hasMatch()) {
            currentInterface.interfaceName = match.captured(1);
            qDebug() << "Interface name parsed: " << currentInterface.interfaceName;
            // parsingInterface = true;
            // continue;
        }
        else if ((match = descRe.match(trimmedLine)).hasMatch()) {
            currentInterface.interfaceDesc = match.captured(1);
            qDebug() << "Interface desc parsed: " << currentInterface.interfaceDesc;
            // parsingInterface = true;
            // continue;
        }
        else if ((match = macRe.match(trimmedLine)).hasMatch()) {
            currentInterface.interfaceMAC = match.captured(1);
            qDebug() << "Interface MAC parsed: " << currentInterface.interfaceMAC;
            // parsingInterface = true;
            // continue;
        }
        else if ((match = typeRe.match(trimmedLine)).hasMatch()) {
            qDebug() << "interface type parsed: " << match.captured(1);
            currentInterface.interfaceType = match.captured(1) == "Primary" ?
                                                 INTERFACE_TYPE_PRIMARY : INTERFACE_TYPE_SECONDARY;
            // parsingInterface = true;
            // continue;
        }
        else if ((match = stateRe.match(trimmedLine)).hasMatch()) {
            qDebug() << "state parsed: " << match.captured(1);
            currentInterface.wifiConnected = match.captured(1) == "connected" ?
                                                 WIFI_STATE_CONNECTED : WIFI_STATE_DISCONNECTED;
            // parsingInterface = true;
            // continue;
        }
        else if ((match = ssidRe.match(trimmedLine)).hasMatch()) {
            qDebug() << "current SSID parsed: " << match.captured(1);
            currentInterface.wifiSSIDConnected = match.captured(1);
            // parsingInterface = true;
            // continue;
        }
        else if ((match = bandRe.match(trimmedLine)).hasMatch()) {
            QString band = match.captured(1);
            qDebug() << "band parsed: " << band;
            if (band == "2.4 GHz") {
                currentInterface.currentBand = WIFI_BAND_2_4_GHZ;
            } else if (band == "5 GHz") {
                currentInterface.currentBand = WIFI_BAND_5_GHZ;
            } else {
                currentInterface.currentBand = WIFI_BAND_UNKNOWN;
            }
            // parsingInterface = true;
            // continue;
        }
        else if ((match = authRe.match(trimmedLine)).hasMatch()) {
            QString auth = match.captured(1);
            qDebug() << "auth type parsed: " << auth;
            if (auth == "Open") currentInterface.authType = AUTH_TYPE_OPEN;
            else if (auth == "Shared") currentInterface.authType = AUTH_TYPE_SHARED;
            else if (auth == "WPA-Personal") currentInterface.authType = AUTH_TYPE_WPA_PERSONAL;
            else if (auth == "WPA-Enterprise") currentInterface.authType = AUTH_TYPE_WPA_ENTERPRISE;
            else if (auth == "WPA2-Personal") currentInterface.authType = AUTH_TYPE_WPA2_PERSONAL;
            else if (auth == "WPA2-Enterprise") currentInterface.authType = AUTH_TYPE_WPA2_ENTERPRISE;
            else if (auth == "WPA3-Personal") currentInterface.authType = AUTH_TYPE_WPA3_PERSONAL;
            else if (auth == "WPA3-Enterprise") currentInterface.authType = AUTH_TYPE_WPA3_ENTERPRISE;
            else currentInterface.authType = AUTH_TYPE_UNKNOWN;

            // parsingInterface = true;
            // continue;
        }
        else if ((match = signalRe.match(trimmedLine)).hasMatch()) {
            qDebug() << "current signal parsed: " << match.captured(1);
            currentInterface.signal = match.captured(1).toInt();
            // parsingInterface = true;
            // continue;
        }
        else if ((match = profileRe.match(trimmedLine)).hasMatch()) {
            qDebug() << "profile used parsed: " << match.captured(1);
            currentInterface.currentProfileUsed = match.captured(1);
            // parsingInterface = true;
            // continue;
        }


    }

    // Добавляем последний интерфейс, если он есть
    if (parsingInterface && currentInterface.interfaceName.length() > 0) {
        qDebug() << "Last interface name to be added: " + currentInterface.interfaceName;
        interfacesDescriptions.append(currentInterface);
        parsingInterface = false;
    }

    return interfacesDescriptions;
}

QList<wifi_network_t> WiFiManager::scanNetworks(interface_desc_t wifi_interface) {
    QProcess process;

    wifiScannedInterface = wifi_interface;

    wifiNetworks.clear();

    // process.start("netsh", {"wlan", "show", "networks", "mode=bssid"});
    QString cmd = QString("cmd /U /C chcp 437 > nul && netsh wlan show networks mode=bssid interface=\"%1\"").arg(wifiScannedInterface.interfaceName);
    process.startCommand(cmd);
    process.waitForFinished();

    QString output = process.readAllStandardOutput();
    QTextStream stream(&output);

    // Регулярные выражения для парсинга
    QRegularExpression ssidRegex("\\bSSID\\b (\\d+)( +):((.+)|\\n)");
    QRegularExpression bssidRegex("( +)\\bBSSID\\b (\\d+)( +): (.+)");
    QRegularExpression signalRegex("( +)\\bSignal\\b( +): (\\d+)(%)");
    QRegularExpression bandRegex("( +)\\bBand\\b( +): (.+)");

    bool parsingNetwork = false;
    wifi_network_t currentNetwork;

    while (!stream.atEnd()) {
        QString line = stream.readLine();

        // Ищем начало новой сети
        QRegularExpressionMatch ssidMatch = ssidRegex.match(line);
        if (ssidMatch.hasMatch()) {
            QString ssid_to_check = ssidMatch.captured(3);
            currentNetwork.wifiSSID = ssid_to_check;
            qDebug() << "Got SSID: " << currentNetwork.wifiSSID;
            parsingNetwork = true;
            continue;
        }

        // Ищем BSSID
        QRegularExpressionMatch bssidMatch = bssidRegex.match(line);
        if (bssidMatch.hasMatch()) {
            currentNetwork.wifiBSSID = bssidMatch.captured(4);
            qDebug() << "Got BSSID: " << currentNetwork.wifiBSSID;
            parsingNetwork = true;
            continue;
        }

        // Ищем уровень сигнала
        QRegularExpressionMatch signalMatch = signalRegex.match(line);
        if (signalMatch.hasMatch()) {
            qDebug() << "Got signal: " << signalMatch.captured(3);
            currentNetwork.signal = signalMatch.captured(3).toInt();
            parsingNetwork = true;
            continue;
        }

        // Ищем полосу частот
        QRegularExpressionMatch bandMatch = bandRegex.match(line);
        if (bandMatch.hasMatch()) {
            QString bandStr = bandMatch.captured(3);
            qDebug() << "Got band: " << bandStr;
            if (bandStr == "2.4 GHz")
                currentNetwork.band = WIFI_BAND_2_4_GHZ;
            else if (bandStr == "5 GHz")
                currentNetwork.band = WIFI_BAND_5_GHZ;
            else
                currentNetwork.band = WIFI_BAND_UNKNOWN;

            // parsingNetwork = true;
            // Добавляем сеть в список
            if (parsingNetwork) {
                currentNetwork.wifiSSID = currentNetwork.wifiSSID.trimmed();

                qDebug() << "WiFi SSID found: " << currentNetwork.wifiSSID << " (" + currentNetwork.wifiBSSID + ")";
                // Excluding empty wifi ssids
                if (!currentNetwork.wifiSSID.isNull())
                    if (currentNetwork.wifiSSID.length() > 0)
                        wifiNetworks.append(currentNetwork);

                parsingNetwork = false;
            }
            continue;
        }

    }

    return wifiNetworks;
}

1

Do you like it? Share on social networks!

0

Comments

Only authorized users can post comments.
Please, Log in or Sign up
  • Last comments
  • Evgenii Legotckoi
    April 16, 2025, 5:08 p.m.
    Благодарю за отзыв. И вам желаю всяческих успехов!
  • IscanderChe
    April 12, 2025, 5:12 p.m.
    Добрый день. Спасибо Вам за этот проект и отдельно за ответы на форуме, которые мне очень помогли в некоммерческих пет-проектах. Профессиональным программистом я так и не стал, но узнал мно…
  • AK
    April 1, 2025, 11:41 a.m.
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    March 9, 2025, 9:02 p.m.
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    March 9, 2025, 4:14 p.m.
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…