C# – System.ArgumentException: VariantWrappers cannot be stored in Variants

c++com

I have a COM object which has the following type def (as seen by OleView):

[id(0x60030010)]
HRESULT Calc(
                [in] BSTR calculation_type, 
                [in] short pricing_model, 
                [in] BSTR option_type, 
                [in] double strike, 
                [in] double spot, 
                [in] double P1, 
                [in] double days, 
                [in] double Volatility, 
                [in] double risk_free_rate, 
                [in] double P2, 
                [in] VARIANT NotUsed, 
                [in] double P3, 
                [in] short binomial_steps, 
                [in, out] VARIANT* dividends, 
                [in, out] VARIANT* days_to_ex_dates, 
                [in] short dividend_count, 
                [in] BSTR dividend_type, 
                [out, retval] VARIANT* );

The COM interop wrapper produces the following method signature:

[return: MarshalAs(UnmanagedType.Struct)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x60030010)]
public virtual extern object Calc([In, MarshalAs(UnmanagedType.BStr)] string calculation_type, [In] short pricing_model, [In, MarshalAs(UnmanagedType.BStr)] string option_type, [In] double strike, [In] double spot, [In] double P1, [In] double days, [In] double Volatility, [In] double risk_free_rate, [In] double P2, [In, MarshalAs(UnmanagedType.Struct)] object NotUsed, [In] double P3, [In] short binomial_steps, **[In, Out, MarshalAs(UnmanagedType.Struct)] ref object dividends, [In, Out, MarshalAs(UnmanagedType.Struct)] ref object days_to_ex_dates**, [In] short dividend_count, [In, MarshalAs(UnmanagedType.BStr)] string dividend_type);

I have tried to call this method the following ways with various exceptions each time:

object dividends = null;
object daysToExDates = null;
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.Runtime.InteropServices.COMException: Type mismatch

object dividends = 0.0d;
object daysToExDates = 0.0d;
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.Runtime.InteropServices.COMException: Type mismatch

object dividends = new double[1];
object daysToExDates = new double[1];
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.IndexOutOfRangeException: Subscript out of range

object dividends = new double[100];
object daysToExDates = new double[100];
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.Runtime.InteropServices.COMException: Object variable or With block variable not set

object dividends = Array.CreateInstance(typeof(double), new int[] { 1 }, new int[] { 1 });
object daysToExDates = Array.CreateInstance(typeof(double), new int[] { 1 }, new int[] { 1 });
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.Runtime.InteropServices.COMException: Object variable or With block variable not set

object dividends = new VariantWrapper(0.0d);
object daysToExDates = new VariantWrapper(0.0d);
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.ArgumentException: VariantWrappers cannot be stored in Variants.

object dividends = new VariantWrapper(new double[1]);
object daysToExDates = new VariantWrapper(new double[1]);
double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 0, 0, 0, 50, ref dividends, ref dividends, 0, "C");

System.ArgumentException: VariantWrappers cannot be stored in Variants.

The most frustrating part is that the following block of VBA works fine:

Dim div() As Double
ReDim div(1 To 1)
Dim price As Double
price = comobj.Calc("P", 1, "C", 2.5, 2.59, 0, 8, 1, 0.01, 0, 0, 0, 50, div, div, 0, "C")

Why doesn't the VariantWrapper work? Any ideas?

Best Solution

VariantWrapper is used to create VARIANTs that contain references (pointers) to other VARIANTs. What you need is a VARIANT, that will be passed by reference, containing a double.

Try the following snippet of code.

double dividends = 0.0d;
double daysToExDates = 0.0d;

// Create Boxed Copies to be referenced
Object oTmpDividends = dividends;
Object oTmpDaysToExDates = daysToExDates;

double price = (double)comobj.Calc("P", 1, "C", 2.5, 2.59, 1, 8, 1, 0.01, 
    0, 0, 0, 50, ref oTmpDividends, ref oTmpDaysToExDates, 0, "C");

// Unbox the copies
dividends = (double)oTmpDividends;
daysToExDates = (double)oTmpDaysToExDates;

This is essentially the same as what VB.NET does, but VB.NET takes care of the difference between object and Object for you, in this scenario.