65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Windows API 检测强制门户

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2投票s)

2016年3月27日

CPOL

2分钟阅读

viewsIcon

18657

如何通过 Windows NLM_INTERNET_CONNECTIVITY 检测您的网络是否位于强制门户(Wi-Fi 热点)之后

引言

最近,我遇到了检测我的连接是否位于强制门户(Wi-Fi 热点)(请阅读此文档)的需求。我查阅了互联网,发现了关于 Mozilla 如何处理此问题的一些有趣的文章;以及 Safari 和其他可能的替代方案。但最终,并且本文将介绍的方法,我选择了使用 Windows 网络列表管理器 API 参考,并查看了 NLM_INTERNET_CONNECTIVITY 标志 此处

背景

MSDN 提供了一个方便的 API 来查询网络连接属性。通过 NLM_INTERNET_CONNECTIVITY,暴露了一组额外的属性,用于 IPV4 和 IPV6 网络协议;例如 NLM_INTERNET_CONNECTIVITY_WEBHIJACK,这是我们想要找到的属性,以识别我们的网络连接是否位于强制门户之后。

Using the Code

这里的总体策略是使用网络列表管理器枚举已连接的连接。然后,通过 INetwork 接口查询 IPropertyBag,查找所需的 NLM_INTERNET_CONNECTIVITY 标志。正如 MSDN 文档所建议:“可以使用 IPropertyBag 接口查询 INetworkINetworkConnection 接口的 NA_InternetConnectivityV4NA_InternetConnectivityV6 属性来检索这些连接标志。” 我首先尝试使用 INetworkConnection 接口查询 IPropertyBag,但在检索标志时一直失败。因此,我切换到 INetwork 接口(其余代码保持不变),它就可以正常工作了。我没有花时间调查为什么 INetworkConnection 失败。但是,我想指出这一点,以防您遇到类似的情况。

最后,我们查看网络连接标志,检查 NLM_INTERNET_CONNECTIVITY_WEBHIJACK 标志是否已设置。如果此标志设置为 NLM_CONNECTIVITY_IPV6_INTERNETNLM_CONNECTIVITY_IPV4_INTERNET,则我们分别位于 IPV6 或 IPV4 的强制门户连接下。

以下是执行上述逻辑并设置布尔变量以检测是否检测到强制门户的代码片段

bool fCaptivePortalDetected = false;

    // Initialize COM.
    if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
    {
        // Declare a pointer to INetworkListManager
        INetworkListManager* pNetworkListManager;

        // Create instance of the CLSID_NetworkListManger COM object
        if (SUCCEEDED(CoCreateInstance(CLSID_NetworkListManager, NULL,
            CLSCTX_ALL, IID_INetworkListManager,
            (LPVOID*)&pNetworkListManager)))
        {           
            // Declare pointer to an IEnumNetworkConnections
            IEnumNetworks* pEnum;

            // Call to GetNetworks from INetworkListManager interface
            if (SUCCEEDED(pNetworkListManager->GetNetworks
            (NLM_ENUM_NETWORK_CONNECTED, &pEnum)) && pEnum != NULL)
            {
                INetwork *pINetwork;
                HRESULT hr = pEnum->Next(1, &pINetwork, nullptr);
                while (hr == S_OK)
                {
                    if (pINetwork != NULL)
                    {
                        IPropertyBag *pNetworkPropertyBag;
                        HRESULT hrQueryInterface = pINetwork->QueryInterface
                        (IID_IPropertyBag, (LPVOID*)&pNetworkPropertyBag);
                        if (SUCCEEDED(hrQueryInterface 1) && pNetworkPropertyBag != nullptr)
                        {
                            NLM_CONNECTIVITY networkConnectivity;
                            VARIANT variantConnectivity;

                            if (SUCCEEDED(pINetwork->GetConnectivity(&networkConnectivity)))
                            {
                                if ((networkConnectivity & 
                                NLM_CONNECTIVITY_IPV4_INTERNET) == 
                                         NLM_CONNECTIVITY_IPV4_INTERNET)
                                {
                                    VariantInit(&variantConnectivity);
                                    if (SUCCEEDED(pNetworkPropertyBag->Read
                                    (NA_InternetConnectivityV4, 
                                           &variantConnectivity, nullptr)) 
                                    && (V_UINT(&variantConnectivity) & 
                                    NLM_INTERNET_CONNECTIVITY_WEBHIJACK) == 
						            NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
                                    {
                                        fCaptivePortalDetected = true;
                                    }
                                    auto t = V_UINT(&variantConnectivity);
                                    VariantClear(&variantConnectivity);
                                }
                                if (!fCaptivePortalDetected && (networkConnectivity 
                                & NLM_CONNECTIVITY_IPV6_INTERNET) == 
                                                  NLM_CONNECTIVITY_IPV6_INTERNET)
                                {
                                    VariantInit(&variantConnectivity);
                                    if (SUCCEEDED(pNetworkPropertyBag->Read
                                                     (NA_InternetConnectivityV6, 
                                    &variantConnectivity, nullptr)) && 
                                    (V_UINT(&variantConnectivity) & 
                                    NLM_INTERNET_CONNECTIVITY_WEBHIJACK) == 
						            NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
                                    {
                                        fCaptivePortalDetected = true;
                                    }
                                    VariantClear(&variantConnectivity);
                                }
                            }
                        }

                        pINetwork->Release();
                    }

                    if (fCaptivePortalDetected)
                        break;


                    hr = hr = pEnum->Next(1, &pINetwork, nullptr);
                }
            }
        }
    }

    // Uninitialize COM.
    // (This should be called on application shutdown.)
    CoUninitialize();

参考和重要链接

我想说,由于本文使用了 Windows 函数,MSDN 库是您最好的朋友,也是您应该首先检查的信息来源。但同时,以下两篇主要文章帮助我理解了这些 API

历史

  • 2016 年 3 月 27 日:初始版本
© . All rights reserved.