In a legacy part of my application, I have the following COM code:
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
};
[..]
}
Here is the GetRawData
signature when explored with ole-com viewer:
[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);
This code worked well for several years, even resisted to the migration from .NET 4.6.1 to .NET Core 3.1.
But lately we migrated to .NET 5 and it failed me: the first call to the GetRawData
method completes successfully, but the second one throws the following exception:
Message:
System.Runtime.InteropServices.COMException : Type mismatch. (0x80020005 (DISP_E_TYPEMISMATCH))
Stack Trace:
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(String& pDataSetName, Object& pvRow, Object& pvColumn, Object& pvKineticIndex, Object& pvWavelengthIndex, Object& pvHorizontalIndex, Object& pvVerticalIndex, Object& pvValue, Object& pvPrimaryStatus, Object& pvSecondaryStatus)
First of all, I was able to fix it by reseting all the parameters to null before the next call to the GetRawData
method. So I am not here to get a fix but an explanation of what happened under the hood:
Why the assignment of a ref
parameter pointing to null works, when the same call with a ref
parameter pointing to an actual object failed?
As you can see in the while body, all the parameters are actually arrays of int or double. I am not very familiar with Interop but I don't understand why I could not assign anything to a ref object
parameter, whatever its current value is.
So I write a little program without Interop to validate that it is actually possible in "vanilla" .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];
}
}
And it completes successfully. So I guess the problem is related to the Interop. According to the exception message (Type mismatch
), I can imagine a start of explanation. But it is still unnatural to me:
- Is it related to the way the owner implement the Interop dll?
- Why does it fail suddently when migrating to .NET 5?