1

I am trying to marshal a vector of the following C++ class to C# that is used in a tree:

class CFileNode
{
  private:
    std::string m_name;
    std::vector<CFileNode*> m_children;
    bool m_isDirectory;
    CFileNode* m_parent;
};

And:

extern DATAACCESSLAYERDLL_API size_t __stdcall Get_FileSystemNodeCount()
{
    // GetFileNodeList() returns a std::vector<CFileNode*>&
    return CFileSystem::GetFileNodeList().size();
}

extern DATAACCESSLAYERDLL_API void __stdcall Get_FileSystemNodes(size_t count, CFileNode** nodes)
{
    nodes = CFileSystem::GetFileNodeList().data();
}

And in C#:

[SuppressUnmanagedCodeSecurity]
[DllImport("MyDll.dll")]
static private extern int Get_FileSystemNodeCount();

[SuppressUnmanagedCodeSecurity]
[DllImport("MyDll.dll")]
static private extern void Get_FileSystemNodes(int count, [In,Out] IntPtr[] outNodes);

And my struct:

[StructLayout(LayoutKind.Sequential, Pack = 1), Serializable]
public struct CFileNode
{
    [MarshalAs(UnmanagedType.LPStr)]
    string m_name;
    [MarshalAs(UnmanagedType.SafeArray)]
    IntPtr[] m_children;
    [MarshalAs(UnmanagedType.Bool)]
    bool m_isDirectory;
    [MarshalAs(UnmanagedType.LPStruct)]
    IntPtr m_parent;
}

And where I use it:

int count = Get_FileSystemNodeCount();
IntPtr[] results = new IntPtr[count];
Get_FileSystemNodes(count, results);

foreach (IntPtr ptr in results)
{
    m_fileSystemNodes.Add(Marshal.PtrToStructure<CFileNode>(ptr));
}

In its current state, the IntPtr[] results is just a big array of zeroes. If I modify C++ GetFileSystemNodes() to go through the vector and add everything to the array nodes, results will have a load of numbers that look like memory addresses (although I have no idea if they are garbage or not), but then the Marshal.PtrToStructure<CFileNode>(ptr) fails with an AccessViolationException.

I would like to transfer this whole tree structure from C++ to C# in as few PInvoke calls as possible, for speed. This is why, before any of this code is executed, my entire tree is flattened out into a list. Each node still has a pointer to its parent and children, so it's easy to reconstruct it. If it makes it easier, I could also just pass the root node.

Here are my questions:

1) Is marshalling vectors of objects that contain other vectors even possible? Should I make my C++ classes simpler, using arrays or something instead?

2) Is there a way to pass the vector of nodes through GetFileSystemNodes without moving everything in the vector to the array argument?

3) What is the correct way to write my C# struct so it matches my C++ one?

2
  • 1
    Related/duplicate?: stackoverflow.com/questions/4747171/… - accepted answer suggests to use e.g.: C++/CLI instead (which would have been my first instinct as well) Commented Dec 16, 2016 at 15:16
  • And the official documentation also suggests to prefer C++/CLI over PInvoke Commented Dec 16, 2016 at 15:18

1 Answer 1

1

This can be done, I would recommend using SWIG. SWIG makes interop between C++ and other languages (including C#) much easier. It has built in support to handle some of the C++ STL containers, such as vectors.

Yes, SWIG can handle vectors of objects, you just have to build them up in the interface file.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.