Позднее связывание COM-объекта с использованием ComInterop в 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, но я просто не знаю, как заставить его работать. Любая помощь будет отличной!
1 ответ
{"Указанное приведение недействительно."}
Это сообщение об ошибке CLR, созданное InvalidCastException. Это сильно ограничивает возможные причины этой неудачи, вы знаете, что на самом деле это не нативный код, который бомбит. Существует не так много возможных причин для этого в позднем коде. Я могу думать только об одном.
Наиболее вероятная причина в том, что в классе CalculatePi отсутствует [ComVisible(true)]
приписывать. Таким образом, CLR перестает работать, когда пытается получить указатель интерфейса IDispatch от объекта. Это работало в раннем случае, компилятор C# мог сказать из библиотеки типов, что ему нужно создать ссылку на IUnivariateFunction. Который виден, так как он пришел из библиотеки взаимодействия.
Должен решить проблему. Но имейте в виду, что метод IOneDimSolver::Solve() на самом деле не совместим с видом кода, который любит использовать позднюю привязку. Язык сценариев не знает, как реализовать интерфейс IUnivariateFunction, он ничего не знает об этом. Вы должны объявить параметр IDispatch*
так что любой может это назвать. В реализации используйте QueryInterface() для получения указателя интерфейса IUnivariateFunction. Вы будете счастливы, когда это удастся, что позволит вам сделать звонок быстро и легко. Но приходится возвращаться к IDispatch::Invoke(), когда это не так. Если вы не хотите писать этот код, то, возможно, лучше не обещать поддержку IDispatch и получить интерфейс от IUnknown.