如何在单独的库中声明相同的接口

本文关键字:声明 接口 单独 | 更新日期: 2023-09-27 17:59:36

我需要在多个程序集中声明相同的接口,但不能引用公共库。

我对接口有相同的定义,但当我试图从另一个应用程序创建它的实例时,会出现这个错误:

无法将"myFilter"类型的对象强制转换为类型"DirectShow.IBaseFilter"

(myFilter声明为:public class myfilter : DirectSow.IBaseFilter...

使用RegAsm.exe 注册源对象

两个程序集都有声明:

[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
Guid("56a86895-0ad4-11ce-b03a-0020af0ba770"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IBaseFilter : IMediaFilter
...

代码是复制的,因此IMediaFilter和所有其他声明完全相同。

我尝试使用以下内容创建对象的实例:

 Type type = Type.GetTypeFromCLSID(new Guid("A3927399-E3AE-41E2-B094-0EA815CC9B9C"));
 IBaseFilter filter = (IBaseFilter)Activator.CreateInstance(type);

如何跨程序集强制转换对象?

如何在单独的库中声明相同的接口

根据定义:你不能。两个彼此无关的独立接口永远不会是彼此"是"的关系,即使它们有"相同"的命名和相同的成员。

专业的解决方案是在一个独立的程序集中分离接口,然后根据需要引用该程序集。所有其他都被认为是黑客攻击(在C#/.NET中)。

要在这种特殊情况下应用所描述的解决方案,您需要2或3个组件:

程序集#1:(它"定义"了接口)。这种"定义"是COM接口导入——在这种特殊情况下:

 [ComImport, System.Security.SuppressUnmanagedCodeSecurity,
 Guid("56a86895-0ad4-11ce-b03a-0020af0ba770"),
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 public interface IBaseFilter : IMediaFilter

组件#2:指程序集#1,包含一个实现接口的类

组件#3:指程序集#1和#2,可以访问和使用/强制转换程序集2中的类。不要在此处(重新)导入接口。对于.NET来说,这将是一个完全相同但完全"其他"的接口,即使它具有完全相同的成员签名。

我认为大会#1和#2可以合并为一个。确保接口声明和类定义都是公共的。关键不是导入两次COM接口,而是导入一次并引用它。

编辑:反思评论:

不要将COM接口和类与.NET接口混淆。(顺便说一句,注释提到的是类id而不是接口id,这在COM中是两个不同的东西。)

通过互操作层高度支持访问.NET中的COM类和接口。互操作层为您完成所有转换和封送,您可以通过方便的.NET接口访问COM组件。然而,这个接口是在(interop)assambly中定义的,并且这些程序集(对于众所周知且经常使用的COM组件)是分布式的,或者是原始COM内容下载的一部分。因此,从字面上看,他们在每台机器上需要的地方。

还有一个概念上的差异,即在COM中,客户端(引用组件)通过only一个guid(这是类id)引用服务器(引用的组件),然后通过only一个guid请求接口的多态实现。因此,客户端可以通过只知道两个guid并执行1-2个COM OS调用来使用组件的特定接口。这是在互操作层中为您准备的黑框。在.NET中,客户端和服务器之间的绑定要强大得多。

如果想要定义此类接口,那么您也必须这样做:以某种方式在一个程序集中定义接口,然后将其分发给其他程序集正在使用该接口并引用该接口的每台机器。

如果您不接受最佳实践的选项:

  • 您可以使用反射来实现类似的松散绑定,但在这种情况下,这被认为是黑客行为,似乎没有必要。请准备好方法名和类名将是字符串,或者至少不正确键入它们不会导致编译错误,而是会导致运行时错误。

  • 您也可以在.NET中定义和实现COM组件,但我不认为这是目标。COM是一种遗留的(很酷的)技术,在当时是非常进步的(不被理解/批评),但这是四分之一个多世纪前(当它被引入时)

(这是问题作者DanW的解决方案)

在DLL中:使组件COM-Visible = checked

using System;
using System.Runtime.InteropServices;
namespace ComExport
{
    [ComImport]
    [Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [TypeIdentifier()]
    public interface ComClass1Interface
    {
        string DoCall();
    }
    [Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class ComClass1 : ComClass1Interface
    {
        public string DoCall()
        {
            return "internal function called";
        }
        public override string ToString()
        {
            return "comclass1.tostring";
        }
    }
}

然后在EXE 中

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ComExportTester
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Load += (o1, e) => Close();
            Type t;
            object o;
            ComExport.ComClass1Interface i1;
            string s = "";
            try
            {
                t = Type.GetTypeFromCLSID(new Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"));
                s += "Type found 't: " + (t == null ? "no" : "yes") + "'n";
                o = t == null ? null : Activator.CreateInstance(t);
                s += "object created 't: " + (o == null ? "no" : o.ToString()) + "'n";
                i1 = o as ComExport.ComClass1Interface;
                s += "interface cast 't: " + (i1 == null ? "no" : "yes") + "'n";
                if (i1 != null)
                    s += i1.DoCall() + "'n";
            }
            catch (Exception x)
            {
                s += x.Message + "'n";
            }
            MessageBox.Show(s);
        }
    }
}
namespace ComExport
{
    [ComImport]
    [Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [TypeIdentifier()]
    public interface ComClass1Interface
    {
        string DoCall();
    }
}

在两者中:

设置x86或x64(在"任何CPU"模式下编译和运行会引发"未注册"异常!)