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

14
我有一个本地的C++ DLL,我想要一个C++/CLI封装层。按照我的理解,如果你只是将一个C++/CLI类添加到项目中,VS会以混合模式进行编译,但是显然我错了,因为VS似乎甚至没有接触托管代码。
那么,在给定现有本机代码库的情况下,需要逐步执行哪些操作来创建混合模式DLL,以便我可以从任何.NET语言链接到该代码?
*我需要这样做是因为我的本机代码使用了我无法P/Invoke的C++类。

你想将现有的C++源代码编译成新的DLL,还是要创建一个使用旧的本地代码DLL的C++/CLI类库? - kmontgom
1
@kmontgom - 拥有一个单一的DLL会很不错,但实话实说,我更愿意遵守最佳实践。 - Adam Haile
6个回答

19

不,只有在告诉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,对吗? - Adam Haile
1
是否引入本地 DLL 取决于 oldskool.lib 是导入库还是静态库。 - Ben Voigt
正如Ben Voigt所建议的,此答案中提供的模式可用于生成包含本机(非托管)目标代码和CLR可见托管类型的真正混合模式程序集。ref class Wrapper将调用转发到非托管实现,该实现可以驻留在单独的模块中或编译为同一模块的一部分。 - IInspectable
@BenVoigt 如果有一个第三方静态C++库,并且想要在其上创建一组DLL类以供C#使用,那么这种方法是否可行? - Mateusz Grzejek
这根本不重要。而且你也没有选择,那个静态库不会魔法般地变成DLL。 - Hans Passant
@MateuszGrzejek:是的,一组C++/CLI包装器是让C#访问C++库的方法。但不要使用Hans的示例作为模板...编写智能指针(new/delete一次,并通过组合重用它。 - Ben Voigt

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

1
开始一个新的C++/CLI项目,然后将您的本地类移动到其中。

1

在项目级别上不开启“公共语言运行时支持”,而是通过查看文件属性并转到C/C++ | 通用 | 公共语言支持,可以仅按文件启用它。

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

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


0
如果您有一个使用本地C++编写的DLL源代码,您可以在混合模式下使用托管C++。微软公司已经有一段时间将一些知名DirectX游戏迁移到.NET的参考项目。其中一个使用了混合模式下的托管C++。代码的一部分被重写为托管代码。一部分代码被简短地更改为以C++混合模式编译,另一部分则被编译为汇编代码(因为性能原因),但也作为不安全代码直接用于托管代码中。这种类型的迁移最终会导致应用程序的真正良好性能。通过这种方式,您不需要在本地代码和托管代码之间进行调用转换。安全和不安全托管代码之间的调用转换速度更快。也许您也应该选择这种方式?

另一种从托管的.NET代码中调用DLL中本地代码的方法是众所周知的。每个C++函数都有未装饰的名称(使用http://www.dependencywalker.com/查看)。如果您的C++ DLL导出类而不是类似于C的函数,则该DLL设计不良。设计良好的DLL要么导出类似于C的函数,要么导出COM接口。如果您有这样的“糟糕”DLL并且不想花时间编写COM,则可以轻松编写另一个DLL,该DLL将扮演存根角色。此DLL从“糟糕”的DLL中导入所有C++类(例如,请参见http://msdn.microsoft.com/en-us/library/81h27t8c.aspx从DLL中导出C++类http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx),并导出类似于C的函数。这种方式也可以。


像你的答案一样,这个评论完全无法理解。无论如何,在托管/非托管互操作方面,从你发布这个提议的答案到今天,没有任何变化。绝对没有必要引入COM来包装ref class中的非托管功能(请参见得票最高的答案)。看起来你不了解或不理解C++/CLI。换句话说:如果你手上只有一把锤子,最终每个问题都开始看起来像一个钉子。很抱歉,这并没有提供有用的贡献。 - IInspectable
1
很有趣,你认为一个使用IInspectable这个屏幕名称的人会对COM不熟悉。无论如何,使用COM很繁琐。你需要编写IDL文件,实现COM对象,要么注册它,要么实现无需注册的COM。这是很多工作,没有明显的好处。使用C++/CLI编写互操作程序集要容易得多。你的其他.NET代码可以立即使用混合模式程序集。听起来你似乎不了解C++/CLI(你谈论的是“托管C++”,当你回答这个问题时已经过去大约十年了)。 - IInspectable
你完全没有理解C++/CLI的重点。你不需要暴露C++签名。你将C++类编译到同一个程序集中,通过ref class包装器公开托管接口。你不会遇到任何你试图用标准化ABI(在这种情况下是COM)解决的复杂问题。你坚持要解决的问题根本不存在。要看清这一点,你需要了解C++/CLI(显然你不懂)。我强烈建议你学习C++/CLI。 - IInspectable
@IInspectable:看一下6年前提出的问题。你会读到关于“本地C++ DLL”的内容。将“C++类编译到同一个程序集”是超出范围的。.Net程序集是COM思想的进一步发展。此外,我之前已经告诉过你,我对任何关于这个主题的讨论都没有兴趣。顺便说一句,C++已经不再是我的兴趣领域了很多年。20年前它很有趣,但今天不再是了。 - Oleg
1
你的问题是什么?你写了一个答案,但完全不对。我投了反对票,并解释了我的投票原因。你一开始坚持认为自己没错,接着就针对个人攻击,现在又失去了兴趣。如果是这样的话,我强烈建议你删除这个答案。它基于如此少的知识,所以错误是很自然的。如果你不想讨论这个问题,那就不要回复,或者默默地删除这个答案。 - IInspectable
显示剩余7条评论

0

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

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


仅有这些是不够的,无法使本地(非托管)代码可供.NET使用。/clr开关只是允许使用C++/CLI语言扩展,用于建模CLR可见接口以供.NET代码使用。它并不能自动将本地代码编译为其他形式(非托管代码仍然编译为本机目标代码,而不是MSIL)。 - IInspectable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接