C++ DLL Interop with C#: Exposing multidimensional arrays of unknown size, and memory-management
I have a C++ library that does some hardcore financial mathematics (Quantlib). I have a normal C++ dll built against it, which exposes a lightweight interface to a C# front-end app to allow users to pass in various parameters, run scenarios etc. The dll then returns the data for display.
I'm really unclear on how to handle the interface between the dll and the C# layer however, and in particular I have no idea where the memory allocation/deallocation is handled, i.e. in C# or the dll
In essence, I'd like my dll to return a class or struct that holds a 2-dimensional array of values (double, char* etc). I won't know the size of this array at the time I call the dll, so it'll have to be allocated by the dll itself I guess.
At the moment, I have the dll returning a looooooooong char* with a pipe-delimited list of values. T开发者_JAVA技巧his seems to work, but it doesn't strike me as a particularly elegant solution.
Any help, guys?
EDIT
thanks a lot for all your feedback. Sorry, I haven't done any DLL programming in years, hence the slightly stupid question.
I decided to simply treat the 2d array as a 1d array with a height/width offset on the C++ side, and to just live with guessing the size of the array up-front. Creating a 2d array on the C# side, and marshalling it to C++ in this way seems to work fine.
Here's what I have. Thoughts, anyone?
C#:
[DllImport(<dllPath>, CallingConvention = CallingConvention.Cdecl)]
static extern void ChangeArray2d(double[,] arr, int l1, int l2);
//populated with some sample values
double[,] arr = new double[,] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } };
ChangeArray2d(arr, arr.GetLength(0), arr.GetLength(1));
C++:
__declspec(dllexport) void ChangeArray2d( double* arrayin, int height, int width)
{
for (int n = 0; n<height; n++){
for (int m = 0; m<width; m++){
arrayin[n*width+m] = arrayin[n*width+m] + 100;
}
};
return;
}
Generally it is common pattern to allocate buffer, pass it (with buffer size as maximum amount of data you can handle at once) to C++ native function that will fill it and then to handle data/free buffer. Or reuse buffer.
If your native function allocates buffer for you and you should free it afterwards - it is vary fragile contract and also prohibits memory reuse. E.g. if you need to call this function 20 times - you'll have 20 unnecessary allocations.
Something like that:
private int YourNativeFunctionStub(IntPtr buffer, int bufferSize)
{
int writtenToBufferBytes = bufferSize; // if it wrote less to buffer - it should return correct count of bytes
// here your library fills the buffer with data
return writtenToBufferBytes;
}
private double[] GetArrayFromNative()
{
int bufsize = 1024; // probably you should find it by calling another func from your library?
IntPtr nativeBuffer = Marshal.AllocHGlobal(bufsize);
try
{
int bytesReceivedInBuffer = YourNativeFunctionStub(nativeBuffer, bufsize);
int receivedArrayLength = bytesReceivedInBuffer / sizeof(double);
var managedArray = new double[receivedArrayLength];
Marshal.Copy(nativeBuffer, managedArray, 0, receivedArrayLength);
return managedArray;
}
finally
{
Marshal.FreeHGlobal(nativeBuffer);
}
}
UPD: see the Hans' comment.
精彩评论