【问题标题】:How to marshal collection in c# to pass to native (C++) code如何在 C# 中编组集合以传递给本机 (C++) 代码
【发布时间】:2011-07-03 09:53:51
【问题描述】:

我正在开发企业应用程序。整个应用程序都是用 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++ 代码

【问题讨论】:

    标签: c# c++ pinvoke marshalling


    【解决方案1】:

    正如 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。

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

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

    【讨论】:

      【解决方案2】:

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

      See this answer.

      【讨论】:

        【解决方案3】:

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

        在 C# 中包装 std::vector&lt;any_type&gt; 确实可以仅使用常规 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工具的作者)

        【讨论】:

        • 很抱歉。我会改变我的答案。请不要对正确答案投反对票。
        • 没问题。我会改变所有的。谢谢!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-14
        • 1970-01-01
        • 2013-04-07
        • 1970-01-01
        • 2012-08-21
        相关资源
        最近更新 更多