Как мне вызвать COM-объект VB6 из C # с помощью dynamic, если у него есть параметр ref?

У меня есть следующая устаревшая функция VB6, которую я хочу вызвать из C #.

Public Function CreateMiscRepayment(ByRef objMiscRepayment As MiscRepayment) As Variant
   ' Code that sets objMiscRepayment here
End Function

Я использую следующий код на C #, но получаю исключение:

dynamic vb6ComObject = Activator.CreateInstance(Type.GetTypeFromProgID(progId));
dynamic miscRepayment = null;
dynamic result = vb6ComObject.CreateMiscRepayment(ref miscRepayment);

Исключение составляют:

System.ArgumentException: Could not convert argument 0 for call to CreateMiscRepayment.
at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
at CallSite.Target(Closure , CallSite , ComObject , Object& )
at CallSite.Target(Closure , CallSite , ComObject , Object& )
at CallSite.Target(Closure , CallSite , Object , Object& )
at CallSite.Target(Closure , CallSite , Object , Object& )
Application\ApplicationClasses.cs(65,0): at ApplicationClasses.CanInstantiateMiscRepayment()

Я пробовал изменить ref на out, но получаю ту же ошибку. Если я опущу ref, метод выполняется без ошибок, но, конечно, miscRepayment по-прежнему имеет значение null, а не содержит объект, который должен был быть передан.


Обновлять

Я пробовал другие способы, в том числе с использованием VB.NET (поскольку он всегда был более дружественным к COM, чем C #).

Со следующим кодом VB.NET:

Dim vb6ComObject = Activator.CreateInstance(System.Type.GetTypeFromProgID(progId))
Dim miscRepayment = Nothing
Dim result = vb6ComObject.CreateMiscRepayment(miscRepayment)

Это вызывает следующее похожее, но другое исключение:

System.Runtime.InteropServices.COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))
    at Microsoft.VisualBasic.CompilerServices.LateBinding.LateGet(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack)
    at Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateGet(Object Instance, Type Type, String MemberName, Object[] Arguments, String[] ArgumentNames, Type[] TypeArguments, Boolean[] CopyBack)
    UnitTest1.vb(19,0): at TestProject1.UnitTest1.TestMethod1()

Интересно, что если я изменю вызов в примере кода C # или VB.NET, чтобы использовать _10 _ / _ 11_ вместо miscRepayment, тогда код выполняется без генерации исключения. Я даже установил точку останова в коде COM-объекта VB6 и могу подтвердить, что код на этом конце выполняется правильно. Очевидно, что при установке для параметра miscRepayment значения _14 _ / _ 15_ в .NET не будет возможности получить созданный объект. Проблема должна быть связана с сортировкой параметров.

Я также пробовал использовать Type.InvokeMember с аргументом ParameterModifier, который отмечает miscRepayment как параметр ref, но получил следующее исключение:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Runtime.InteropServices.COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))

     --- End of inner exception stack trace ---
    at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters)
    at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)
    UnitTest1.vb(18,0): at TestProject1.UnitTest1.TestMethod1()

Наконец, я пробовал следующий код VB.NET:

Dim vb6ComObject = Activator.CreateInstance(System.Type.GetTypeFromProgID(progId))
Dim args(0) As Object
Microsoft.VisualBasic.CompilerServices.LateBinding.LateCall(vb6ComObject, type, "CreateMiscRepayment", args, Nothing, New Boolean() {True})

Это вызывает следующее исключение:

System.Runtime.InteropServices.COMException: Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))
    at Microsoft.VisualBasic.CompilerServices.LateBinding.InternalLateCall(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack, Boolean IgnoreReturn)
    at Microsoft.VisualBasic.CompilerServices.LateBinding.LateCall(Object o, Type objType, String name, Object[] args, String[] paramnames, Boolean[] CopyBack)
    UnitTest1.vb(17,0): at TestProject1.UnitTest1.TestMethod1()

Со всем кодом, который вызывает исключение, COM-объект VB6 никогда не вызывается. Код взаимодействия COM должен подавляться при попытке маршалинга параметра ref.

При поиске в Google я встречал несколько примеров с использованием Type.InvokeMember, но параметры ref всегда относятся к простым типам, таким как целые числа и строки.


person John Mills    schedule 16.12.2011    source источник
comment
Что если вы измените dyanmic miscRepayment на object miscRepayment? (И относитесь к нему как к dynamic позже, если это сработает ... но я не использую dynamic или C # 4. Кроме того, есть ли способ получить группу динамических методов? Например, принудительно отключить от нее действие / функцию / делегат?)   -  person    schedule 16.12.2011
comment
@pst: я попытался изменить с dynamic на object, как вы предложили, но он по-прежнему вызывает то же исключение. Не уверен насчет твоего последнего бита.   -  person John Mills    schedule 16.12.2011
comment
Мне было интересно, возможно ли это ((MyDelegate)it.method)(...);, но это не так - по крайней мере, из-за того, что это выражение - похоже. Можете ли вы заставить его работать с помощью обычного отражения? Это, по крайней мере, означало бы, эй, это как-то работает! (или нет).   -  person    schedule 16.12.2011
comment
Возможно, некоторые подсказки: stackoverflow.com/questions/2475310/   -  person    schedule 16.12.2011
comment
Прошло некоторое время с тех пор, как я имел дело с COM-объектами, но если вы не сможете получить определение MiscRepayment вместо динамического. Также могу я посмотреть, как вы сослались на vb6ComObject, я уверен, что он будет стандартным, просто хочу убедиться.   -  person Adam    schedule 16.12.2011
comment
@pst: Я пробовал использовать отражение, но безуспешно. type.GetMethod("CreateMiscRepayment") возвращает ноль. type.GetMethods() возвращает 7 методов, но это общедоступные методы в MarshalByRefObject.   -  person John Mills    schedule 16.12.2011
comment
@ Адам: Я создал экземпляр vb6ComObject, позвонив Activator.CreateInstance(Type.GetTypeFromProgID(progId). Я не имею в виду сборку взаимодействия; надеялся, что динамика позволит мне обойтись без этого.   -  person John Mills    schedule 16.12.2011
comment
Можете ли вы использовать отражение, чтобы получить фактический тип объекта, который вам нужен. Итак, можете ли вы использовать Activator.CreateInstance для создания экземпляра нужного вам объекта. Затем используйте отражение, чтобы получить и заполнить свойства, такие как instance.GetType (). GetProperty (nameofproperty). Глядя на ошибку, у него просто возникают проблемы с сопоставлением подписей метода, который вы хотите вызвать, поскольку параметр должен быть его спрашиваю типа.   -  person Adam    schedule 16.12.2011
comment
@ Адам: Я попробовал, когда Pst предложил использовать отражение, но он не показал ни одного из методов, определенных в коде VB 6. Я предполагаю, что интерфейс IDispatch - это все, что у нас есть, поэтому будет работать только позднее связывание. Я также попробую запросить тип, возвращаемый GetTypeFromProgID(progId), чтобы узнать, содержит ли он методы.   -  person John Mills    schedule 16.12.2011


Ответы (2)


Похоже, что в .NET нет способа вызвать метод для COM-объекта, который принимает параметр ref со сложным типом.

У меня зарегистрировал ошибку в Microsoft. Голосуйте it, если эта проблема также влияет на вас.

Обновление 30.04.2013

Есть комментарий к отчету об ошибке от Microsoft, в котором говорится, что она исправлена. Однако нет упоминания о том, какая версия .NET была затронута.

person John Mills    schedule 19.12.2011
comment
Вряд ли будет так. Хотя я не пробовал это с COM-объектами, я вызвал класс через Activator.CreateInstance с параметром ref в конструкторе. На самом деле не было необходимости отмечать, что это ссылка, поскольку она учитывалась автоматически. Если вы имеете дело с параметрами метода, вы можете использовать typeof (TheType) .MakeByRefType (). Опять же, я мог ошибаться :) - person Adam; 19.12.2011
comment
Я уверен, что он отлично подходит для отражения над объектами .NET. Однако это не работает (для меня) с COM. Только счастлив, что ошибся, и вполне счастлив изменить принятый ответ, если кто-то может показать, как заставить его работать. - person John Mills; 20.12.2011
comment
Есть новости по этому поводу. Я борюсь с той же проблемой, и нет четкого решения. MS с там обычной хренью ... - person MoonKnight; 07.03.2014

На самом деле это не ответ, а обходной путь.

Мне кажется, что когда вы меняете способ доступа к COM-объектам с dynamic на статический, проблема исчезает. Под статическим способом я подразумеваю подготовить dll для COM-объекта с помощью C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\TlbImp.exe. Думаю, это ранняя привязка.

person Jarekczek    schedule 21.05.2016