【问题标题】:How can I instantiate a COM class interface generically如何一般地实例化 COM 类接口
【发布时间】:2012-09-20 05:06:26
【问题描述】:

我正在尝试重构一段代码,但我想不出任何选项。

这是我的原始代码:

        if (WebConfigSettings.ComPartition == null && HttpContext.Current != null)
            Nses = new NSession();
        else
            Nses = (INSession)Marshal.BindToMoniker(string.Format("partition:{0}/new:NuntioServer.NSession", WebConfigSettings.ComPartition));

        if (WebConfigSettings.ComPartition == null && HttpContext.Current != null)
            apses.Wses = new WSession();
        else
            apses.Wses = (IWSession)Marshal.BindToMoniker(string.Format("partition:{0}/new:NuntioServer.WSession", WebConfigSettings.ComPartition));  

这就是我尝试重构它的方式:
(是的,在 C# 中你可以instantiateaninterface。)

    public static TInterface Get<TSubInterface, TInterface>() where TSubInterface: TInterface
    {
        <snip></snip>
        if (!useComPartitions)
            return Activator.CreateInstance<TSubInterface>(); // --> this is not cooperating

        return (TInterface)Marshal.BindToMoniker(.....);
    }

这是我已经尝试过的:

  1. 我尝试指定 new() 约束,然后执行“new TSubInterface()”: 这会导致构建错误:“..必​​须是具有公共无参数构造函数的非抽象类型,才能将其用作泛型类型或方法中的参数'TSubInterface'..”

  2. 当我使用 Activator.CreateInstance 时,出现运行时异常:“无法创建接口的实例”

  3. 当我使用 Activator.CreateComInstanceFrom("someAssemblyName", "typeName") 时,出现编译错误:“无法将表达式类型 'System.Runtime.Remoting.ObjectHandle' 转换为返回类型 TInterface”

[edit] 我可以通过添加 'where TSubInterface : class 来编译这个,但我不确定这是否有意义,因为 TSubInterface 是一个接口。
使用 CreateComInstanceFrom 也不起作用,因为它试图找到在该 dll 不存在且不应该存在的目录中指定的程序集。

我能以某种方式编译并运行它吗?

【问题讨论】:

  • 你用哪个代码这个方法?
  • nses = COMUtils.Get() NSession 和 INSession 都是接口。
  • 您希望它工作只是因为您可以或打算在生产代码中使用它?如果是另一个,那么你最好使用一些控制容器的反转来实现类似的事情。
  • 如果任何魔法让您通过 C#(即在您的链接中)实例化接口在通过反射工作时不适用,我不会感到惊讶。我怀疑编译器会将链接的代码翻译成标准的类实例化 IL,但是反射没有这个选项。
  • 我被 Both NSession and INSession are interface 弄糊涂了,你有一个约束 NSession : INSession。这些真的是两个接口,一个继承另一个吗?如果是这样,为什么模板TClass中的接口的名称。

标签: c# generics com activator


【解决方案1】:

您需要专注于能够从接口名称创建类对象的表面上的魔力。让我们选择一个每个人都可以尝试的例子。创建一个新的控制台应用程序并使用 Project + Add Reference,Browse 选项卡并选择 c:\windows\system32\shell32.dll。

查看使用对象浏览器生成的互操作库。请注意 Shell 类型如何成为接口类型。现在写下这段代码:

class Program {
    static void Main(string[] args) {
        var shl = new Shell32.Shell();
    }
}

在 .exe 文件上编译并运行 ildasm.exe。你会看到:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] class [Interop.Shell32]Shell32.Shell 'shl')
  IL_0000:  nop
  IL_0001:  newobj     instance void [Interop.Shell32]Shell32.ShellClass::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ret
} // end of method Program::Main

注意类型名称是如何替换从 Shell 到 ShellClass。类型库导入器创建了该类,它使用原始的 coclass 名称并将“Class”附加到名称中。编译器会进行替换。

这是关键,Activator.CreateInstance() 无法进行相同的替换。除了直接使用 IFooClass 名称而不是接口名称之外,我没有看到让泛型进行相同替换的明显方法。从技术上讲,您可以检索类型库导入器应用于接口类型的 [CoClass] 属性。

【讨论】:

  • 将你的标记为正确,因为它提出了一个解决方案(使用“CoClass”属性)并解释了它不像我希望的那么容易的一些原因
【解决方案2】:

可以通过找出该接口的 coClass 并创建其实例来完成:

var coClassAttribute = type.GetCustomAttribute<CoClassAttribute>(); // our extension method
return (TSubInterface)Activator.CreateInstance(coClassAttribute.CoClass);

我对此并不满意,但它确实有效。 (不会将此标记为正确答案)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-20
    • 2011-12-31
    • 1970-01-01
    • 2016-10-04
    • 2012-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多