我如何编组一个向量来自 C++ dll 到 C# 应用程序?

发布于 2024-08-30 18:44:11 字数 980 浏览 2 评论 0原文

我有一个 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# 中编组对象,但这似乎太复杂并且解释得太少。整数向量是执行此操作的正确方法吗?还是还有其他技巧或方法?

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::vector<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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ら栖息 2024-09-06 18:44:11

STL 是一种特定于 C++ 的库,因此您无法将其作为一个对象直接传递给 C#。

std::vector 可以保证的一件事是 &v[0] 指向第一个元素,并且所有元素线性地位于内存中(换句话说,就内存布局而言,它就像 C 数组)

所以marshal 作为 int 数组...这应该不难 - 网上有很多例子。

添加了

假设您仅将数据从 C++ 传递到 C# :

C# 无法处理 C++ 向量对象,因此不要尝试通过引用传递它:相反,您的 C++ 代码必须返回指向整数数组的指针...

如果您不打算这样做要从多个线程使用此函数,您可以使用静态存储:

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 中的内存。

为了简单起见,不要传递矢量数据的大小同样,只需在末尾放置一个哨兵值(例如 -1),以便 C# 代码可以检测数据结束的位置。

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.

ぽ尐不点ル 2024-09-06 18:44:11

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

在 C# 中包装 std::vector 确实可以仅使用常规 P/Invoke Interop,但很复杂。甚至任何类型的 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-我们/library/system.runtime.interopservices.icustommarshaler.aspx

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

浪漫人生路 2024-09-06 18:44:11

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文