You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C#调用WlanAPI时如何区分SSID相似的WiFi网络?

嘿,我来帮你搞定相似SSID的WiFi网络区分问题!不管是从通用逻辑还是C#代码实现,我都给你梳理得明明白白。

通用区分思路(非代码层面)

首先,不用代码的话,你得抓住这些核心点来区分两个SSID相似的网络:

  • 唯一标识符是关键:每个已保存的WiFi配置文件在系统里都有专属的唯一标识——要么是系统自动生成的配置文件名(当SSID重复时,系统会自动加后缀,比如MyWiFi和MyWiFi 2),要么是底层的GUID,这俩绝对不会重复,是最可靠的区分依据。
  • 查额外属性:比如网络的频段(2.4G/5G)、AP的物理地址(MAC)、加密类型(WPA2/WPA3)、连接历史,这些属性大概率不一样,能帮用户快速区分哪个是家里的、哪个是公司的。
  • 让用户自定义标签:在你的工具里加个功能,允许用户给每个相似SSID的网络加备注,比如“客厅5G”“卧室2.4G”,展示时直接显示备注,体验感拉满。
C#代码实现步骤

接下来是具体的C#调用WlanAPI的方法,核心是获取每个WiFi配置文件的唯一标识和详细属性:

1. 准备工作:声明API和结构体

首先需要用DllImport引入wlanapi.dll里的关键函数,同时定义对应的结构体(因为WlanAPI是C++写的,需要在C#里映射这些结构):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Xml;

// 函数声明
[DllImport("wlanapi.dll", SetLastError = true)]
private static extern uint WlanOpenHandle(uint dwClientVersion, IntPtr pReserved, out uint pdwNegotiatedVersion, out IntPtr phClientHandle);

[DllImport("wlanapi.dll", SetLastError = true)]
private static extern uint WlanEnumInterfaces(IntPtr hClientHandle, IntPtr pReserved, out IntPtr ppInterfaceList);

[DllImport("wlanapi.dll", SetLastError = true)]
private static extern uint WlanGetProfileList(IntPtr hClientHandle, ref Guid pInterfaceGuid, IntPtr pReserved, out IntPtr ppProfileList);

[DllImport("wlanapi.dll", SetLastError = true)]
private static extern uint WlanGetProfile(IntPtr hClientHandle, ref Guid pInterfaceGuid, string strProfileName, IntPtr pReserved, out IntPtr ppProfileXml, out WlanProfileFlags pdwFlags, IntPtr pReserved2);

[DllImport("wlanapi.dll", SetLastError = true)]
private static extern uint WlanFreeMemory(IntPtr pMemory);

[DllImport("wlanapi.dll", SetLastError = true)]
private static extern uint WlanCloseHandle(IntPtr hClientHandle, IntPtr pReserved);

// 结构体和枚举
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WlanInterfaceList
{
    public uint dwNumberOfItems;
    public uint dwIndex;
    public IntPtr InterfaceInfo;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WlanInterfaceInfo
{
    public Guid InterfaceGuid;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string strInterfaceDescription;
    public WlanInterfaceState isState;
}

public enum WlanInterfaceState
{
    NotReady, Connected, AdHocNetworkFormed, Disconnecting, Disconnected, Associating, Discovering, Authenticating
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WlanProfileList
{
    public uint dwNumberOfItems;
    public uint dwIndex;
    public IntPtr ProfileInfo;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct WlanProfileInfo
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string strProfileName;
    public WlanProfileInfoFlags dwFlags;
}

[Flags]
public enum WlanProfileInfoFlags
{
    None = 0, User = 1, GroupPolicy = 2, AllUser = 4
}

public class WiFiProfile
{
    public string ProfileName { get; set; } // 系统生成的唯一配置文件名
    public string SSID { get; set; }
    public string AuthenticationType { get; set; }
    public string BandInfo { get; set; } // 频段信息(2.4G/5G)
}

2. 核心逻辑:获取并解析WiFi配置

下面是获取所有已保存WiFi配置的核心代码,会提取每个配置的唯一名称、SSID、加密类型,还能解析频段信息:

public List<WiFiProfile> GetAllSavedWiFiProfiles()
{
    var profiles = new List<WiFiProfile>();
    uint negotiatedVersion;
    IntPtr clientHandle;

    // 打开WLAN操作句柄
    var result = WlanOpenHandle(2, IntPtr.Zero, out negotiatedVersion, out clientHandle);
    if (result != 0)
        throw new Win32Exception((int)result);

    try
    {
        IntPtr interfaceListPtr;
        result = WlanEnumInterfaces(clientHandle, IntPtr.Zero, out interfaceListPtr);
        if (result != 0)
            throw new Win32Exception((int)result);

        var interfaceList = (WlanInterfaceList)Marshal.PtrToStructure(interfaceListPtr, typeof(WlanInterfaceList));
        // 遍历每个无线网卡接口
        for (int i = 0; i < interfaceList.dwNumberOfItems; i++)
        {
            var interfaceInfo = (WlanInterfaceInfo)Marshal.PtrToStructure(
                IntPtr.Add(interfaceList.InterfaceInfo, i * Marshal.SizeOf(typeof(WlanInterfaceInfo))), 
                typeof(WlanInterfaceInfo));

            IntPtr profileListPtr;
            result = WlanGetProfileList(clientHandle, ref interfaceInfo.InterfaceGuid, IntPtr.Zero, out profileListPtr);
            if (result != 0)
                continue;

            var profileList = (WlanProfileList)Marshal.PtrToStructure(profileListPtr, typeof(WlanProfileList));
            // 遍历每个接口下的WiFi配置文件
            for (int j = 0; j < profileList.dwNumberOfItems; j++)
            {
                var profileInfo = (WlanProfileInfo)Marshal.PtrToStructure(
                    IntPtr.Add(profileList.ProfileInfo, j * Marshal.SizeOf(typeof(WlanProfileInfo))), 
                    typeof(WlanProfileInfo));

                // 获取配置文件的XML内容
                IntPtr profileXmlPtr;
                WlanProfileFlags flags;
                result = WlanGetProfile(clientHandle, ref interfaceInfo.InterfaceGuid, profileInfo.strProfileName, IntPtr.Zero, out profileXmlPtr, out flags, IntPtr.Zero);
                if (result != 0)
                    continue;

                var profileXml = Marshal.PtrToStringUni(profileXmlPtr);
                WlanFreeMemory(profileXmlPtr); // 释放内存

                // 解析XML提取关键信息
                var doc = new XmlDocument();
                doc.LoadXml(profileXml);
                var nsManager = new XmlNamespaceManager(doc.NameTable);
                nsManager.AddNamespace("wlan", "http://www.microsoft.com/networking/WLAN/profile/v1");

                var ssidNode = doc.SelectSingleNode("//wlan:SSID/wlan:name", nsManager);
                var authNode = doc.SelectSingleNode("//wlan:authentication", nsManager);
                // 尝试解析频段(有些配置可能没有,需要结合可用网络列表补充)
                var bandNode = doc.SelectSingleNode("//wlan:band", nsManager);
                string bandInfo = bandNode?.InnerText switch
                {
                    "2.4GHz" => "2.4G频段",
                    "5GHz" => "5G频段",
                    _ => "未知频段"
                };

                profiles.Add(new WiFiProfile
                {
                    ProfileName = profileInfo.strProfileName,
                    SSID = ssidNode?.InnerText ?? "未知SSID",
                    AuthenticationType = authNode?.InnerText ?? "未知加密",
                    BandInfo = bandInfo
                });
            }
        }
    }
    finally
    {
        // 务必关闭句柄释放资源
        WlanCloseHandle(clientHandle, IntPtr.Zero);
    }

    return profiles;
}

3. 展示时的区分方式

拿到WiFiProfile列表后,展示的时候可以这样处理:

var wifiProfiles = GetAllSavedWiFiProfiles();
foreach (var profile in wifiProfiles)
{
    // 优先显示配置文件名(唯一),再展示SSID和其他属性
    Console.WriteLine($"* {profile.ProfileName} | SSID: {profile.SSID} | {profile.BandInfo} | 加密: {profile.AuthenticationType}");
}

比如输出会是:

* MyWiFi | SSID: MyWiFi | 2.4G频段 | 加密: WPA2PSK
* MyWiFi 2 | SSID: MyWiFi | 5G频段 | 加密: WPA3SAE

这样用户一眼就能区分开两个相同SSID的网络。

额外优化建议
  • 结合可用网络列表:如果需要获取AP的MAC地址,可以调用WlanGetAvailableNetworkList API,拿到当前可扫描到的网络的BSSID(MAC),然后和已保存的配置文件关联起来,区分更精准。
  • 添加用户自定义标签:在你的工具里加个数据库或配置文件,让用户给每个ProfileName绑定自定义备注,展示时替换成用户熟悉的名称。
  • 可视化区分:用不同的图标或颜色标注2.4G/5G网络,或者加密类型,提升用户体验。

内容的提问来源于stack exchange,提问作者RLH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:00:58