3

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 the list is build, I need to return it back to .NET. So That array(list) should be converted to the transport which easily can be read on .NET side or the initial list can be used "as is" just passing the pointer.

Thanks in advance for giving me a hint!

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
John
  • 1,834
  • 5
  • 32
  • 60
  • 1
    In C++ dynamic array = [`std::vector`](http://en.cppreference.com/w/cpp/container/vector) – NathanOliver Jun 05 '15 at 15:42
  • Are you P/Invoking or working in C++/CLI? For C++/CLI you can just `gcnew` an `Array` or `List` and be done. – crashmstr Jun 05 '15 at 15:47
  • @crashmstr I created standard c++ win32 library. Just Invoking (marshaling). c++ methods via obtaining the pointer to exported method in c++ dll. – John Jun 05 '15 at 15:50

2 Answers2

4

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;
}
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Is SAFEARRAY the underlying layer for a vector? Can I dig the SAFEARRAY out and pass to c# .NET? Or Should I copy the content of vector to a new created SAFEARRAY? – John Jun 05 '15 at 19:42
  • 1
    @John: I'm not sure about what you exactly mean by SAFEARRAY being the underlying for a vector. std::vector and SAFEARRAY are completely different entities: the former is a C++ standard class template, the latter is a Win32 OLE structure. The .NET's CLR understands SAFEARRAY very well, so it can extract the raw array data from the passed SAFEARRAY. But SAFEARRAY lacks the programming interface versatility and power of std::vector. That's why I suggested to build the dynamic array using std::vector first, and then copy to SAFEARRAY, and use it for marshalling. – Mr.C64 Jun 06 '15 at 10:41
  • OK, now everything is now clear to me. Thanks again for your tips. – John Jun 06 '15 at 10:49
  • I have been struggling with creating and passing to c# a SAFEARRAY and it is not working at all :( Could You look at that? http://stackoverflow.com/questions/31011617/return-safearray-from-c-to-c-sharp – John Jun 23 '15 at 21:06
  • What var Type i should use for a struct? – John Jun 23 '15 at 21:24
3

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.

Nikita
  • 950
  • 8
  • 19