Marshalling Complex structures between C#/C++
c#, c++, marshalling
Solution
Okay, I have a working sample. I'm posting this as another answer because it's a very different approach.
So, on the C++ side, I've got this header file:
struct Struct1
{
int d1[10];
int d2[10];
};
extern "C" __declspec(dllexport) void __stdcall
TestApi2(int* pLength, Struct1 **pStructures);
And the following code:
__declspec(dllexport) void __stdcall
TestApi2(int* pLength, Struct1 **pStructures)
{
int len = 10;
*pLength = len;
*pStructures = (Struct1*)LocalAlloc(0, len * sizeof(Struct1));
Struct1 *pCur = *pStructures;
for (int i = 0; i < len; i++)
{
for (int idx = 0; idx < 10; ++idx)
{
pCur->d1[idx] = i + idx;
pCur->d2[idx] = i + idx;
}
pCur ++;
}
}
Now, the important bit is `LocalAlloc`. That's actually the place where I've had all the issues, since I allocated the memory wrong. `LocalAlloc` is the method .NET calls when it does `Marshal.AllocHGlobal`, which is very handy, since that means we can use the memory in the caller and dispose of it as needed.
Now, this method allows you to return an arbitrary length array of structures. The same approach can be used to eg. return a structure of an array of structures, you're just going deeper. The key is the `LocalAlloc` - that's memory you can easily access using the `Marshal` class, and it's memory that isn't thrown away.
The reason you have to also return the length of the array is because there's no way to know how much data you're "returning" otherwise. This is a common "problem" in unmanaged code, and if you've ever done any P/Invoking, you know everything about this.
And now, the C# side. I've tried hard to do this in a nice way, but the problem is that arrays of structures simply aren't blittable, which means you can't simply use `MarshalAs(UnmanagedType.LPArray, ...)`. So, we have to go the `IntPtr` way.
The definitions look like this:
[StructLayout(LayoutKind.Sequential)]
public class Struct1
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] d1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] d2;
}
[DllImport(@"Win32Project.dll", CallingConvention = CallingConvention.StdCall)]
static extern void TestApi2(out int length, out IntPtr structs);
Basically, we get a pointer to the length of the "array", and the pointer to the pointer to the first element of the array. That's all we need to read the data.
The code follows:
int length;
IntPtr pStructs;
TestApi2(out length, out pStructs);
// Prepare the C#-side array to copy the data to
Struct1[] structs = new Struct1[length];
IntPtr current = pStructs;
for (int i = 0; i < length; i++)
{
// Create a new struct and copy the unmanaged one to it
structs[i] = new Struct1();
Marshal.PtrToStructure(current, structs[i]);
// Clean-up
Marshal.DestroyStructure(current, typeof(Struct1));
// And move to the next structure in the array
current = (IntPtr)((long)current + Marshal.SizeOf(structs[i]));
}
// And finally, dispose of the whole block of unmanaged memory.
Marshal.FreeHGlobal(pStructs);
The only thing that changes if you want to really return a structure of an array of structures is that the method parameters move into the "wrapping" structure. The handy thing is that .NET can automatically handle the marshalling of the wrapper, the less-handy thing is that it can't handle the inner array, so you again have to use length + IntPtr to manage this manually.
Problem
I'm trying to populate an array of structures from C++ and pass the result back to C#. I thought maybe creating a struct with an array of structures maybe the way forward as most examples I have come across use structures(but passing basic types). I have tried the following but no luck so far. Found an example at: http://limbioliong.wordpress.com/2011/08/20/passing-a-pointer-to-a-structure-from-c-to-c-part-2/?relatedposts_exclude=542 I have the following in C# ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; namespace CSharpDLLCall { class Program { [StructLayout(LayoutKind.Sequential,Pack=8)] public struct Struct1 { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public double[] d1; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public double[] d2; } [StructLayout(LayoutKind.Sequential)] public struct TestStructOuter { public int length; public IntPtr embedded; } static void Main(string[] args) { Program program = new Program(); program.demoArrayOfStructs(); } public void demoArrayOfStructs() { TestStructOuter outer = new TestStructOuter(); testStructOuter.embedded = new Struct1[10]; outer.length = 10; outer.embedded = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Struct1)) * 10); Marshal.StructureToPtr(outer, outer.embedded, false); testAPI2(ref outer); outer = (TestStructOuter)(Marshal.PtrToStructure(outer.embedded, typeof(TestStructOuter))); Marshal.FreeHGlobal(outer.embedded); } [DllImport(@"C:\CPP_Projects\DLL\DLLSample\Release\DLLSample.dll")] static extern void testAPI2(IntPtr pTestStructOuter); } } ``` In C++ in the header i have ``` #ifdef DLLSAMPLE_EXPORTS #define DLLSAMPLE_API __declspec(dllexport) #else #define DLLSAMPLE_API __declspec(dllimport) #endif #include <iostream> using namespace std; #pragma pack(1) struct struct1 { double d1[]; double d2[]; }; struct TestStructOuter { struct1* embedded; }; extern "C" { DLLSAMPLE_API void __stdcall testAPI2(TestStructOuter* pTestStructOuter); } ``` In the cpp I have: ``` #include "stdafx.h" #include "DLLSample.h" __declspec(dllexport) void __stdcall testAPI2(TestStructOuter* pTestStructOuter) { // not sure that this is necessary // for (int i = 0; i < 10 ; ++i) // { // pTestStructOuter->embedded = new struct1; // } for (int i = 0; i < 10 ; ++i) { struct1 s1; for (int idx = 0; idx < 10; ++idx) { s1.d1[i] = i+0.5; s1.d2[i] = i+0.5; } pTestStructOuter->embedded[0] = s1; } } ``` This doesn't seem to work the error i get: The parameter is incorrect.(Exception from HRESULT:0x80070057 (E_INVALIDARG)) Which probably means that its not recognizing the array of structures. Any ideas how I can do this? Thanks.