正文开始:

C#是一种托管(managed)的面向对象语言,而C++默认为非托管(native)。因此由C++生成的dll文件也应分为

托管C++dll与非托管C++dll两种。以下将分别说明。

1. 生成与使用托管C++dll

什么是托管C++?

托管是.NET的一个专门概念,它倡导一种新的编程理念,因此我们完全可以把“托管”视为“.NET”。由托管概念所引发

的C++应用程序包括托管代码、托管数据和托管类三个组成部分。
    托管代码
.Net环境提供了许多核心的运行(RUNTIME)服务,比如异常处理和安全策略。为了能使用这些服务,必须要给运行环

境提供一些信息代码(元数据),这种代码就是托管代码。所有的C#、VB.NET、JScript.NET默认时都是托管的,但

Visual C++默认时不是托管的,必须在编译器中使用命令行选项(/CLR)才能产生托管代码。 
    托管数据
与托管代码密切相关的是托管数据。托管数据是由公共语言运行的垃圾回收器进行分配和释放的数据。默认情况下,

C#、Visual Basic 和 JScript.NET 数据是托管数据。不过,通过使用特殊的关键字,C# 数据可以被标记为非托管数

据。Visual C++数据在默认情况下是非托管数据,即使在使用 /CLR 开关时也不是托管的。
    托管类
尽管Visual C++数据在默认情况下是非托管数据,但是在使用C++的托管扩展时,可以使用“__gc”关键字将类标记为

托管类。就像该名称所显示的那样,它表示类实例的内存由垃圾回收器管理。另外,一个托管类也完全可以成为

.NET 框架的成员,由此可以带来的好处是,它可以与其他语言编写的类正确地进行相互操作,如托管的C++类可以

从Visual Basic类继承等。但同时也有一些限制,如托管类只能从一个基类继承等。

由上可以看到,所谓的托管C++与非托管C++主要的区别在于二者安全策略与内存管理策略的不同。托管的C++采用

与Java类似的内存管理,简单的例子就是托管中只有New而没有delete,对于习惯了非托管C++的人来说接受这一点

是一个由繁到简的过程,比较容易的过程。另外需要强调一点,在托管C++中只有一种数据类型,即类型,类型包括

值类型、引用类型与指针类型。我们常用的类可以是引用类型或者是值类型,而结构、枚举与基本类型都是值类型。

二者区别于数据存储位置不同,前者位于托管堆(heap)上,后者存储在堆栈(stack)上。为了与非托管C++区分,

在托管C++中使用gcnew关键字,并用^取代*,例如,有托管类TestManaged与非托管类TestNative,分别使用如下

实例化。

TestManaged ^t = gcnew Testmanaged()//托管类
TestNative * t = new TestNative()//非托管类

指针类型很少使用,只有在对非托管类进行封装时会用到。

封装非托管C++类

假设现有一非托管类avplayer,这是一个真实的类库,类头文件如下

//
// avplayer.h
// ~~~~~~~~~~
//#ifndef __AVPLAYER_H__
#define __AVPLAYER_H__#ifdef API_EXPORTS
#define EXPORT_API __declspec(dllexport)
#else
#define EXPORT_API __declspec(dllimport)
#endif#pragma once// 打开媒体类型.
#define MEDIA_TYPE_FILE 0//rtp
#define MEDIA_TYPE_BT   1
#define MEDIA_TYPE_HTTP 2//ts
#define MEDIA_TYPE_RTSP 3
#define MEDIA_TYPE_YK   4// 渲染模式.
#define RENDER_DDRAW    0
#define RENDER_D3D      1
#define RENDER_OGL      2
#define RENDER_SOFT     3
#define RENDER_Y4M      4class player_impl;
// avplayer封装类.
class EXPORT_API avplayer
{
public:avplayer(void);~avplayer(void);public:// 创建窗口, 也可以使用subclasswindow附加到一个指定的窗口.HWND create_window(const char *player_name);// 打开一个媒体文件// movie 文件名.// media_type 表示打开的媒体类型.// render_type 表示播放渲染模式, 默认是ddraw渲染.// 注意, 这个函数只打开文件, 但并不播放, 重新打开文件前, 必// 须关闭之前的媒体文件, 否则可能产生内存泄漏! 另外, 在播放// 前, avplayer必须拥有一个窗口.BOOL open(const char *movie, int media_type, int render_type = RENDER_SOFT);// 开始播放视频.// fact 表示播放视频的起始位置, 按视频百分比计算, 默认从文件头开始播放.// index 播放索引为index的文件, index表示在播放列表中的位置计数, 从0开始计// 算, index主要用于播放多文件的bt文件, 单个文件播放可以使用直接默认为0而不// 需要填写参数.//BOOL play(double fact = 0.0f, int index = 0);BOOL play();/*..................*/<pre name="code" class="cpp">private:player_impl *m_impl;
};#endif // __AVPLAYER_H__
</pre><pre>

现要实现对上述类的封装,主要涉及数据类型转换,关键点在注释处给出.

数据转换对照表:

// avplayer_cplus_managed.h
#include "avplayer.h"
#pragma comment(lib,"avcore.lib")
#pragma onceusing namespace System;//托管类中应广泛使用命名空间namespace avplayer_cplus_managed {//必须定义命名空间public ref class avplayer_managed//声名为ref class类型(引用类型){// TODO:  在此处添加此类的方法。public:avplayer_managed(){m_avplayer_c = new avplayer;//由于avplayer类为非托管类,所以只能使用指针类型。};~avplayer_managed(){delete m_avplayer_c;//new出的必须delete,否则内存泄漏};enum class MediaType//自定义枚举,也是值类型{RTP = 0,TS = 2};enum class RenderType{DDRAW = 0,D3D,OGL,SOFT,Y4M};public:// 创建窗口, 也可以使用subclasswindow附加到一个指定的窗口.System::IntPtr create_window(System::String ^player_name);HWND类型对应IntPtr// 打开一个媒体文件// movie 文件名.// media_type 表示打开的媒体类型.// render_type 表示播放渲染模式, 默认是ddraw渲染.// 注意, 这个函数只打开文件, 但并不播放, 重新打开文件前, 必// 须关闭之前的媒体文件, 否则可能产生内存泄漏! 另外, 在播放// 前, avplayer必须拥有一个窗口.System::Boolean open(System::String ^ movie, MediaType media_type, RenderType render_type/* = RENDER_SOFT*/);//String为托管类,因此使用^代替*// 开始播放视频.// fact 表示播放视频的起始位置, 按视频百分比计算, 默认从文件头开始播放.// index 播放索引为index的文件, index表示在播放列表中的位置计数, 从0开始计// 算, index主要用于播放多文件的bt文件, 单个文件播放可以使用直接默认为0而不// 需要填写参数.//BOOL play(double fact = 0.0f, int index = 0);System::Boolean play();使用Boolean代替BOOL/*...................*/private:!avplayer_managed(){delete m_avplayer_c;}private:avplayer* m_avplayer_c;};
}

实现文件

// 这是主 DLL 文件。
//<span style="font-family: Arial, Helvetica, sans-serif;">avplayer_cplus_managed.cpp</span>#include "stdafx.h"#include "avplayer_cplus_managed.h"System::IntPtr avplayer_cplus_managed::avplayer_managed::create_window(System::String ^player_name)
{char *t = (char *)(void*)Marshal::StringToHGlobalAnsi(player_name);//将String转换为char*System::IntPtr hwnd(m_avplayer_c->create_window(t));//HWND可直接转换为IntPtrMarshal::FreeHGlobal(IntPtr(t));return hwnd;
}System::Boolean avplayer_cplus_managed::avplayer_managed::open(System::String ^ movie, MediaType media_type, RenderType render_type/* = RENDER_SOFT*/)
{char *t = (char *)(void*)Marshal::StringToHGlobalAnsi(movie);System::Boolean b = m_avplayer_c->open(t, (int)media_type, (int)render_type);Marshal::FreeHGlobal(IntPtr(t));return b;
}System::Boolean avplayer_cplus_managed::avplayer_managed::play()
{return m_avplayer_c->play();
}

上述工作做好后,avplayer_cplus_managed已经是托管类,将上述工程生成CLR DLL文件。在C#项目把DLL文件直接添加引用,就可调用avplayer_cplus_managed类。

C#项目中调用源码

using avplayer_cplus_managed;//使用avplayer命名空间namespace avplayer_csharp
{public partial class mediaPlayer : Form{private avplayer_managed m_avplayer;private Boolean m_bPlaying;private Form m_childForm;public mediaPlayer(){m_avplayer = new avplayer_managed();//实例化avplayerm_bPlaying = false;InitializeComponent();}private void start_Click(object sender, EventArgs e){bool b = m_avplayer.open("dvs6467_p2p.sdp",avplayer_managed.MediaType.RTP,avplayer_managed.RenderType.SOFT);if(b){ThreadStart startPlayer = new ThreadStart(playThread); //线程起始设置Thread playerThread = new Thread(startPlayer); //实例化要开启的新类playerThread.Start();//开启线程m_bPlaying = true;}}//播放器线程private void playThread(){if (m_avplayer.play())//成功则等待,不成功返回m_avplayer.wait_for_completion();elsereturn;}
}

2 直接使用非托管C++DLL

这种方法缺点是只能使用DLL中export的函数。主要步骤:

[DllImport("Dll文件名"),entryPoint = "函数名",CharSet]
public static extern 函数声名

网上多为此类方法,可自行查找

以下转自http://blog.csdn.net/zhangzxy161723/article/details/4132853

C#语言使用方便,入门门槛较代,上手容易,并且语法与C,java有很类似的地方,IDE做的也好,通用性好,是MS下一代开发的主要力量.但是其开源代码较少,类库不是十分完美,在架构方面还有一些需要做的工作.C++写的程序占用内存较小,直接对内存或者文件操作,因此一些关键的步骤或者一些最内层的循环在一定程序上还需要依赖C++.
对于一直做c++开发的人员,使用c#来做界面将是一个很好的选择。
c#调用DLL用两种方式:直接导出函数;使用def文件导出函数。

1.下面介绍一个不采用def导出文件的简单例子
第一步,用C++做一个可以导出函数的dll(不采用def文件)
cxyMath.h
//在这里定义导出哪一些函数

class MyMathFuncs
{
public:// Returns a + bstatic __declspec(dllexport) double Add(double a, double b);// Returns a - bstatic __declspec(dllexport) double Subtract(double a, double b);// Returns a * bstatic __declspec(dllexport) double Multiply(double a, double b);// Returns a / b// Throws DivideByZeroException if b is 0static __declspec(dllexport) double Divide(double a, double b);
};

cxyMath.cpp的实现就很简单了,代码附在上传的文件中,在这里就不贴代码了,编译成dll后,拷贝dll,lib文件到C#的工程中的debug的目录下(如果你写的是release版,请将dll,lib拷贝到relase文件夹下)

第二步:找出导出的函数名
写成如下形式,方便CS的调用
不采用def文件导出的函数名有些奇怪,但还是可以看出函数的层次,?函数名@类名@命名空间@@******,
找函数名可以使用ultraedit32,打开lib文件,就可看到了
另外,我们可以使用dllexp这个程序找出导出的函数(这个程序见附录)

[DllImport("cppdll.dll",EntryPoint="?Divide@MyMathFuncs@MathFuncs@@SANNN@Z",CharSet = CharSet.Auto)]
public static extern double Divide(double a,double b);
[DllImport("cppdll.dll",EntryPoint="?Multiply@MyMathFuncs@MathFuncs@@SANNN@Z",CharSet = CharSet.Auto)]
public static extern double Multiply(double a,double b);

第三步:调用

private void button1_Click(object sender, System.EventArgs e)
{MessageBox.Show(Multiply(12,13).ToString());
}

2.采用def 文件导出函数

第一种方式比较简单,但是找一个dll函数的入口地址,还是比较麻烦的,并且,入口地址没有太大的意义,不直观,不好记忆
一般情况下,我们可以选择使用def文件导出函数
第一步,新建一个win32 application然后在应用程序的设置中选择动态dll,然后选择导出符号,这样,vs2003就为我们生成了一个非常完整的架子,但是美中不足的是生成的dll导出的函数也是和第一中情况一样
第二步,添加一个def文件,生成def文件的同时,vs2003自动为我们添加了这样一行,
LIBRARY win32dll
我们只要在他的下面加上我们要导出的函数就可以了.
   GetAName   @1
   ShowMyName @2
   PerfTest   @3
这样经过编译我们使用dllexp查看,看到的就不再是一些没有意义的函数名了,而是我们在def中定义的文件函数名
第三步,拷贝lib,dll文件到CS工程中就可以了,
我们就不在这里一一叙述了

C#调用成功调用C++ dll笔记相关推荐

  1. JNA实战笔记汇总一 简单认识JNA|成功调用JNA

    一.简介 先说JNI(Java Native Interface)吧,有过不同语言间通信经历的一般都知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可.首先看下 ...

  2. QT调用C#写的Dll

    QT调用C#写的Dll 参见: https://blog.csdn.net/weixin_42420155/article/details/81060945 C#写的dll是没有dllMain入口函数 ...

  3. C#调用非托管C++DLL:直接调用法

    在实际软件开发过程中,由于公司使用了多种语言开发,在C#中可能需要实现某个功能,而该功能可能用其他语言已经实现了,那么我们可以调用其他语言写好的模块吗?还有就是,由于C#开发好的项目,我们可以利用re ...

  4. pb调用c语言dll,PB调用C#编写的Dll类库

    在c# 中编写com组件,供PB调用实例 前言:c#中写的dll直接是不能被pb调用的,只有写成com组件才可以调用,所以用c#写dll时要注意. c#中新建类库 类库类型为通用类库,项目名为AddC ...

  5. QT调用C++写的Dll

    C#写的dll是没有dllMain入口函数的,是一种中间语言,需要.Net运行时进行做本地化工作,因此如果要调用C#写的dll,需要依赖.Net运行时,然而Qt中还无法直接调用.Net运行时,最好的方 ...

  6. C++ 常见错误(00) —— C#调用c++做的dll是报错

    C#调用c++做的dll是报错"System.BadImageFormatException:"试图加载格式不正确的程序. (异常来自 HRESULT:0x8007000B)&qu ...

  7. vs2015制作供js调用的ATL组件dll库详解

    ATL组件和mfc ActiveX组件都可以供网页去调用,在我其它的博客中详细讲述了ocx组件的开发方式以及在网页中如何调用,在这篇博客中将详细讲解ATL组件的开发以及在网页中的调用. 1 创建ATL ...

  8. wasm转c调用与封装至dll案例

    wasm转c调用与封装至dll案例 准备工作 初级 猿人学练习题 中级 崔大网习题 高级 某视频网站 准备工作 相关文档: 1.某德地图矢量瓦片逆向(快速wasm逆向),执行wasm2c翻译出来的c代 ...

  9. C#调用银联接口 posinf.dll方法调用

    开发调用pos机接口收银时需调用银联提供的接口posinf.dll里面的方法 尝试过多种调用方式均无效,最终在多方沟通后成功调用,分享如下 .net代码 声明调用方法: [DllImport(&quo ...

最新文章

  1. 马上 2018 年了,该不该下定决心转型AI呢?
  2. k8s多master建议用几个_Kubernetes 教程之跟着官方文档从零搭建 K8S
  3. Markdown 进阶
  4. (11)VHDL例化system Verilog
  5. spark学习-54-Spark RDD的clean()方法
  6. 日历控件,可运行在XHTML1.0下
  7. Windows Server 2008首选项
  8. 一篇牛B的 纹理映射 大全
  9. 【扩频通信】基于matlab GUI扩频通信系统仿真【含Matlab源码 772期】
  10. ppp协议 服务器,PPP上的认证协议
  11. 机顶盒两个灯出现红色
  12. Vue后台管理系统模板推荐
  13. PS用套索工具抠图,并修改背景颜色
  14. Java SE学习笔记
  15. 1054: 猴子吃桃(C)
  16. 美术绘画之原画场景深入刻画-张聪-专题视频课程
  17. POJ 3107 Godfather (树的重心)
  18. seurattogiotto中的python环境设置
  19. 微博生态圈盈利模式分析
  20. 什么是纠删码(与纠错码的区别)|纠删码与副本对比|LDPC码

热门文章

  1. 终于搞清楚开漏输出和推挽输出这个鬼东西
  2. CNN入门之cnn架构和cnn卷积、采样
  3. 《白帽子讲web安全》第3章 跨站脚本攻击(XSS)
  4. 2019浦发银行郑州分行实习生招募经历
  5. 怎么检测细胞是否受支原体污染?
  6. 矩母函数(Moment Generating Function)
  7. java实战项目源码-模仿天猫商城网站
  8. 机器学习(周志华)——决策树
  9. 台式电脑怎么调分辨率_台式电脑分辨率怎么调(分辨率调多少合适)
  10. crlf换行 idea_Git中换行符(CRLF、LF和CR)的转换