【问题标题】:How is marshalling performed when C++ code is called from C++/CLI?从 C++/CLI 调用 C++ 代码时如何执行编组?
【发布时间】:2011-03-30 09:03:51
【问题描述】:

根据this question,可以使用 C++/CLI无缝组合托管和非托管代码。我不太明白 - 托管和非托管之间不应该有编组吗?

例如,我有一个 InnerLibrary,它被编译为本机 C++ .dll,并发布了一个标头,以及从 InnerLibrary 调用代码的 C++/CLI OuterLibrary。会有编组吗?谁来实施它,成本是多少?

【问题讨论】:

    标签: .net c++-cli marshalling mixed-mode


    【解决方案1】:

    嗯,这是一个内置在 C++/CLI 编译器中的功能,称为 C++ Interop。您可能认为涉及的黑魔法要少得多。 JIT 编译器生成的机器代码与 C++ 编译器生成的机器代码完全相同。所有 .NET 值类型在 C++ 中都有直接等价物,因此不需要转换。它确实自动处理引用类型,你必须自己做。 pin_ptr,通常。

    真正所做的只是注入一些代码来处理从托管堆栈帧到非托管堆栈帧的转换。该代码将一个特殊的“cookie”放在堆栈上,由垃圾收集器识别。这可以防止它误入非托管框架并将非托管指针错误地识别为对象引用。该代码并不多,在 Release 构建中大约需要 5 纳秒,给予或接受。

    【讨论】:

    • KeyValuePair<string,string> 是一种值类型,但没有直接等效项。如果每个成员都与布局兼容(内在类型或其他与布局兼容的值类型),则值类型是布局兼容的。
    【解决方案2】:

    不必进行任何编组,因为 C++/CLI 能够发出直接进行调用的不安全代码。看看 Reflector 中的一些 C++/CLI 代码——它看起来与 C# 非常不同。

    这是 C# 不能做的事情(至少,没有unsafe 关键字和一些指针黑客),这也是纯模式 C++/CLI 不能做的事情(原因与C#)。

    .NET 不安全代码能够直接调用非托管函数;只是这种能力并不方便,除非通过 C++/CLI。

    【讨论】:

    • 我不明白。假设有人想从他的 C# 代码中调用 OuterLibrary(并且 OuterLibrary 将在内部调用 InnerLibrary)。他能在没有编组的情况下工作吗?
    • @sharptooth:仅当您创建可访问的公共方法并在内部调用您的 InnerLibrary 时。另请参阅我的回答,其中指出编组并不总是无缝的,即使使用 C++/CLI 也是如此。
    • @sharptool,使用 C++/CLI,您可以有效地自己编写编组代码
    【解决方案3】:

    编组是将非托管数据或调用引入托管环境的过程。它只是——可以这么说——两者之间的翻译。

    使用 C++/CLI,您可以混合搭配。这意味着,如果您直接使用您的库,使用 *.h 文件并使用传统的 C++ 代码,它将是非托管的并且没有封送处理。如果您使用 BCL 类或您自己的托管代码访问该数据,则您将手动添加封送层,但仅在需要时。即,LPTSTR 需要转换为托管字符串才能用作一个。使用 C++/CLI,您可以跳过这一步并坚持使用传统的 C++ 代码,创建更快、更宽松的代码,但代价是不使用安全、经过检查的托管代码。

    【讨论】:

      【解决方案4】:

      涉及编组,但您(即程序员)必须明确地进行。

      如果您的 C++CLI OuterLibrary 调用具有采用 System.String/System::String^ 的函数,则 C++ 类型系统要求您在将其传递给采用 const char* 的 InnerLibrary 函数之前执行类型转换。您必须自己进行转换 - 这就是编组。

      Microsoft 发布了称为C++ Support Library 的东西,它提供了有助于C++ C++CLI 交互的函数。

      【讨论】:

        【解决方案5】:

        这取决于所涉及的数据类型。

        intdouble 等内部类型(string 不符合条件)在本机代码和托管代码中具有相同的表示形式,不需要封送处理。内部类型的数组也以相同的方式布局(如果我们忽略元数据 .NET 存储,但这与数组内容是分开的)。

        使用所有成员都是内在类型的显式布局属性的值类型也是内存布局兼容的。

        如果数据存储在托管堆上的对象中,则可能需要固定(这适用于所有数组)。

        另一方面,类类型必须来回转换/翻译。

        【讨论】:

        • 如果intdouble等被装箱,那么它们的内部表示不同于原生C++版本的。
        • 不,不是。添加了元数据标头,但值的表示形式未更改,因此本机代码可以直接读取和写入值,除了将值固定到位之外,无需进行封送处理。
        【解决方案6】:

        这里有两点:

        1) 托管/非托管代码转换:每个转换都有其固定成本。当 C++/CLI 代码在单个程序集中编译时,编译器会尽可能地管理所有代码,以尽量减少此类转​​换。当从 C++/CLI 代码调用外部非托管 Dll 时,这种优化是不可能的。因此,至少在时间紧迫的部分中,尽量减少此类转​​换是一个好主意。在此处查看更多信息:http://msdn.microsoft.com/en-us/magazine/dd315414.aspx,性能和互操作边界的位置

        2) 参数编组。这取决于参数类型。有些参数不需要编组,例如像 int 这样的简单类型。字符串应该被编组。一些技巧,比如固定指针,可以防止简单类型数组编组。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-12-03
          • 2011-01-13
          • 1970-01-01
          • 1970-01-01
          • 2016-06-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多