C++で近くのWi-Fiの情報を得る

ChatGPTにWi-Fiの情報をC++で取得するコードを書かせてみました

# AIに命令したこと

・C++でWi-Fiの情報を得る
・電波の強さ、セキュリティレベルを得る

当初はこの二つだけでしたが
何度もプログラムをテストしていくうちに
・名前が日本語のWi-Fiが表示されるとき必ず文字化けする
・同じWi-Fiが何度も現れる
・電波の強さ、セキュリティレベルが数字で出てきて性能が良いのか悪いのかがわかりづらい

といった問題が発生したので

・電波の強さやセキュリティレベルを数字に加え、五段階に評価を分けて日本語で表示する
・名前が日本語のWi-Fiを正しく表示する

さらに追加でこの二つも命令してみました


# includeするものなど

```cpp
#include <iostream>
#include <windows.h>
#include <wlanapi.h>
#include <locale>
#include <codecvt>
#include <set>
#include <vector>
#include <algorithm>

#pragma comment(lib, "wlanapi.lib")
```
# ネットワーク情報を格納する構造体
```cpp
struct NetworkInfo 
{
    std::string ssid;
    DWORD signalQuality;
    DOT11_CIPHER_ALGORITHM defaultCipherAlgorithm;
    DOT11_AUTH_ALGORITHM defaultAuthAlgorithm;
};
```
# セキュリティの評価
```cpp
enum SecurityLevel
{
    VerySecure,
    Secure,
    Moderate,
    Unsafe,
    VeryUnsafe
};
```

# Cipher Algorithmの列挙型
```cpp
 namespace CipherAlgorithms 
{
    enum CipherAlgorithm
    {
        WEP40 = 1,
        TKIP = 2,
        CCMP = 4,
        WEP104 = 5,
        Unknown = 0xFF
    };
}
```
# Cipher Algorithmを評価してセキュリティレベルを返す関数
```cpp
SecurityLevel EvaluateSecurityLevel(DWORD cipherAlgorithm)
{
    switch (cipherAlgorithm) 
    {
        case CipherAlgorithms::WEP40:
            return Unsafe;
        case CipherAlgorithms::TKIP:
            return Moderate;
        case CipherAlgorithms::CCMP:
            return Secure;
        case CipherAlgorithms::WEP104:
            return Unsafe;
        default:
            return VeryUnsafe;
    }
}
```
# セキュリティレベルを文字列に変換する関数
```cpp
std::string ConvertSecurityLevelToJapanese(SecurityLevel securityLevel)
{
    switch (securityLevel)
    {
        case VerySecure:
            return "非常に安全";
        case Secure:
            return "安全";
        case Moderate:
            return "それなり";
        case Unsafe:
            return "危険";
        case VeryUnsafe:
            return "非常に危険";
        default:
            return "不明";
    }
}
```
# 認証アルゴリズムを評価してセキュリティレベルを返す関数
```cpp
SecurityLevel EvaluateAuthAlgorithmSecurity(DOT11_AUTH_ALGORITHM authAlgorithm) 
{
    switch (authAlgorithm) 
    {
        case DOT11_AUTH_ALGO_80211_OPEN: // オープンシステム認証
            return VeryUnsafe;
        case DOT11_AUTH_ALGO_80211_SHARED_KEY: // 共有キー認証
            return Moderate; // 仮の評価です
        case DOT11_AUTH_ALGO_WPA: // WPA認証
            return Moderate; // 仮の評価です
        case DOT11_AUTH_ALGO_WPA_PSK: // WPA-PSK認証
            return Moderate; // 仮の評価です
        case DOT11_AUTH_ALGO_WPA_NONE: // WPAでの認証なし
            return VeryUnsafe; // 仮の評価です
        case DOT11_AUTH_ALGO_RSNA: // RSNA認証
            return Secure; // 仮の評価です
        case DOT11_AUTH_ALGO_RSNA_PSK: // RSNA-PSK認証
            return Secure; // 仮の評価です
        default:
            return VeryUnsafe;
    }
}
```
# セキュリティレベルを文字列に変換する関数
```cpp
std::string ConvertAuthSecurityLevelToJapanese(SecurityLevel securityLevel) 
{
    switch (securityLevel) 
    {
        case VerySecure:
            return "非常に安全";
        case Secure:
            return "安全";
        case Moderate:
            return "それなり";
        case Unsafe:
            return "危険";
        case VeryUnsafe:
            return "非常に危険";
        default:
            return "不明";
    }
}
```
# 電波の強さで比較するための関数オブジェクト
```cpp
 struct CompareBySignalQuality
{
    bool operator()(const NetworkInfo& a, const NetworkInfo& b) const 
    {
        return a.signalQuality > b.signalQuality;
    }
};
```

Signal Qualityの数値を日本語に変換する関数
-
```cpp
std::string ConvertSignalQualityToJapanese(DWORD signalQuality)
{
    if (signalQuality >= 80) 
    {
        return "非常に強い";
    }
    else if (signalQuality >= 60) 
    {
        return "強い";
    }
    else if (signalQuality >= 40)
    {
        return "それなり";
    }
    else if (signalQuality >= 20) 
    {
        return "弱い";
    }
    else 
    {
        return "クソザコ";
    }
}
```



main関数
-
```cpp
int main()
{
    while (true)
    {
        // Wi-Fiハンドルの初期化
        DWORD dwMaxClient = 2; // 最大のクライアント数
        HANDLE hClient = NULL;
        DWORD dwCurVersion = 0;
        DWORD dwResult = 0;

        // バージョンの確認
        dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);
        if (dwResult != ERROR_SUCCESS)
        {
            std::cerr << "WlanOpenHandle がエラー コードで失敗しました: " << dwResult << std::endl;
            return 1;
        }

        // インターフェースのリスト取得
        PWLAN_INTERFACE_INFO_LIST pInterfaceList = NULL;
        dwResult = WlanEnumInterfaces(hClient, NULL, &pInterfaceList);
        if (dwResult != ERROR_SUCCESS)
        {
            std::cerr << "WlanEnumInterfaces がエラー コードで失敗しました: " << dwResult << std::endl;
            WlanCloseHandle(hClient, NULL);
            return 1;
        }

        // スキャンの開始
        dwResult = WlanScan(hClient, &pInterfaceList->InterfaceInfo[0].InterfaceGuid, NULL, NULL, NULL);
        if (dwResult != ERROR_SUCCESS)
        {
            std::cerr << "WlanScan がエラー コードで失敗しました: " << dwResult << std::endl;
            WlanFreeMemory(pInterfaceList);
            WlanCloseHandle(hClient, NULL);
            return 1;
        }

        // スキャン結果の取得
        PWLAN_AVAILABLE_NETWORK_LIST pNetworkList = NULL;
        dwResult = WlanGetAvailableNetworkList(hClient, &pInterfaceList->InterfaceInfo[0].InterfaceGuid, 0, NULL, &pNetworkList);
        if (dwResult != ERROR_SUCCESS)
        {
            std::cerr << "WlanGetAvailableNetworkList がエラー コードで失敗しました:" << dwResult << std::endl;
            WlanFreeMemory(pInterfaceList);
            WlanCloseHandle(hClient, NULL);
            return 1;
        }

        // すでに表示したSSIDを管理するためのset
        std::set<std::string> displayedSSIDs;

      
        // 表示した回数
        int count = 0;


        // ネットワーク情報を格納するvector
        std::vector<NetworkInfo> networkInfos;


        // スキャン結果の処理
        for (DWORD i = 0; i < pNetworkList->dwNumberOfItems; i++) {
            WLAN_AVAILABLE_NETWORK network = pNetworkList->Network[i];

            std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
            std::string ssid = (network.dot11Ssid.uSSIDLength == 0) ? "非公開のネットワーク" : converter.to_bytes(std::wstring(network.dot11Ssid.ucSSID, network.dot11Ssid.ucSSID + network.dot11Ssid.uSSIDLength));

            // すでに表示したSSIDの場合はスキップ
            if (displayedSSIDs.find(ssid) == displayedSSIDs.end())
            {
                // ネットワーク情報をvectorに追加
                networkInfos.push_back({ ssid, network.wlanSignalQuality, network.dot11DefaultCipherAlgorithm, network.dot11DefaultAuthAlgorithm });

                // すでに表示したSSIDをsetに追加
                displayedSSIDs.insert(ssid);

            }
        }

        // 電波の強さでソート
        std::sort(networkInfos.begin(), networkInfos.end(), CompareBySignalQuality());

        // ソート後の情報を表示
        for (const auto& networkInfo : networkInfos) 
        {
            std::cout << "SSID: " << networkInfo.ssid << std::endl;
            std::cout << "Signal Quality: " << networkInfo.signalQuality << " (" << ConvertSignalQualityToJapanese(networkInfo.signalQuality) << ")" << std::endl;

           
            // 暗号アルゴリズムのセキュリティレベルを表示
            SecurityLevel cipherSecurityLevel = EvaluateSecurityLevel(networkInfo.defaultCipherAlgorithm);
            std::cout << "暗号のセキュリティレベル: " << ConvertSecurityLevelToJapanese(cipherSecurityLevel) << std::endl;

            // 認証アルゴリズムのセキュリティレベルを表示
            SecurityLevel authSecurityLevel = EvaluateAuthAlgorithmSecurity(networkInfo.defaultAuthAlgorithm);
            std::cout << "認証のセキュリティレベル: " << ConvertAuthSecurityLevelToJapanese(authSecurityLevel) << std::endl;

            std::cout << "------------------------" << std::endl;
            count++;
        }
        // 周辺のWi-Fiネットワークの情報を表示
        std::cout << "検出したWi-Fiの件数: " << count << "件" << std::endl;
        std::cout << "------------------------" << std::endl;

        //改行しまくり
        std::cout << std::endl;
        std::cout << std::endl;
        std::cout << std::endl;


        // メモリの解放とハンドルのクローズ
        WlanFreeMemory(pInterfaceList);
        WlanCloseHandle(hClient, NULL);

        // 一時停止（ユーザーが画面を確認するため）
        system("pause");
    }
   

    return 0;
}
```

実行結果の例
-

```text
SSID: Wi-Fiの名前
Signal Quality: 20 (弱い)
暗号のセキュリティレベル: 安全
認証のセキュリティレベル: 安全
------------------------
SSID: Wi-Fiの名前
Signal Quality: 18 (クソザコ)
暗号のセキュリティレベル: 安全
認証のセキュリティレベル: 非常に危険
------------------------
検出したWi-Fiの件数: 2件
------------------------
```
:::note warn
稀に名前の部分が文字化けする場合があります
:::

---

# 動作環境
Microsoft Visual C++ 2022　Version 17.8.3での動作は確認できました

:::note alert
Windows以外の環境では動作しませんのでご注意ください
もしWindows以外の環境でやってみたい場合は
コードをChatGPTに貼って問い詰めてみてください
:::

# わかったこと
C++で周りのWi-Fiの名前・電波の強さ・暗号と認証のセキュリティレベルを取得できる

# ひとこと
これを応用すれば
周りのWi-Fiの性能によって手に入るモンスターの種類、ステータスが違うっていうゲームが作れそうな気がする