如何将vector <int>从C ++ dll编组到C#应用程序?</int>

时间:2010-04-30 20:47:37

标签: c# .net c++ stl marshalling

我有一个C ++函数,它生成一个有趣的矩形列表。我希望能够从C ++库中获取该列表并返回到调用它的C#应用​​程序。

到目前为止,我正在对矩形进行编码:

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

然后编码一些向量:

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);
   }
}

与C#交互的标题看起来像(并适用于许多其他函数):

extern "C" {
    __declspec(dllexport) void __cdecl MyFunc(..., std::vector<int>& rectanglePoints);
}

我可以做一些关键字或其他事情来取出那组矩形吗?我在C#中找到了用于编组对象的this article,但它看起来太复杂了,太过于缺乏解释。整数向量是正确的方法,还是有其他技巧或方法?

3 个答案:

答案 0 :(得分:4)

STL是一个特定于C ++的库,所以你不能直接将它作为一个对象传递给C#。

关于std :: vector的一件事就是&amp; v [0]指向第一个元素,所有元素都线性地位于内存中(换句话说,它就像内存方面的C数组一样)布局)

因此编组为int ...这应该不难 - 网上有很多例子。

假设您只将数据从C ++传递给C#:

C#无法处理C ++向量对象,所以不要尝试通过引用传递它:而是你的C ++代码必须返回一个指向int数组的指针......

如果您不打算在多个线程中使用此功能,则可以使用静态存储:

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;
}

如果返回的数据大小很大,则调用getRects(true),以便在v中释放内存。

为简单起见,不要传递矢量数据的大小,只需在末尾添加一个sentinel值(比如说-1),这样C#代码就可以检测数据的结束位置。

答案 1 :(得分:1)

是。您可以。实际上,不仅std::vectorstd::stringstd::wstring,任何标准C ++类或您自己的类都可以编组或实例化,并从C#/ .NET调用。

使用常规P / Invoke Interop确实可以在C#中包装std::vector<any_type>,但这很复杂。甚至任何类型的std::map都可以在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;
        }
    }
}

请参阅以下链接

C#/.NET PInvoke Interop SDK

(我是SDK工具的作者)

一旦准备好了C ++类的C#包装类,就可以很容易地实现ICustomMarshaler,这样就可以从.NET编组C ++对象了。

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

答案 2 :(得分:0)

我很确定你不能这样做。您必须能够将C ++代码直接转换为C#类,因此您至少必须复制vector类的内部以正确编组它。我也很确定你无法跨越边界移动引用,你必须使用IntPtr(原始指针)。我所知道的方法是编组一组原始结构。