COM对象在C#中使用ComInterop进行后期绑定

时间:2017-11-27 08:52:12

标签: c# com com-interop idispatch

我有一个简单的数学实用程序的COM对象。其中,它导出2个接口 - IDL如下:

interface IUnivariateFunction : IDispatch
{
    HRESULT evaluate([in] double a, [out, retval] double* b);
};

interface IOneDimSolver : IDispatch
{
    HRESULT Solve([in] double min, [in] double max, [in] double targetAccuracy, [in] LONG maxIterations, [in] IUnivariateFunction* function, [out, retval] double* result);
};

然后IOneDimSolver的实现是:

coclass Brent
{
    [default] interface IOneDimSolver;
};

如果我想在C#中使用这个对象并且我有TypeLibrary可用,那么使用这个功能非常简单 - 我可以实现一些功能来解决:

public class CalculatePi : IUnivariateFunction
{
    public double evaluate(double x)
    {
        return x - Math.PI;
    }
}

然后使用它:

var brent = new Brent();
var result = brent.Solve(-10.0, 10.0, 1E-10, 100, new CalculatePi());

这很有效,所以没有问题。但是,我还想证明我可以将它用于后期绑定(此时几乎纯粹用于学术目的)。

var brent = Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")));
var result = brent.GetType().InvokeMember("Solve", BindingFlags.InvokeMethod, null, brent, new object[] { -10.0, 10.0, 1E-10, 100, new CalculatePi() });

看起来brent对象的创建正常,但对InvokeMember的调用失败并显示消息{"Specified cast is not valid."}

我猜测CalculatePi的类型与COM IDispatch机器期望的任何内容不兼容,但我不确定如何使其工作。任何帮助都会很棒!

1 个答案:

答案 0 :(得分:3)

  

{“指定的演员表无效。”}

这是一个由InvalidCastException产生的CLR异常消息。这极大地限制了这次事故的可能原因,你知道它实际上不是炸弹的本机代码。在后期绑定代码中没有很多可能的原因。我只能想到一个。

最可能的原因是CalculatePi类缺少[ComVisible(true)]属性。因此,当CLR尝试从对象获取IDispatch接口指针时,它会发挥作用。它在早期的情况下工作,C#编译器可以从类型库告诉它需要生成IUnivariateFunction引用。这是可见的,因为它来自互操作库。

应该解决这个问题。但请记住,IOneDimSolver :: Solve()方法实际上与喜欢使用后期绑定的代码类型不兼容。脚本语言不知道如何实现IUnivariateFunction接口,它对它一无所知。您应该声明参数IDispatch*,以便任何人都可以调用它。在实现中,使用QueryInterface()来获取IUnivariateFunction接口指针。成功后你会很开心,让你快速轻松地拨打电话。但是当它没有时,必须回到IDispatch :: Invoke()。如果您不想编写该代码,那么最好不承诺IDispatch支持并从IUnknown派生接口。