如何一般地实例化COM类接口

时间:2012-09-28 10:04:27

标签: c# generics com activator

我正在尝试重构一段代码并且用尽了我能想到的选项。

这是我原来的代码:

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

AND

        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#中你可以instantiate an interface。)

    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”

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

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

2 个答案:

答案 0 :(得分:2)

您需要关注能够从接口名称创建类对象的看似魔力。让我们举一个每个人都可以尝试的例子。创建一个新的控制台应用程序并使用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]属性。

答案 1 :(得分:0)

可以通过弄清楚该接口的coClass是什么来创建一个实例:

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

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