*/ -->

.NET Visual C#を使ってルータからグローバルIPアドレスを取得する方法です。

開発環境は、以下の通りです。

  • IDE : Microsoft Visual Studio 2010(以下VS2010)
  • OS : Windows 7
  • 言語 : C# (4.0)

参照の追加

通常通りC#のプロジェクトを作成し、「UPNPLib」への参照を追加します。

メニューの「プロジェクト」から「参照の追加」を選択し、「COM」タブのリストから「UPnP 1.0 Type Library (Control Point)」を選択して「OK」ボタンを押せば完了です。

コード

kamoya11さんのコードがものすごく参考になりました。

C#でUPnP NAT Traversal - kamoyaの日記

http://d.hatena.ne.jp/kamoya11/20100303/1267613950

このコードは、ポート開放処理に対応したものなので、コードの追記が必要でした。また、VS2010で一部エラーになってしまう部分があったので、適宜修正が必要です。

以下が修正したコードです。

using System;
using System.Runtime.InteropServices;
using UPNPLib;
class UPnPControlPoint
{
    private UPnPService Service { get; set; }
    private UPnPDevice GetDevice(IUPnPDeviceFinder finder, string typeUri)
    {
        foreach (UPnPDevice item in finder.FindByType(typeUri, 0))
        {
            return item;
        }
        return null;
    }
    private UPnPDevice GetDevice(IUPnPDeviceFinder finder)
    {
        UPnPDevice device = this.GetDevice(finder, "urn:schemas-upnp-org:service:WANPPPConnection:1");
        if (device == null)
        {
            device = this.GetDevice(finder, "urn:schemas-upnp-org:service:WANIPConnection:1");
        }
        return device;
    }
    private UPnPService GetService(UPnPDevice device, string serviceId)
    {
        try
        {
            return device.Services[serviceId];
        }
        catch (ArgumentException)
        {
            return null;
        }
    }
    private UPnPService GetService(UPnPDevice device)
    {
        UPnPService service = this.GetService(device, "urn:upnp-org:serviceId:WANPPPConn1");
        if (service == null)
        {
            service = this.GetService(device, "urn:upnp-org:serviceId:WANIPConn1");
        }
        return service;
    }
    private UPnPService GetService()
    {
        UPnPDevice device = this.GetDevice(new UPnPDeviceFinder());
        if (device == null)
        {
            return null;
        }
        return this.GetService(device);
    }
    public UPnPControlPoint()
    {
        this.Service = GetService();
    }
    private object InvokeAction(string bstrActionName, object vInActionArgs)
    {
        if (Service == null)
        {
            return null;
        }
        try
        {
            object result = new object();
            Service.InvokeAction(bstrActionName, vInActionArgs, ref result);
            return result;
        }
        catch (COMException)
        {
            return null;
        }
    }
    public string GetExternalIPAddress()
    {
        object result = InvokeAction("GetExternalIPAddress", new object[] { });
        if (result == null)
        {
            return null;
        }
        return (string)((object[])result)[0];
    }
    public void AddPortMapping(string remoteHost, ushort externalPort, string protocol, ushort internalPort, string internalClient, string description)
    {
        var arguments = new object[] { remoteHost, externalPort, protocol, internalPort, internalClient, true, description, 0 };
        InvokeAction("AddPortMapping", arguments);
    }
    public void DeletePortMapping(string remoteHost, ushort externalPort, string protocol)
    {
        var arguments =  new object[] { remoteHost, externalPort, protocol };
        InvokeAction("DeletePortMapping", arguments);
    }
}

赤字の部分が変更と追記した部分になります。

このクラスを使う場合は、以下のようにコーディングします。

private void button1_Click(object sender, EventArgs e)
{
    new Task(() =>
    {
        UPnPControlPoint p = new UPnPControlPoint();
        var result = p.GetExternalIPAddress();

        this.Invoke((MethodInvoker)delegate()
        {   
            this.textBox1.Text = result;
        });
    }).Start();
}

グローバルIPアドレスを取得する時に、約15秒ほど時間がかかる為、「Task」を利用して非同期処理を行っています。

フォームと同じスレッドで実行すると、フリーズしてしまうので注意が必要です。

関連性がある記事

Disqusでコメント