从 C++ dll 编组导出的字符串向量

Posted

技术标签:

【中文标题】从 C++ dll 编组导出的字符串向量【英文标题】:Marshaling exported string vector from C++ dll 【发布时间】:2011-10-25 01:36:30 【问题描述】:

如何编组从 C++ dll 导出的字符串向量?我希望在我的 C# 程序中使用它们之前将它们分开。你能帮帮我吗?

【问题讨论】:

不能,C++ 对象不能直接在 C# 程序中使用。它们甚至不能在用不同编译器编译的另一个 C++ 程序中使用。 C++ 没有二进制标准。您需要使用 C++/CLI 语言编写的胶水代码来转换它们。一种知道如何编译本机 C++ 和托管代码的语言。 或者你可以导出一个使用char*数组而不是向量的函数。 这里的问题类似:***.com/questions/5102436/… @Hans C# 有MarshallAsAttribute... @ColeJohnson:很多结构都可以——至少 C++11 有一些关于所谓的“标准布局”结构的规则,这使得编组和编组更简单一些。使用结构/类的内容。但是,为了应用这些规则,您必须避免使类变得有用的许多因素,而不仅仅是原始数据的分组。如果您的类型使用virtual,具有引用类型或非标准布局类型字段,在继承链中的多个类中定义了成员,或者具有与其他字段不同的访问控制,那么您就是回到 IB 领域(或更糟)。 【参考方案1】:

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

在 C# 中包装 std::vector<any_type> 确实可以使用常规 P/Invoke 互操作,但它很复杂。甚至任何类型的 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工具的作者)

【讨论】:

如果您担心那些“根本不知道该怎么做” 的人的反对意见,那么您应该提供一个示例。如果你不解释怎么做,那么你就没有回答问题 非常感谢。我将举一个简单的例子来说明如何做到这一点。 刚刚添加了一个简单的例子。谢谢! 几个问题: (1) 编译器不需要导出使对象有用的成员函数;在构建 DLL 时,您必须以某种方式告诉它导出它们。如果 DLL 是第三方库……嗯。你被它提供的任何 API 困住了。 (2) C和C++的调用约定不一样,尤其是调用成员函数的时候。只是它们如何不同,是特定于实现的。有些人希望 *cx 中的 this,有些人可能希望它在堆栈上,等等。如果您的 DLL 是用 VS 编写的,这可能会起作用。还有其他编译器吗?嗯。 感谢您的评论。对于(1),不是这样,如果是模板类,所有实现都在头文件里面,即使没有在原始C++ DLL中导出,如果是C++类的实现,仍然可以在补充中导出在 CPP 文件中并且类没有被导出,即使从 C 或 C++ 中也无法正常访问它,对吧?对于 (2),这就是调用约定发挥作用的地方,您需要使用正确的调用约定来装饰 P/Invoke,它不仅适用于 C++ 函数,C 风格的函数也需要它。

以上是关于从 C++ dll 编组导出的字符串向量的主要内容,如果未能解决你的问题,请参考以下文章

C#编组来自C++ DLL的无符号字符返回值[重复]

C# 和 C++ dll - 编组字符串

创建要在 C# 中编组的 C++ Dll 的最佳实践 [关闭]

将引用类型从 C++ 编组到 C#

从 C# 到 C++ 的编组

从 c# dll 调用/编组字符串到非托管代码