开发者

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

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 that is calling it.

So far, I'm encoding the rectangles like so:

struct ImagePatch{ 
   int xmin, xmax, ymin, ymax;
}

and then encoding some vectors:

void MyFunc(..., std::vector<int>& rectanglePoints){
   std::vector<ImagePatch> patches; //this is filled with rectangles
   for(i = 0; i < patches.size(); i++){
       rectanglePoints.push_back(patches[i].xmin);
       rectanglePoints.push_back(patches[i].xmax);
       rectanglePoints.push_back(patches[i].ymin);
       rectanglePoints.push_back(patches[i].ymax);
   }
}

The header for interacting with C# looks like (and works for a bunch of other functions):

extern "C" {
    __declspec(dllexport) void __cdecl MyFunc(..., std::vecto开发者_JAVA百科r<int>& rectanglePoints);
}

Are there some keywords or other things I can do to get that set of rectangles out? I found this article for marshalling objects in C#, but it seems way too complicated and way too underexplained. Is a vector of integers the right way to do this, or is there some other trick or approach?


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.


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


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜