LAN ケーブルは繋がっていますか?(Part.1)

Windows 2000 以降、メディア検出機能というものが実装されています。
この機能は、ネットワークアダプターに LAN ケーブルが接続されているのか、接続されていないのかを検出する…

メディア検出

つまり、タスクトレイにあるネットワークアイコンに表示されているのは、メディア検出機能?

この機能ですが、上記 KB にあるようにレジストリで OFF にすることができます。

HKLM\System\CurrentControlSet\Services\Tcpip\Parameters

ここに、DWORD で DisableDHCPMediaSense というエントリーを作成します。
値を 1 にすると、メディア検出機能を OFF に、0 にすると ON(デフォルト)にすることができます。
(設定変更後は再起動要) もちろん、上記は Vista 以降の OS でも有効な情報です。

では、メディア検出機能を無効にしてから、LAN ケーブルを抜いてみると…
タスクトレイのアイコンにはバツマークが表示され、ケーブルが繋がっていないことがわかります。
タスクトレイのネットワークアイコンは、メディア検出機能 ON/OFF の影響は受けないようです。

実際のところ、メディア検出機能の中心はネットワークアダプターにバインドされている TCP/IP プロトコルレイヤー
自体の動作になっています。メディア検出機能 が ON になっているときに、LAN ケーブルを 抜いてしまうと、
リンク状態は OFF になり、ネットワークアダプターにバインドされている TCP/IP プロトコルレイヤーがアンロードされ、
IP アドレスが 0.0.0.0 になってしまいます。
OFF の状態だと、リンク状態はそのまま、つまり、TCP/IP プロトコルレイヤーがそのままの状態になっているので
IP アドレスもそのままの状態です。

今回は、LAN ケーブルが繋がっているのかどうかをプログラミングで調べる方法を紹介します。
何種類か方法があると思うのですが、今回紹介するのは、次の 2 つの方法です。

  1. WMI の Win32_NetworkAdapter クラスを使う
  2. IP Helper の GetIfTable() を使う

まず、Win32_NetworkAdapter クラスを使い、LAN ケーブルが繋がっているのかを調べる方法です。

まず、このクラスの Name プロパティを使って、アダプター情報だけを取得してみると

Win32_NetworkAdapter_一覧

となり、12 個アダプターがあるという結果に。あわわわ…。
ちなみに、Win32_NetworkAdapter は IPv4 のみの対応です。
実際に知りたいのは、物理アダプターに対するケーブルの接続状態なので PhysicalAdapter プロパティを使い
フィルタリングしました。また、実際に参照するのは、NetConnectionStatus プロパティです。

pEnumObject には、Win32_NetworkAdapter クラスオブジェクトが格納されている

pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
CComVariant var;
CComBSTR strClassProp = L"PhysicalAdapter";
hRes = pClassObject->Get(strClassProp, 0, &var, 0, 0);
if (var.boolVal == VARIANT_TRUE) // 物理アダプターだけにフィルタリング
{
・・・
        strClassProp = L"NetConnectionStatus";
        hRes = pClassObject->Get(strClassProp, 0, &var, 0, 0);
・・・
ここで var.intVal にケーブルの接続状態が格納される

”ケーブルを繋いで実行”、”ケーブルを抜いて実行” の結果は、次の表示になり、判別できたようです。

Win32_NetworkAdapter_結果

参考までに、検証用に書いたコードは以下のようになります。
(COM を使っているので ATL を使用 & エラーチェックは一部省略)

int IsMediaStatus()
{
    HRESULT hRes;
    hRes = CoInitializeSecurity(NULL, -1, NULL, NULL,
                RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE,
                NULL, EOAC_NONE, 0);
    if (SUCCEEDED(hRes))
    {
        CComPtr<IWbemLocator> pIWbemLocator = NULL;
        CComPtr<IWbemServices> pWbemServices = NULL;
        hRes = pIWbemLocator.CoCreateInstance(CLSID_WbemAdministrativeLocator,
            NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER);
        if (SUCCEEDED(hRes))
        {
            CComBSTR bstrNamespace = (L"root\\cimv2");
            hRes = pIWbemLocator->ConnectServer(bstrNamespace,
                        NULL, NULL, NULL, 0, NULL, NULL, &pWbemServices);
            CComBSTR strQuery = L"Select * from Win32_NetworkAdapter";
            CComBSTR strQL = L"WQL";
            CComPtr<IEnumWbemClassObject> pEnumObject = NULL;
            hRes = pWbemServices->ExecQuery(strQL, strQuery,
                WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
                NULL, &pEnumObject);
            ULONG uCount = 1, uReturned;
            while (TRUE)
            {
                CComPtr<IWbemClassObject> pClassObject = NULL;
                hRes = pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
                if (hRes == WBEM_S_NO_ERROR)
                {
                    CComVariant var;
                    CComBSTR strClassProp = L"PhysicalAdapter";
                    hRes = pClassObject->Get(strClassProp, 0, &var, 0, 0);
                    if (var.boolVal == VARIANT_TRUE) // 物理アダプターだけにフィルタリング
                    {
                        strClassProp.Empty();
                        strClassProp = L"Name";
                        hRes = pClassObject->Get(strClassProp, 0, &var, 0, 0);
                        printf("Adapter Name: %ls\n", var.bstrVal);
                        strClassProp.Empty();
                        strClassProp = L"MACAddress";
                        hRes = pClassObject->Get(strClassProp, 0, &var, 0, 0);
                        printf("MAC Address : %ls\n", var.bstrVal);
                        strClassProp.Empty();
                        strClassProp = L"NetConnectionStatus";
                        hRes = pClassObject->Get(strClassProp, 0, &var, 0, 0);
                        switch (var.intVal)
                        {
                            case 0: printf("Media Status: Disconnected.\n"); break;
                            case 1: printf("Media Status: Connecting.\n"); break;
                            case 2: printf("Media Status: Connected.\n"); break;
                            case 3: printf("Media Status: Disconnecting.\n"); break;
                            case 7: printf("Media Status: Media disconnected.\n"); break;
                            default: printf("Media Status: Other(%d).\n", var.intVal); break;
                        }
                    }
                }
                else
                    break;
            }
        }
    }

return 0;
}

WMI を使っているので、CoInitialize(NULL)、CoUninitialize() を忘れずに使います。
なお、WMI は、VB スクリプトでも使うことができます。

Option Explicit
Dim objWMIService, ObjItem
Dim strComputer, colItems, strStatus

On Error Resume Next

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery ("Select * from Win32_NetworkAdapter",,48)

For Each objItem in colItems
    Select Case objItem.NetConnectionStatus
        Case 0 strStatus = "Disconnected"
        Case 1 strStatus = "Connecting"
        Case 2 strStatus = "Connected"
        Case 3 strStatus = "Disconnecting"
        Case 4 strStatus = "Hardware not present"
        Case 5 strStatus = "Hardware disabled"
        Case 6 strStatus = "Hardware malfunction"
        Case 7 strStatus = "Media disconnected"
        Case 8 strStatus = "Authenticating"
        Case 9 strStatus = "Authentication succeeded"
        Case 10 strStatus = "Authentication failed"
        Case 11 strStatus = "Invalid address"
        Case 12 strStatus = "Credentials required"
    End Select
 
If objItem.PhysicalAdapter = True Then
WScript.Echo "Adapter Name: " & objItem.Name & vbCRLf  &_
"MAC Address : " & objItem.MACAddress & vbCRLf & _
"Media Status: " & strStatus & vbCRLf
End IF
Next

WScript.Quit

あ~、COM って便利よね~。

Part.2 では、IP Helper API を使い、同じように検証してみます。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。