I have to find the way to build the array of structs on C++ Win32 side. I don\'t have the initial number of items. It should be very fast to resize that array.
When
A very common way of implementing "dynamic arrays" in C++ is to use STL's std::vector
. In your case, you can define a vector<SomeData>
. std::vector
can change its size dynamically (i.e. at run-time), as per your request: you can use its push_back
or emplace_back
methods for that purpose, adding new items to the vector.
However, C# doesn't "understand" std::vector
.
To marshal its content to C#, you could use a SAFEARRAY
, which the CLR understands very well. The good thing about SAFEARRAY is that it stores also the array size, besides the array content: it's a self-contained data structure.
So, you can create a SAFEARRAY of proper size, fill it with the content dynamically created in the vector, and pass that SAFEARRAY to C#.
Note that you could also build a SAFEARRAY directly, without passing for std::vector. But STL's vector has a more powerful programming interface; so if you want to do several additions of items via push_back or emplace_back at run-time, building the std::vector first, and then "marshalling" it into a SAFEARRAY, could be a better option. Anyway, that depends on your particular needs and context.
As an alternative, you could use C++/CLI as a bridging layer between the C++ side and the C# side. In this case you could use gcnew
to create a .NET "managed" array
.
And, as another option, you could export a couple of functions from your native C++ DLL with a C interface:
a function to get the count of items in the array
another function to get the actual array data in an output caller-allocated buffer (the size of which is returned by the previous function)
In C#, the output buffer pointer is passed using an IntPtr
output parameter.
You can then use Marshal.PtrToStructure
to marshal a single data item, and you can increase the pointer using Marshal.SizeOf
, to make it point to the next item in the array.
Something like this:
// In C++:
struct SomeData
{
/* your data fields */
};
// Export these two functions from your native DLL:
extern "C" int GetSomeDataCount( /* some params */ );
extern "C" void GetSomeData( SomeData* ptr, /* some other params */ );
// In C#:
public struct SomeData
{
// Map C++ data structure fields to C#
}
static extern int GetSomeDataCount( /* some params */ );
static extern void GetSomeData( [Out] out IntPtr ptr, /* some other params */ );
SomeData[] GetSomeData( /* some params */ )
{
// Allocate an array of proper size
SomeData[] dataArray = new SomeData[ GetSomeDataCount( /* some params */ ) ];
// Fill the array with content from GetSomeData
IntPtr dataPtr;
GetSomeData( out dataPtr, /* some other params */ );
for (int i = 0; i < dataArray.Length; i++)
{
dataArray[i] = Marshal.PtrToStructure(dataPtr, typeof(SomeData));
dataPtr += Marshal.SizeOf(typeof(SomeData));
}
return dataArray;
}
Generally if your C++ dll compiled with CLI support you can simply use managed containers. If you don't want your dll to be compiled with CLI option, then you may write small C++/CLI wrapper dll which will call methods from native C++ dll and store objects in the managed container. Another possible solution is to change interface of the C++ lib to return objects by indexes, support insertion and/or removal.
std::vector<CFoo> vec;
void init() {
//read data to vec
}
CFoo getIthElement(int i) {
return vec[i];
}
int getElementCount() {
return vec.size();
}
So you will use getIthElement
and getElementCount
functions on the C# side.