#c# #com #interop #.net-5
Вопрос:
В устаревшей части моего приложения у меня есть следующий COM-код:
var plate = new Plate(); // this is the COM object
[..]
string dataSetName = string.Empty;
object row = null;
object column = null;
object kineticIndex = null;
object wavelengthIndex = null;
object horizontalIndex = null;
object verticalIndex = null;
object value = null;
object primaryStatus = null;
object secondaryStatus = null;
while(plate.GetRawData(
ref dataSetName,
ref row,
ref column,
ref kineticIndex,
ref wavelengthIndex,
ref horizontalIndex,
ref verticalIndex,
ref value,
ref primaryStatus,
ref secondaryStatus) != 0)
{
var rawDataReader = new RawDataReader
{
DataSetName = dataSetName,
Row = (int[])row,
Column = (int[])column,
KineticIndex = (int[])kineticIndex,
WavelengthIndex = (int[])wavelengthIndex,
HorizontalIndex = (int[])horizontalIndex,
VerticalIndex = (int[])verticalIndex,
Value = (double[])value,
PrimaryStatus = (int[])primaryStatus,
SecondaryStatus = (int[])secondaryStatus
};
[..]
}
Вот GetRawData
подпись при изучении с помощью средства просмотра ole-com:
[id(0x00000019), helpstring("method GetRawData")]
long GetRawData(
BSTR* pDataSetName,
VARIANT* pvRow,
VARIANT* pvColumn,
VARIANT* pvKineticIndex,
VARIANT* pvWavelengthIndex,
VARIANT* pvHorizontalIndex,
VARIANT* pvVerticalIndex,
VARIANT* pvValue,
VARIANT* pvPrimaryStatus,
VARIANT* pvSecondaryStatus);
Этот код хорошо работал в течение нескольких лет, даже сопротивлялся переходу с .NET 4.6.1 на .NET Core 3.1.
Но недавно мы перешли на .NET 5, и это меня подвело: первый вызов GetRawData
метода завершается успешно, но второй вызывает следующее исключение:
Сообщение:
System.Runtime.InteropServices.COMException : Type mismatch. (0x80020005 (DISP_E_TYPEMISMATCH))
Трассировка стека:
RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams) RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Object[] aArgs, Boolean[] aArgsIsByRef, Int32[] aArgsWrapperTypes, Type[] aArgsTypes, Type retType) IPlate.GetRawData(Stringamp; pDataSetName, Objectamp; pvRow, Objectamp; pvColumn, Objectamp; pvKineticIndex, Objectamp; pvWavelengthIndex, Objectamp; pvHorizontalIndex, Objectamp; pvVerticalIndex, Objectamp; pvValue, Objectamp; pvPrimaryStatus, Objectamp; pvSecondaryStatus)
Прежде всего, я смог исправить это, сбросив все параметры до нуля перед следующим вызовом GetRawData
метода. Так что я здесь не для того, чтобы получить исправление, а для объяснения того, что произошло под капотом:
Почему назначение ref
параметра, указывающего на null, работает, когда один и тот же вызов с ref
параметром, указывающим на фактический объект, не удался?
Как вы можете видеть в теле while, все параметры на самом деле являются массивами int или double. Я не очень хорошо знаком с взаимодействием, но я не понимаю, почему я не мог ничего назначить ref object
параметру, независимо от его текущего значения.
Поэтому я пишу небольшую программу без взаимодействия, чтобы проверить, что это действительно возможно в «ванильной» .NET 5:
class Program
{
static void Main(string[] args)
{
object param = null;
Test(ref param);
Test(ref param);
}
static void Test(ref object param)
{
param = new int[12];
}
}
И это успешно завершается. Поэтому я предполагаю, что проблема связана с взаимодействием. Согласно сообщению об исключении ( Type mismatch
), я могу представить себе начало объяснения. Но для меня это все еще неестественно:
- Связано ли это с тем, как владелец реализует библиотеку dll взаимодействия?
- Почему он внезапно выходит из строя при переходе на .NET 5?
Комментарии:
1. Как точно определяется GetRawData (idl, .h, другая информация) и как это реализовано?
2. Я знаю, что вам нужны объяснения, и, к сожалению, я не могу их предложить, но я вижу, что ваш код все еще относится к эпохе до NET 4
dynamic
. Попробуйте более новый синтаксис, описанный в Cutting Edge — C# 4.0, ключевое слово Dynamic и COM . Новый стиль гораздо более снисходителен иdynamic
на самом деле был разработан для взаимодействия с COM3. @MickyD — динамика с COM, к сожалению, нарушена, как и многие другие материалы COM (потому что это «Только для Windows!») 🙁 с .NET 5: github.com/dotnet/runtime/issues/12587
4. @SimonMourier вы знаете, что я собирался сказать что-то о том, что .NET 5, скорее всего, является причиной, но я был удивлен, что у OP что-то работает. Я не знал, что динамика сломана. Чем больше я слышу о .NET 5 (больше нет доменов приложений, центров сертификации, GAC, частично доверенного кода), тем больше я разочаровываюсь. Так же, как и XNA Framework 4 до этого, оба они были переписаны для наименьшего общего знаменателя 🙁
5. В автоматизации COM подобные параметры имеют семантику [вход, выход]. Другими словами, COM-сервер должен делать что-то нетривиальное, когда вы передаете ненулевой аргумент. Он должен сначала уничтожить вариант, прежде чем присваивать предполагаемое значение. Имеет значение для варианта, требующего хранения (строки, массивы, указатели интерфейса). Это не особенно сложно, VariantClear() выполняет свою работу, но, похоже, автор этого компонента ошибся в этом. Понятия не имею, почему, конечно, может быть предупреждение об очень неэффективном коде, эти массивы особенно дороги для маршалинга.