10

I have a C# .NET 2.0 CF project where I need to invoke a method in a native C++ DLL. This native method returns an array of type TableEntry. At the time the native method is called, I do not know how large the array will be.

How can I get the table from the native DLL to the C# project? Below is effectively what I have now.

// in C# .NET 2.0 CF project [StructLayout(LayoutKind.Sequential)] public struct TableEntry { [MarshalAs(UnmanagedType.LPWStr)] public string description; public int item; public int another_item; public IntPtr some_data; } [DllImport("MyDll.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)] public static extern bool GetTable(ref TableEntry[] table); SomeFunction() { TableEntry[] table = null; bool success = GetTable( ref table ); // at this point, the table is empty } // In Native C++ DLL std::vector< TABLE_ENTRY > global_dll_table; extern "C" __declspec(dllexport) bool GetTable( TABLE_ENTRY* table ) { table = &global_dll_table.front(); return true; } 

Thanks, PaulH

2
  • 1
    Shouldn't that be TABLE_ENTRY**, since you want to write a pointer? Commented Mar 8, 2010 at 17:13
  • @OregonGhost - You are correct, it should be. Commented Mar 8, 2010 at 18:35

1 Answer 1

14

When marshalling an array of unknown size from native to managed I find the best strategy is as follows

  • Type the array to IntPtr in managed code
  • Have the native code return both the array and a size parameter.
  • Manually marshal the data from IntPtr to the custom struct on the managed side.

As such I would make the following changes to your code.

Native:

extern "C" __declspec(dllexport) bool GetTable( TABLE_ENTRY** table, __int32* pSize ) { *table = &global_dll_table.front(); *pSize = static_cast<int32>(global_dll_table.size()); return true; } 

Managed:

[DllImport("MyDll.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool GetTable(out IntPtr arrayPtr, out int size); public static List<TableEntry> GetTable() { var arrayValue = IntPtr.Zero; var size = 0; var list = new List<TableEntry>(); if ( !GetTable(out arrayValue, out size)) { return list; } var tableEntrySize = Marshal.SizeOf(typeof(TableEntry)); for ( var i = 0; i < size; i++) { var cur = (TableEntry)Marshal.PtrToStructure(arrayValue, typeof(TableEntry)); list.Add(cur); arrayValue = new IntPtr(arrayValue.ToInt32() + tableEntrySize); } return list; } 
Sign up to request clarification or add additional context in comments.

2 Comments

Good solution, unless the interface of the native DLL cannot be changed (which is, unfortunately, a common case when P/Invoking existing libraries).
Hi @JaredPar, I see that you're still active here. I have a new question that my code is modeled after your solution here. I was hoping you could take a look if you have time and see if you can point me in the right direction, here is the question stackoverflow.com/questions/38648173/… Thanks

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.