为什么我不能投我的COM对象来实现它在C#中的接口?为什么我不能投我的COM对象来实现它在C#中的接

2019-05-12 02:04发布

我在DLL(该代码显示在从元数据中的Visual Studio)此接口:

#region Assembly XCapture.dll, v2.0.50727
// d:\svn\dashboard\trunk\Source\MockDiagnosticsServer\lib\XCapture.dll
#endregion

using System;
using System.Runtime.InteropServices;

namespace XCapture
{
    [TypeLibType(4160)]
    [Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]
    public interface IDiagnostics
    {
        [DispId(1)]
        void GetStatusInfo(int index, ref object data);
    }
}

所以,我创建了这样一个类的COM服务器:

[ComVisible(true)]
[Guid(SimpleDiagnosticsMock.CLSID)]
[ComDefaultInterface(typeof(IDiagnostics))]
[ClassInterface(ClassInterfaceType.None)]
public class SimpleDiagnosticsMock : ReferenceCountedObject, IDiagnostics
{
    public const string CLSID = "281C897B-A81F-4C61-8472-79B61B99A6BC";

    // These routines perform the additional COM registration needed by 
    // the service. ---- stripped from example

    void IDiagnostics.GetStatusInfo(int index, ref object data)
    {
        Log.Info("GetStatusInfo called with index={0}, data={1}", index, data);

        data = index.ToString();
    }
}

服务器似乎做工精细,我能够使用对象从一个VBScript。 但后来我尝试从另一个C#客户端使用它:

    [STAThread]
    static void Main(string[] args)
    {
        Guid mockClsId = new Guid("281C897B-A81F-4C61-8472-79B61B99A6BC");
        Type mockType = Type.GetTypeFromCLSID(mockClsId, true);
        IDiagnostics mock = (IDiagnostics)Activator.CreateInstance(mockType);

        //var diag = mock as IDiagnostics;

        object s = null;
        mock.GetStatusInfo(3, ref s);

        Console.WriteLine(s);
        Console.ReadKey();
    }

而它失败

无法转换类型的COM对象“系统.__ ComObject”到接口类型“XCapture.IDiagnostics”。 此操作失败的原因是对IID“{XXXXXXXXXXXX-XXXXXXXX-XXXXXXXXXXXX}”的界面处的COM组件调用QueryInterface失败,因为以下错误:不支持此接口(从HRESULT异常:0x80004002(E_NOINTERFACE)) 。

我究竟做错了什么?

我也曾尝试使用InvokeMember,除我是不是能够得到裁判,返回的数据参数还挺奏效。

编辑:添加STAThread属性来我的主要程序。 这并没有解决问题,但你除非你绝对相信你不需要它真的应该使用STAThread与COM。 见下面汉斯帕桑特的答案。

Answer 1:

此异常可以是一个DLL地狱问题。 但是,最简单的解释是什么从你的片段缺失 。 您的Main()方法缺少[STAThread]属性。

这时候,你在你的代码中使用COM对象的事项的重要因素。 他们大多是不是线程安全的,他们需要一个线程,这是一个热情好客的家不能支持的线程代码。 该属性强制线程的状态,一个你可以用Thread.SetApartmentState明确设置()。 你不能为一个应用程序的主线程做,因为Windows启动它,这个属性是用来配置它。

如果你忽略它,那么你的主线程加入MTA,多线程的公寓。 然后COM被迫创建一个新的线程来给组件一个安全的家。 这需要从你的主线程到辅助线程封送所有呼叫。 该E_NOINTERFACE错误时COM无法找到一个方法来做到这一点升高时,它需要知道如何序列化方法参数的帮手。 在这个时候,需要由COM开发者的照顾,他并没有那么做。 马虎,但并非不寻常。

STA线程的要求是,它也泵消息循环。 你从Application.Run在一个WinForms或WPF应用程序获得的那种()。 你没有一个在你的代码。 你可能逃脱它,因为你实际上并没有让从工作线程的所有呼叫。 但是,COM组件往往依赖于消息循环是供自己使用。 你会被它行为不端,而不是引发一个事件或死锁注意到这一点。

因此,开始通过首先将属性固定这样的:

[STAThread]
static void Main(string[] args)
{
    // etc..
}

这将解决这个例外。 如果你有描述的事件提升或死锁问题,那么你就需要改变你的应用程序类型。 的WinForms通常很容易走了。

我不能以其他方式采取捅在嘲笑失败。 有参与COM显著部署的详细信息,注册表项必须写允许COM发现组件。 你必须得到的GUID权利和接口必须是完全匹配的。 Regasm.exe需要注册.NET组件这是[标记有ComVisible特性。 如果试图嘲笑现有的COM组件,并得到它的权利,那么你会毁了真正的组件注册。 不能肯定这是值得追求的)和你有显著的问题添加到[标记有ComVisible特性]集的引用时,IDE拒绝允许.NET程序通过COM使用.NET程序集。 只有后期绑定能骗过机器。 从COM异常情况来看,还没有接近了嘲讽呢。 最好使用COM组件原样,也是一个真正的考验。



Answer 2:

那么,问题是,我与IDiagnostics接口DLL是从TLB中产生,而TLB从来没有得到注册。

由于DLL是从TLB进口,RegAsm.exe不予登记库。 所以我用了regtlibv12.exe工具注册的TLB本身:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\regtlibv12.exe "$(ProjectDir)\lib\Diagnostics.tlb"

然后,一切都奇迹般地开始工作。

由于regtlibv12是不支持的工具,我仍然不知道如何正确地做到这一点。



文章来源: Why cannot I cast my COM object to the interface it implements in C#?
标签: c# com