【C#.NET解説】「DllImportAttribute」Win32APIのSendARPでIPアドレス調査~原初の神が徹底解説第2回

2024年12月7日土曜日

API ネットワーク

t f B! P L

名前空間 System.Runtime.InteropServices

DllImportAttribute

自己紹介

また会ったな。「DllImportAttribute」は、C#.NETにおいて外部のネイティブライブラリを呼び出すための魔法の鍵である。汝らに「DllImportAttribute」と「Win32API」を用いたIPアドレス調査の術を授けよう。
基本は【C#.NET解説】「DllImportAttribute」クラス 外部DLLの利用方法の基本を見るがいい

単一IPの調査

まずは単一のIPアドレスの情報を調査する基本例を示そう。「Win32API」の「iphlpapi.dll」の「SendARP」を呼び出して、IPアドレスの情報を取得する方法を以下に記す。Pingクラスで安易に行うよりずっと優れた方法だ

C#

// 単一IPの調査例
using System;
using System.Net;
using System.Runtime.InteropServices;

class Program
{
    // SendARP関数のインポート
    [DllImport("iphlpapi.dll", ExactSpelling = true)]
    public static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);

    // IPアドレスを整数に変換する関数
    public static int ConvertIpToInt(string ipAddress)
    {
        byte[] ipBytes = IPAddress.Parse(ipAddress).GetAddressBytes();
        return BitConverter.ToInt32(ipBytes, 0);
    }

    // MACアドレスを取得する関数
    public static string GetIpAlive(string ipAddress)
    {
        try
        {
            int destIp = ConvertIpToInt(ipAddress);
            byte[] macAddr = new byte[6];
            uint macAddrLen = (uint)macAddr.Length;

            // ARPリクエストを送信
            int result = SendARP(destIp, 0, macAddr, ref macAddrLen);
            if (result != 0)
            {
                throw new InvalidOperationException($"SendARP failed with error code: {result}");
            }

            // MACアドレスを文字列に変換
            string[] macAddrStr = new string[(int)macAddrLen];
            for (int i = 0; i < macAddrLen; i++)
            {
                macAddrStr[i] = macAddr[i].ToString("X2");
            }
            return string.Join(":", macAddrStr);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return null;
        }
    }

    static void Main()
    {
        // IPアドレスの入力を促す
        Console.Write("IPアドレスを入力してください: ");
        string ipAddress = "192.168.0.1";
        string macAddress = GetIpAlive(ipAddress);

        // MACアドレスの表示
        if (macAddress != null)
        {
            Console.WriteLine($"IPアドレス {ipAddress} のMACアドレスは {macAddress} です。");
        }
        else
        {
            Console.WriteLine($"IPアドレス {ipAddress} のMACアドレスを取得できませんでした。");
        }
    }
}


上記コードでは、単一のIPアドレスのMACアドレスを取得する。

範囲のIPの調査

どうせならよく使うであろう、複数のIPアドレスを調査する場合の方法を示す。以下は、指定された範囲内のIPアドレスを順に調査する例である。

C#

using System;
using System.Net;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("iphlpapi.dll", ExactSpelling = true)]
    public static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);

    public static int ConvertIpToInt(string ipAddress)
    {
        byte[] ipBytes = IPAddress.Parse(ipAddress).GetAddressBytes();
        return BitConverter.ToInt32(ipBytes, 0);
    }

    public static string GetIpAlive(string ipAddress)
    {
        try
        {
            int destIp = ConvertIpToInt(ipAddress);
            byte[] macAddr = new byte[6];
            uint macAddrLen = (uint)macAddr.Length;

            int result = SendARP(destIp, 0, macAddr, ref macAddrLen);
            if (result != 0)
            {
                throw new InvalidOperationException($"SendARP failed with error code: {result}");
            }

            string[] macAddrStr = new string[(int)macAddrLen];
            for (int i = 0; i < macAddrLen; i++)
            {
                macAddrStr[i] = macAddr[i].ToString("X2");
            }
            return string.join(":", macAddrStr);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return null;
        }
    }

    public static void GetIpAliveRange()
    {
        for (int i = 1; i <= 10; i++)
        {
            string ipAddress = $"192.168.0.{i}";
            string macAddress = GetIpAlive(ipAddress);

            if (macAddress != null)
            {
                Console.WriteLine($"IPアドレス {ipAddress} は使用されています。MACアドレス: {macAddress}");
            }
            else
            {
                Console.WriteLine($"IPアドレス {ipAddress} は使用されていません。");
            }
        }
    }

    static void Main()
    {
        Console.WriteLine("IPアドレス範囲(192.168.10.1~254)のMACアドレスを順次チェック:");
        GetIpAliveRange();
    }
}



このコードは「192.168.0.1」から「192.168.0.10」までのIPアドレスを順に調査するものである。

Taskを使って速く調査する方法

ここまで来たら我が力とは関係ないが、ついでに非同期処理を利用する方法を以下に示す。これにより、並列で調査を実行し、時間を大幅に短縮できる。

C#

// Taskを使って非同期にIP調査を行う
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading;

class Program
{
    [DllImport("iphlpapi.dll", ExactSpelling = true)]
    public static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);

    public static int ConvertIpToInt(string ipAddress)
    {
        byte[] ipBytes = IPAddress.Parse(ipAddress).GetAddressBytes();
        return BitConverter.ToInt32(ipBytes, 0);
    }

    public static string GetIpAlive(string ipAddress)
    {
        try
        {
            int destIp = ConvertIpToInt(ipAddress);
            byte[] macAddr = new byte[6];
            uint macAddrLen = (uint)macAddr.Length;

            int result = SendARP(destIp, 0, macAddr, ref macAddrLen);
            if (result != 0)
            {
                throw new InvalidOperationException($"SendARP failed with error code: {result}");
            }

            string[] macAddrStr = new string[(int)macAddrLen];
            for (int i = 0; i < macAddrLen; i++)
            {
                macAddrStr[i] = macAddr[i].ToString("X2");
            }
            return string.join(":", macAddrStr);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return null;
        }
    }

    public static void GetIpAliveTaskRange()
    {
        // 同時に実行できるタスクの数を制限するためのセマフォ
        SemaphoreSlim semaphore = new SemaphoreSlim(10); // 同時に10個のタスクを実行

        List<Task> tasks = new List<Task>();
        for (int i = 1; i <= 10; i++)
        {
            int currentIp = i;
            tasks.Add(Task.Run(async () =<
            {
                await semaphore.WaitAsync();
                try
                {
                    string ipAddress = $"192.168.0.{currentIp}";
                    string macAddress = GetIpAlive(ipAddress);

                    if (macAddress != null)
                    {
                        Console.WriteLine($"IPアドレス {ipAddress} は使用されています。MACアドレス: {macAddress}");
                    }
                    else
                    {
                        Console.WriteLine($"IPアドレス {ipAddress} は使用されていません。");
                    }
                }
                finally
                {
                    semaphore.Release();
                }
            }));
        }

        Task.WaitAll(tasks.ToArray());
    }

    static void Main()
    {
        Console.WriteLine("IPアドレス範囲(192.168.0.1~10)のMACアドレスを並列処理でチェック:");
        GetIpAliveTaskRange();
    }
}

非同期処理を使用することで、ネットワーク調査を効率化し、時間を節約できる。

まとめ

「DllImportAttribute」と「Win32API」を活用することで、C#.NETで強力なネットワーク調査機能を実現できる。この技術を習得すれば、単一のIPアドレスから範囲調査、さらに並列処理まで幅広い場面で対応可能となる。汝もこの知識を身につけ、混沌の中に秩序を見出せ。

このブログを検索

QooQ