11

C# 4 と Microsoft の COM ベースのNAT トラバーサル API (Hnetcfg.dll)を使用して、NAT トラバーサルに UPnP を使用するコード (家庭用のみ) を少し書こうとしています。

残念なことに (またはおそらく幸いなことに)、最後に .NET で COM 相互運用を行う必要があったのは、最後の氷河期の頃でした。相互運用のための C# の動的型の使用とコールバックの記述方法について根本的に混乱しているようです (そのため、 COM サーバーがマネージ コードを呼び出します)。

エキサイティングな数行のコードを次に示します。

// Referencing COM NATUPNPLib ("NATUPnP 1.0 Type Library")

using System;
using NATUPNPLib;

class NATUPnPExample
{
    public delegate void NewNumberOfEntriesDelegate(int lNewNumberOfEntries);

    public static void NewNumberOfEntries(int lNewNumberOfEntries)
    {
        Console.WriteLine("New number of entries: {0}", lNewNumberOfEntries);
    }

    public static void Main(string[] args)
    {
        UPnPNAT nat = new UPnPNAT();
        NewNumberOfEntriesDelegate numberOfEntriesCallback = NewNumberOfEntries;

        nat.NATEventManager.NumberOfEntriesCallback = numberOfEntriesCallback;

        nat.StaticPortMappingCollection.Add(4555, "TCP", 4555, "192.168.0.1", true, "UPnPNAT Test");

        // Presumably my NewNumberOfEntries() method should be called by the COM component about now

        nat.StaticPortMappingCollection.Remove(4555, "TCP");
    }
}

上記のコードでは、Add と Remove の呼び出しは問題なく機能します。素晴らしい。

問題は、ポート マッピング エントリの数がいつ変更されたかを知りたいことです。そのためには、コールバック インターフェイス ( INATEventManager::put_NumberOfEntriesCallback ) を登録する必要があります。これは、 INATNumberOfEntriesCallback または IDispatch インターフェイスをサポートする必要があります。VS2012 のオブジェクト ブラウザは、 INATEventManager::put_NumberOfEntriesCallback を次のように記述します。

dynamic NumberOfEntriesCallback { set; }

C# 4 では派手な属性で何かを装飾する必要はなく、下品な方法でデリゲートを INATEventManager::put_NumberOfEntriesCallback に詰め込み、.NET を心配させるだけでコールバックを登録できるという印象を受けました。 IDispatch について、混乱を解消します。しかし、私はひどく間違っているようです。

それで、ええと... NewNumberOfEntries メソッドが確実に呼び出されるようにするにはどうすればよいですか?

また、例外がスローされずに記述できるnat.NATEventManager.NumberOfEntriesCallback = 1;かどうかも少し心配です。nat.NATEventManager.NumberOfEntriesCallback = "Sausages";

4

2 に答える 2

1

私はあなたが抱えていたのと同じ問題を抱えていました.このトピックにはあまり助けがないので、あなたの投稿は非常に役に立ちました! ポイントが足りないなどの理由であなたの答えにコメントすることはできませんが、あなたの答えは最高ですが、思ったようには機能しません。

nat.NATEventManager.ExternalIPAddressCallback = new CallbackDisp();

同じディスパッチを使用して動作し、外部 IP が変更されたときに通知します。でも、

nat.NATEventManager.NumberOfEntriesCallback = new CallbackDisp();

次の条件からの UPnP マップの変更のみを報告します: A.) NATUPnP インスタンスによって追加/削除されました.. この場合:

    nat.StaticPortMappingCollection.Add();

または B.) インスタンスの作成時にすでにマップされていた:

 var nat = new UPnPNAT();

例として、プログラムを開始したときに Utorrent が実行されていて、プログラムの終了をブロックする何かがあった場合 (Console.WriteLine();) など.. Utorrent を終了すると、コールバックが起動し、マップ変更。それはまさに私が最初に望んでいたものです。ただし、Utorrent や UPnP を使用する他のアプリを再度開いた場合、コールバックは発生せず、変更は通知されません。

言うまでもなく、それは非常に苛立たしいものでした。わかったらシェアお願いします!特定の間隔で StaticPortMappingCollection をポーリングすることで機能を簡単に実装できることはわかっていますが、少し「ハッキー」に思えます。

于 2014-06-10T00:41:28.497 に答える
1

なんとか出来たようです。2 つのオプション - カスタム インターフェイス "INATNumberOfEntriesCallback" (タイプ ライブラリでは宣言されていないようです。自分で宣言する必要があります) と、DispId(0) を使用したプレーン ディスパッチを使用します。IDispatch/IUnknown への変換は、フレームワークによって自動的に実行されます。そう:

オプション1。

INATNumberOfEntriesCallback を宣言し、そのインターフェイスを実装するコールバック クラスを作成します (厄介な部分は Guid です。「Natupnp.h」ファイルに由来するもので、タイプ ライブラリにあるようには見えません)。

// declare INATNumberOfEntriesCallback interface 
[ComVisible(true)]
[Guid("C83A0A74-91EE-41B6-B67A-67E0F00BBD78")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface INATNumberOfEntriesCallback
{
    void NewNumberOfEntries(int val);
};

// implement callback object
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class CallbackNewNumberOfEntries : INATNumberOfEntriesCallback
{
    public void NewNumberOfEntries(int val)
    {
        Console.WriteLine("Number of entries changed: {0}", val);
    }
}

class NATUPnPExample
{
    public static void Main(string[] args)
    {
        var nat = new UPnPNAT();

        nat.NATEventManager.NumberOfEntriesCallback = new CallbackNewNumberOfEntries();

        nat.StaticPortMappingCollection.Add(4555, "TCP", 4555, "192.168.0.1", true, "UPnPNAT Test");

        // Presumably my NewNumberOfEntries() method should be called by the COM component about now

        nat.StaticPortMappingCollection.Remove(4555, "TCP");
    }
}

オプション 2。

無地発送をご利用ください。ドキュメントには、dispid(0) を使用でき、4 つの (!) パラメータを使用して呼び出す必要があると記載されています ( docsの備考セクションを参照)。したがって、基本的に次の構成は「ディスパッチ」の方法で機能するようです。

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class CallbackDisp
{
    [DispId(0)]
    public void OnChange(string updateType, object obj, object name, object val)
    {
        Console.WriteLine("{0}: {1} = {2}", updateType, name, val);
    }
}

class NATUPnPExample
{
    public static void Main(string[] args)
    {
        var nat = new UPnPNAT();

        nat.NATEventManager.NumberOfEntriesCallback = new CallbackDisp();

        nat.StaticPortMappingCollection.Add(4555, "TCP", 4555, "192.168.0.1", true, "UPnPNAT Test");

        // Presumably my NewNumberOfEntries() method should be called by the COM component about now

        nat.StaticPortMappingCollection.Remove(4555, "TCP");
    }
}
于 2013-12-27T03:57:49.060 に答える