如何在 C# 中封送集合以传递给本机 (C++) 代码

发布于 2024-10-19 09:01:55 字数 401 浏览 7 评论 0原文

我正在从事企业应用程序开发。整个应用程序都是用 C++ 开发的,除了 UI 是用 C# 开发的,现在是时候将 UI 与 C++ 代码挂钩了。经过详细研究,我选择了 PInvoke 来做到这一点。 一切都很成功,我唯一遇到的问题是如何将集合传递给 C++ 代码。 例如:

C# 侧代码

List<string> lst = new List<string>();
lst.Add("1");
lst.Add("2");
lst.Add("3");
lst.Add("4");

C++ 侧代码

std::vector<std::string> vStr;

现在我如何将 lst 传递给本机 C++ 代码

I am working on an enterprise application development. the entire application is developed in c++, except the UI, which is developed in c#, now its time to hookup the UI with c++ code. After detailed study i choose PInvoke in order to do so.
All is successful, the only case where i stuck is that how can i pass collection to C++ code.
e.g:

C# Side Code

List<string> lst = new List<string>();
lst.Add("1");
lst.Add("2");
lst.Add("3");
lst.Add("4");

C++ Side Code

std::vector<std::string> vStr;

Now how do i pass lst to native C++ code

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

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

发布评论

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

评论(3

银河中√捞星星 2024-10-26 09:01:56

正如 mzabsky 提到的,您无法封送这些类型。但是,您可以编组数组:

理论上的 C++ 导出:

extern "C" __declspec(dllexport) void __stdcall Foo(wchar_t const* const* values, int length)
{
    // Check argument validity...

    // If you really need a vector
    std::vector<std::wstring> vStr(values, values + length);

    //...
}

P/Invoke 签名:

[DllImport("foo.dll")]
static extern void Foo([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr)] string[] values, int length);

来自 C# 的调用:

Foo(lst.ToArray(), lst.Count);

请注意,我在这里使用 std::wstring;您可以使用 char 代替 wchar_t,使用 LPStr 代替 LPWStr,使用 std::string 代替 std::wstring。

请注意,这将从列表中分配一个数组,然后向量将复制该数组的内容。如果原始列表的大小很小,那么这个问题应该可以忽略不计。

编辑:修复标记(< 和 >)。

As mzabsky mentioned, you cannot marshal these types. You can, however, marshal an array:

The theoretical C++ export:

extern "C" __declspec(dllexport) void __stdcall Foo(wchar_t const* const* values, int length)
{
    // Check argument validity...

    // If you really need a vector
    std::vector<std::wstring> vStr(values, values + length);

    //...
}

The P/Invoke signature:

[DllImport("foo.dll")]
static extern void Foo([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr)] string[] values, int length);

The call from C#:

Foo(lst.ToArray(), lst.Count);

Note that I'm using std::wstring here; you could instead use char instead of wchar_t, LPStr instead of LPWStr, and std::string instead of std::wstring.

Note that this will allocate an array from the list and then the vector will copy the array's contents. If the original list is small in size, this should be of negligible concern.

Edit: fixing markup (< and >).

别忘他 2024-10-26 09:01:56

你不能这样做,只能编组 C 类型。您必须编写一个 C++/CLI 包装器(或围绕 C++ 向量的 C 包装器)。

查看此答案。

You can't do this, only C types can be marshalled. You will have to write a C++/CLI wrapper (or a C wrapper around the C++ vector).

See this answer.

初见终念 2024-10-26 09:01:56

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

在 C# 中包装 std::vector 确实可以仅使用常规 P/Invoke Interop,但它很复杂。甚至任何类型的 std::map 都可以在 C#/.NET 中完成。

从 .NET 世界实例化 C++ 对象的基本思想是从 .NET 分配 C++ 对象的精确大小,然后调用从 C++ DLL 导出的构造函数来初始化该对象,然后您将能够调用任何访问该 C++ 对象的函数,如果任何方法涉及其他 C++ 类,您还需要将它们包装在 C# 类中,对于具有原始类型的方法,您可以简单地 P/Invoke 它们。如果你只有几个方法需要调用,那会很简单,手动编码不会花很长时间。当您使用完 C++ 对象后,您将调用 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="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()
    {
        Marshal.FreeHGlobal(this.ptr);
    }
}

有关详细信息,请参阅以下链接,

C#/.NET PInvoke Interop SDK

(我是SDK工具的作者)

Yes. You can. Actually, not just 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.

The basic idea of instantiating a C++ object from .NET world is to allocate exact size of the C++ object from .NET, then call the constructor which is exported from the C++ DLL to initialize the object, then you will be able to call any of the functions to access that C++ object, if any of the method involves other C++ classes, you will need to wrap them in a C# class as well, for methods with primitive types, you can simply P/Invoke them. If you have only a few methods to call, it would be simple, manual coding won't take long. When you are done with the C++ object, you call the destructor method of the C++ object, which is a export function as well. if it does not have one, then you just need to free your memory from .NET.

Here is an example.

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="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()
    {
        Marshal.FreeHGlobal(this.ptr);
    }
}

For the detail, please see the below link,

C#/.NET PInvoke Interop SDK

(I am the author of the SDK tool)

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