How to build a dynamic array in C++ and return it back to C#/.NET

前端 未结 2 1527
忘掉有多难
忘掉有多难 2021-01-15 05:24

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

2条回答
  •  野的像风
    2021-01-15 05:44

    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. 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;
    }
    

提交回复
热议问题