Passing a structure with array of another structure from C# to C (P/Invoke)
I have the following structure:
[StructLayout(LayoutKind.Sequential)]
public struct TCurve
{
public int fNumItems; /* Number of TRatePts in fArray */
public IntPtr fArray; /* Dates & rates */ // pointer to an array of TRatePt[]
public long fBaseDate; /* Discount date */
public double fBasis; //ZERO_CURVE_BASIS in capvoll.c which is #defined as ANNUAL_BASIS 1 /* Number compounding periods / year */
public long fDayCountConv; //ZERO_CURVE_DAY_COUNT_CONV in capvoll.c which is #defined as GTO_ACT_365F; = 2L /* How the year fraction is computed */
public IntPtr fClassHandle; /* C++ class handle implementation */
};
[StructLayout(LayoutKind.Sequential)]
public struct TRatePt
{
public long fDate;
public double fRate;
};
The TRatePt is defined as follows:
[DllImport("alib.dll", EntryPoint = "GtoCapFloorAvgVol")]
public static extern int CapFloorAvgVol(
long capStartDate, /* (I) */
long capExpiryDate, /* (I) */
double[] strikeRates, /* (I) */
int numStrikeRates, /* (I) */
double[] principles, /* (I) */
int numPrinciples, /* (I) */
int moneymarketDen, /* (I) # days/year */
ref TDateInterval resetPeriod, /* (I) Time Between caplets */
int optionType, /* (I) Cap or Floor */
char stubPosition, /* (I) 2/16/94 GG 'F'ront or 'B'ack */
[In] IntPtr zeroCurve, /* (I) For discounting Pointer to TCurve*/
double price, /* (I) Price */
double avgVolGuess, /* (I) Average Volatility guess */
out double avgVol); /* (O) Average Volatility */
The C declaration is:
GTO_EXPORT(int ) GtoCapFloorAvgVol(
TDate capStartDate, /* (I) */
TDate capExpiryDate, /* (I) */
double *strikeRates, /* (I) */
int numStrikeRates, /* (I) */
double *principles, /* (I) */
int numPrinciples, /* (I) */
int moneymarketDen, /* (I) # days/year */
TDateInterval *resetPeriod, /* (I) Time Between caplets */
int optionType, /* (I) Cap or Floor */
char stubPosition, /* (I) 2/16/94 GG 'F'ront or 'B'ack */
TCurve *zeroCurve, /* (I) For discounting */
double price, /* (I) Price */
double avgVolGuess, /* (I) Average Volatility guess */
double *avgVol) /* (O) Average Volatility */
Thanks in advance!
Ok so here are the C structs:
typedef struct _TCurve{
int fNumItems; /* Number of TRatePts in fArray */
TRatePt *fArray; /* Dates & rates */
TDate fBaseDate; /* Discount date */
double fBasis; /* Number compounding periods / year */
long fDayCountConv; /* How the year fraction is computed */
void *fClassHandle; /* C++ class handle implementation */ } TCurve;
and :
typedef struct{
TDate fDate;
double fRate;} TRatePt;
By the way if you know how to handle the void* help would be welcome...
PS: TDate is just a
typedef long int TDate;
And here is how I use it:
TCurve _zeroCurve = new TCurve()
{
fBaseDate = _tempValueDate,
fNumItems = _ratePoints.Length,
fBasis = 2L,
fDayCountConv = 1
};
int _sizeOfTCurve = Marshal.SizeOf(typeof(TCurve));
IntPtr p_zeroCurve = Marshal.AllocHGlobal(_sizeOfTCurve);
Marshal.StructureToPtr(_zeroCurve, p_zeroCurve, false);
开发者_如何学运维 int _status;
_zeroCurve.fArray = Marshal.AllocHGlobal(_ratePoints.Length * Marshal.SizeOf(typeof(TRatePt)));
try
{
IntPtr _ratePt = _zeroCurve.fArray;
for (int _i = 0; _i < _ratePoints.Length; _i++)
{
Marshal.StructureToPtr(_ratePoints[_i], _ratePt, false);
//_ratePt = new IntPtr(_ratePt.ToInt64() + Marshal.SizeOf(typeof(TRatePt)));
IntPtr _nextRatePt = new IntPtr(_ratePt.ToInt64() + Marshal.SizeOf(typeof(TRatePt)));
_ratePt = _nextRatePt;
//_ratePt += Marshal.SizeOf(typeof(TRatePt));
}
_status = CapFloorAvgVol(_tempStartDate, _temPexpiryDate, strikeRates, strikeRates.Length, principles, principles.Length, moneymarketDen,
ref _resetPeriod, optionType, stubPosition, p_zeroCurve, price, avgVolGuess, out avgVol);
}
finally
{
Marshal.FreeHGlobal(_zeroCurve.fArray);
}
Marshal.FreeHGlobal(p_zeroCurve);
p_zeroCurve = IntPtr.Zero;
OK, here's how to do what I think you mean, and I'm sorry it took me so long to work it out. You will need to use manual marshalling for the pointer to TCurve
.
I've simplified your struct for convenience.
[StructLayout(LayoutKind.Sequential)]
public struct TCurve
{
public int fNumItems;
public IntPtr fArray;//you need to marshal this manually
};
...
[DllImport("alib.dll", EntryPoint = "GtoCapFloorAvgVol")]
public static extern int CapFloorAvgVol(
...
TCurve ref zeroCurve,
...
);
...
TCurve curve = new TCurve();
TRatePt[] items = new TRatePt[2];
//for example, but you'd have the items coming in as a parameter I guess
curve.fNumItems = items.Length;
curve.fArray = Marshal.AllocHGlobal(items.Length * Marshal.SizeOf(typeof(TRatePt)));
try
{
IntPtr item = curve.fArray;
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], item, false);
item = new IntPtr(item.ToInt64() + Marshal.SizeOf(typeof(TRatePt)));
}
//now call the function passing the curve struct
}
finally
{
Marshal.FreeHGlobal(curve.fArray);
}
I doubt that the Pack = 1
is correct. It's pretty rare to pack structs in C.
Another problem is that long
is 64 bits in C# but long int
, long
, int
are all 32 bits in C++ on Windows.
Finally, the void*
parameter is an opaque pointer and you should declare it as IntPtr
in your C# struct.
Isnt it working with unsafe structs ?
[StructLayout(LayoutKind.Sequential)]
public unsafe struct TCurve
{
public TRatePt* fArray; /* Dates & rates */ // pointer to an array of TRatePt[]
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct TRatePt
{
public long fDate;
public double fRate;
};
And then, in your code :
unsafe
{
TCurve _zeroCurve = new TCurve();
var arr = new TRatePt[]{new TRatePt(), new TRatePt()};
fixed (TRatePt* pt = arr)
{
_zeroCurve.fArray = pt;
// do sthing while your array is fixed
}
}
精彩评论