C# 4、COM 互操作和 UPnP:三驾马车

Posted

技术标签:

【中文标题】C# 4、COM 互操作和 UPnP:三驾马车【英文标题】:C# 4, COM interop and UPnP: A trying triumvirate 【发布时间】:2012-10-26 04:38:31 【问题描述】:

我正在尝试使用 C# 4 和 Microsoft 基于 COM 的 NAT traversal API (Hnetcfg.dll) 编写一些使用 UPnP 进行 NAT 遍历的代码(仅供家庭使用)。

不幸的是(或者也许幸运的是)我最后一次在 .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 调用工作得很好。太棒了。

麻烦的是,我还想知道端口映射条目的数量什么时候发生了变化,为此我需要注册一个回调接口(@98​​7654322@),它必须支持 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"; 而不抛出异常。

【问题讨论】:

我以前没有亲自使用过这个库,但也许下面的网站会帮助你制作回调:social.msdn.microsoft.com/Forums/vstudio/en-US/… 【参考方案1】:

看来我能够让它工作。两个选项 - 使用自定义接口“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");
    

【讨论】:

【参考方案2】:

我遇到了和你一样的问题,因为在这个话题上没有太多帮助,所以你的帖子帮助很大!它不会让我评论你的答案,因为我没有足够的分数或其他任何东西,但你的答案是最好的,但并不完全符合我的想法。

nat.NATEventManager.ExternalIPAddressCallback = new CallbackDisp();

工作,使用相同的调度,并会在外部 IP 更改时告诉您。然而,

nat.NATEventManager.NumberOfEntriesCallback = new CallbackDisp();

仅报告来自这些条件的 UPnP 地图更改:A.) 它是由 NATUPnP 实例添加/删除的。在这种情况下:

    nat.StaticPortMappingCollection.Add();

OR B.) 它在创建实例时已经被映射:

 var nat = new UPnPNAT();

例如,如果 Utorrent 在您启动程序时正在运行,并且您有一些东西阻止程序退出(Console.WriteLine();) 例如.. 当您退出 Utorrent 时,回调将触发,并通知你的地图变化。这正是我一开始想要的。但是,如果您重新打开 Utorrent 或任何其他使用 UPnP 的应用程序,它不会触发回调,也不会通知您更改。

不用说,这非常令人沮丧。如果你弄清楚了,请分享!我知道我可以通过在给定的时间间隔轮询 StaticPortMappingCollection 轻松实现该功能,但对我来说这似乎有点“hacky”。

【讨论】:

以上是关于C# 4、COM 互操作和 UPnP:三驾马车的主要内容,如果未能解决你的问题,请参考以下文章

腾讯云邱跃鹏:云大数据和AI是助力数字中国的三驾马车

Google三驾马车:GFSMapReduce和Bigtable

大数据的那些事:三驾马车之坑人的MapReduce

华为云原生的“三驾马车”

(2021-04-02)笔试常见的“三驾马车”

让数据使用自由而安全,安华金和“三驾马车”驱动数据安全治理