How can I marshall a vector from a C++ dll to a C# application?

前端 未结 3 973
名媛妹妹
名媛妹妹 2020-12-10 06:26

I have a C++ function that produces a list of rectangles that are interesting. I want to be able to get that list out of the C++ library and back into the C# application th

相关标签:
3条回答
  • 2020-12-10 06:48

    I'm pretty sure you can't do this. You have to be able to translate the C++ code directly to a C# class, so you would at least have to replicate the internals of the vector class to marshall it correctly. I'm also pretty sure you won't be able to move references across the boundary, you'll have to use IntPtr (raw pointers). The approach that i know works is to marshall a raw array of the structs.

    0 讨论(0)
  • 2020-12-10 07:04

    The STL is a C++ specific library, so you cant directly get it across as one object to C#.

    The one thing that is guaranteed about std::vector is that &v[0] points to the first element and all the elements lie linearly in memory (in other words, its just like a C array in terms of memory layout)

    So marshal as array of int... which shouldn't be hard - There are lot of examples on the web.

    Added

    Assuming you only pass the data from C++ to C# :

    C# cannot handle a C++ vector object, so do not try passing it by reference : Instead your C++ code must return a pointer to an array of ints...

    If you are not going to be using this function from multiple threads, you can use static storage :

    int *getRects(bool bClear)
    {
        static vector<int> v; // This variable persists across invocations
        if(bClear)
        {
            v.swap(vector<int>());
        }
        else
        {
            v.clear();
            // Fill v with data as you wish
        }
    
        return v.size() ? &v[0] : NULL;
    }
    

    call getRects(true) if the returned data is significant in size, so you release the memory in v.

    For simplicity, instead of passing out the size of the vector data too, just put a sentinel value at the end (like say -1) so the C# code can detect where the data ends.

    0 讨论(0)
  • 2020-12-10 07:10

    Yes. You can. Actually, not just std::vector, std::string, std::wstring, any standard C++ class or your own classes can be marshaled or instantiated and called from C#/.NET.

    Wrapping a std::vector<any_type> in C# is indeed possible with just regular P/Invoke Interop, it is complicated though. even a std::map of any type can be done in C#/.NET.

    public class SampleClass : IDisposable
    {    
        [DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi,          CallingConvention=CallingConvention.ThisCall)]
        public extern static void SampleClassConstructor(IntPtr thisObject);
    
        [DllImport("YourDll.dll", EntryPoint="DestructorOfYourClass", CharSet=CharSet.Ansi,          CallingConvention=CallingConvention.ThisCall)]
        public extern static void SampleClassDestructor(IntPtr thisObject);
    
        [DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi,      CallingConvention=CallingConvention.ThisCall)]
        public extern static void DoSomething(IntPtr thisObject);
    
        [DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi,      CallingConvention=CallingConvention.ThisCall)]
        public extern static void DoSomething(IntPtr thisObject, int x);
    
        IntPtr ptr;
    
        public SampleClass(int sizeOfYourCppClass)
        {
            this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
            SampleClassConstructor(this.ptr);  
        }
    
        public void DoSomething()
        {
            DoSomething(this.ptr);
        }
    
        public void DoSomethingElse(int x)
        {
            DoSomethingElse(this.ptr, x);
        }
    
        public void Dispose()
        {
            if (this.ptr != IntPtr.Zero)
            {
                // The following 2 calls equals to "delete object" in C++
                // Calling the destructor of the C++ class will free the memory allocated by the native c++ class.
                SampleClassDestructor(this.ptr);
    
                // Free the memory allocated from .NET.
                Marshal.FreeHGlobal(this.ptr);
    
                this.ptr = IntPtr.Zero;
            }
        }
    }
    

    Please see the below link,

    C#/.NET PInvoke Interop SDK

    (I am the author of the SDK tool)

    Once you have the C# wrapper class for your C++ class ready, it is easy to implement ICustomMarshaler so that you can marshal the C++ object from .NET.

    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.icustommarshaler.aspx

    0 讨论(0)
提交回复
热议问题