在 C# 中使用 ComInterop 的 COM 对象后期绑定
COM object late binding using ComInterop in C#
我有一个用于一些简单数学实用程序的 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 机制所期望的任何内容都不兼容,但我只是不确定如何使其工作。任何帮助都会很棒!
{"Specified cast is not valid."}
这是一条 CLR 异常消息,由 InvalidCastException 产生。这极大地限制了造成这种事故的可能原因,你知道它实际上不是炸弹的本机代码。在后期绑定代码中没有太多可能的原因。我只能想到一个。
最可能的原因是 CalculatePi class 缺少 [ComVisible(true)]
属性。因此,当 CLR 试图从对象获取 IDispatch 接口指针时,它会崩溃。它适用于早期绑定情况,C# 编译器可以从类型库中判断它需要生成 IUnivariateFunction 引用。这是可见的,因为它来自互操作库。
应该解决问题。但请记住,IOneDimSolver::Solve() 方法实际上与 喜欢 使用后期绑定的代码类型不兼容。脚本语言不知道如何实现 IUnivariateFunction 接口,它对此一无所知。您应该声明参数 IDispatch*
以便任何人都可以调用它。实现中使用QueryInterface()获取IUnivariateFunction接口指针。当它成功时,您会很高兴,让您可以快速轻松地拨打电话。但是当它没有时必须退回到 IDispatch::Invoke() 。如果您不想编写该代码,那么最好不要承诺 IDispatch 支持并从 IUnknown 派生接口。
我有一个用于一些简单数学实用程序的 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 机制所期望的任何内容都不兼容,但我只是不确定如何使其工作。任何帮助都会很棒!
{"Specified cast is not valid."}
这是一条 CLR 异常消息,由 InvalidCastException 产生。这极大地限制了造成这种事故的可能原因,你知道它实际上不是炸弹的本机代码。在后期绑定代码中没有太多可能的原因。我只能想到一个。
最可能的原因是 CalculatePi class 缺少 [ComVisible(true)]
属性。因此,当 CLR 试图从对象获取 IDispatch 接口指针时,它会崩溃。它适用于早期绑定情况,C# 编译器可以从类型库中判断它需要生成 IUnivariateFunction 引用。这是可见的,因为它来自互操作库。
应该解决问题。但请记住,IOneDimSolver::Solve() 方法实际上与 喜欢 使用后期绑定的代码类型不兼容。脚本语言不知道如何实现 IUnivariateFunction 接口,它对此一无所知。您应该声明参数 IDispatch*
以便任何人都可以调用它。实现中使用QueryInterface()获取IUnivariateFunction接口指针。当它成功时,您会很高兴,让您可以快速轻松地拨打电话。但是当它没有时必须退回到 IDispatch::Invoke() 。如果您不想编写该代码,那么最好不要承诺 IDispatch 支持并从 IUnknown 派生接口。