tags:

views:

453

answers:

1

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?

A: 

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.

meklarian
Thanks for the comment. I have already tried that tho in 1 step rather than two:object dividends = 0.0d;object daysToExDates = 0.0d;
Note that the capitalization is significant in C#. 'Object' is not the same as 'object' in C#. 'Object' is a value type that contains an instance. 'object' is a reference type that refers to another given instance. Hence the ref parameter doesn't work properly for 'object' in this case because it is just a reference to the actual .NET object. But it should work for 'Object' because that type may contain an object and passing it by reference in this scenario will implicitly pass the contents (a double) by reference as COM expects.
meklarian
Please note the following link for more information on Boxing, it might make the distinction clearer than I can.http://msdn.microsoft.com/en-us/magazine/cc301569.aspx
meklarian