C++/CLI 混合模式 DLL 创建

Posted

技术标签:

【中文标题】C++/CLI 混合模式 DLL 创建【英文标题】:C++/CLI Mixed Mode DLL Creation 【发布时间】:2011-02-11 02:09:25 【问题描述】:

我有一个本机 C++ DLL,我想要一个 C++/CLI 包装层。据我了解,如果你简单地在项目中添加一个 C++/CLI 类,VS 会编译为混合模式,但我显然错了,因为 VS 似乎甚至没有触及托管代码。

所以,给定一个预先存在的本机代码库究竟,你需要做什么来创建一个混合模式 DLL,以便我可以链接到那个任何 .NET 语言的代码?

*我需要这样做,因为我的本机代码使用了我无法 P/Invoke 的 C++ 类。

【问题讨论】:

你想将现有的 C++ 源代码编译成一个新的 DLL,还是你想制作一个使用旧本机代码 DLL 的 C++/CLI 类库? @kmontgom - 拥有一个 DLL 会很好,但老实说,我宁愿做任何最佳实践。 【参考方案1】:

嗯,不,在您告诉 C++/CLI 编译器您的旧版 DLL 是用非托管代码编写的之前,它不会成为混合模式。这应该很明显,您应该从非托管 DLL 导出中得到链接器错误。您需要使用#pragma managed:

#pragma managed(push, off)
#include "oldskool.h"
#pragma comment(lib, "oldskool.lib")
#pragma managed(pop)

using namespace System;

public ref class Wrapper 
private:
    COldSkool* pUnmanaged;
public:
    Wrapper()  pUnmanaged = new COldSkool; 
    ~Wrapper()  delete pUnmanaged; pUnmanaged = 0; 
    !Wrapper()  delete pUnmanaged; 
    void sampleMethod()  
        if (!pUnmanaged) throw gcnew ObjectDisposedException("Wrapper");
        pUnmanaged->sampleMethod(); 
    
;

【讨论】:

我认为这是因为您正在创建一个新的 DLL 并链接到完全原生的 DLL? 是否拉入原生 DLL 取决于 oldskool.lib 是导入库还是静态库。 @AdamHaile:正如 Ben Voigt 所暗示的,此答案中提出的模式可用于生成真正的混合模式程序集,其中包含本机(非托管)对象代码以及 CLR-可见的托管类型。 ref class Wrapper 将调用转发给非托管实现,该实现可以驻留在单独的模块中,也可以编译到同一个模块中。 @BenVoigt 如果一个人有一个第三方静态 C++ 库并希望在其上创建一组 DLL 类供 C# 使用,这种方法是一种好方法吗? 没关系。而且你也没有选择的余地,静态库不会神奇地变成 DLL。【参考方案2】:

防止 /clr 影响现有代码的一个好选择是将所有现有代码编译到本机静态库中,然后在 C++/CLI dll 的链接步骤中包含该静态库。

【讨论】:

【参考方案3】:

开始一个新的 C++/CLI 项目,然后将您的本机类移至该项目。

【讨论】:

【参考方案4】:

无需在项目级别打开“公共语言运行时支持”,只需查看文件的属性并转到 C/C++ | 就可以逐个文件启用它。一般 |通用语言支持。

这可能会使您的本机代码和 C++/CLI 代码更容易放在同一个项目中,而不是创建一个单独的 C++/CLI DLL,只包含包装器,或者必须使用大量托管/非托管 pragma。

因此,只需在您要编写的 C++/CLI .NET 包装类上执行此操作即可。

【讨论】:

【参考方案5】:

如果您有原生 C++ 的 DLL 源代码,则可以在混合模式下使用托管 C++。一段时间以来,Microsoft 有一个将一些著名的 DirectX 游戏迁移到 .NET 的参考项目。一种在混合模式下使用托管 C++。部分代码被重写为托管代码。一部分很快被更改为在混合模式下编译为 C++,一部分被编译为汇编代码(由于性能原因),但也直接在托管代码内部用作不安全代码。这种迁移结果在最终应用程序中获得了非常好的性能。通过这种方式,您不必花时间在本机代码和托管代码之间进行封送处理。安全和不安全托管代码之间的编组非常快。或许你也应该选择这种方式?

另一种从托管 .NET 代码中的 DLL 调用本机代码的方法是众所周知的。每个 C++ 函数都有未修饰的名称(使用 http://www.dependencywalker.com/ 来查看)。如果您的 C++ DLL 导出类而不是类 C 函数,则此 DLL 设计不佳。设计良好的 DLL 要么导出类 C 函数,要么导出 COM 接口。如果你有这样“坏”的 DLL 并且不想花时间写一个 COM,你可以很容易地再写一个 DLL 来扮演 stub 的角色。此 DLL 从“坏”DLL 导入所有 C++ 类(例如参见 http://msdn.microsoft.com/en-us/library/81h27t8c.aspx、Exporting a C++ class from a DLL 和 http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx)并导出类 C 函数。这种方式也可以。

【讨论】:

抱歉,这是如此不知情,我不得不对它投反对票。 “每个 C++ 函数都有未修饰的名称” - 嗯...不。其余的完全没用,以至于我无法找到一个很好的解释来解释这个提议的答案收到的赞成票。除了指出 COM 是一个有效的互操作策略之外,其他一切要么具有误导性,要么完全错误。我会建议删除(认真)。 @IInspectable:首先看答案的日期。我不清楚你有哪种情况以及你在寻找什么?在我看来,问题的根源是好的,语言和编译器独立,从 C++ 导出对象和方法到 DLL 中。 COM 为这个问题提供了很好的解决方案。在我的选项中,混合 C++/CLI 创建的良好设计将包括以 COM dll 的形式进行 C++ 开发以及在 C# 中使用 DLL 或在 C# 中开发 COM 以及在 C++ 中使用 C# DLL。 就像你的回答一样,这条评论完全无法理解。无论如何,就托管/非托管互操作而言,在您发布此建议的答案和今天之间,没有任何变化。绝对没有必要引入 COM 来将非托管功能包装在 ref class 中(参见 highest voted answer)。您似乎不了解或不了解 C++/CLI。换句话说:如果你只有一把锤子,最终每个问题都开始看起来像钉子。抱歉,这不是一个有用的贡献。 有趣的是,您假设网名 IInspectable 的人不熟悉 COM。无论如何,使用 COM 是乏味的。您必须编写 IDL 文件、实现 COM 对象、注册它或实现免注册 COM。很多工作,没有明显的好处。使用 C++/CLI 编写互操作程序集要容易得多。您的其他 .NET 代码可以立即使用混合模式程序集。听起来确实像你不懂 C++/CLI(你说的是“托管 C++”,当你回答这个问题时,它已经死了大约十年)。 您的问题到底是什么?你写了一个答案,那是一英里多。我投了反对票,并解释了我的投票。你首先坚持,你不会错,然后你变得个人化,现在你失去了兴趣。如果是这种情况,我强烈建议删除此答案。它基于如此少的知识,因此很自然地会出错。如果你不想讨论这个,干脆不要顶嘴,或者默默地删除这个答案。【参考方案6】:

C++ 项目文件需要 /clr 选项。我相信,这可以在常规选项卡上为整个项目设置,也可以在单个文件上设置。

一旦指定了 clr 选项,Visual Studio 将使用 C++/CLI 构建该类。

【讨论】:

仅此而已,无法让 .NET 访问本机(非托管)代码。 /clr 开关仅允许使用 C++/CLI 语言扩展,可用于对 CLR 可见的接口进行建模以供 .NET 代码使用。它不会自动将本机代码编译成其他东西(非托管代码仍然编译为本机目标代码,而不是 MSIL)。

以上是关于C++/CLI 混合模式 DLL 创建的主要内容,如果未能解决你的问题,请参考以下文章

混合模式 C++/CLI 崩溃:atexit 中的堆损坏(静态析构函数注册)

如何处理混合模式项目中的打印?

混合模式调试(C++、C#、VB)

在调试期间进入 c++/CLI 包装器 dll 和 c# dll,同时在 firebreath 上制作插件

避免在 C++/CLI 项目中加载 .Net Dlls?

MFC/C#.NET混合模式本地化(多语言/多语言应用)