说明:本文是本人在学习封装DLL时看到的,觉得说得很清楚,为了防止忘记,特意复制下来学习用,原文链接:https://blog.csdn.net/songyi160/article/details/51075023
在VS2015中先创建C#项目,然后再创建要编写的动态库DLL项目,这样做的好处是整个解决方案的编程环境是C#模式,这样就可以有很多智能的提示或快捷的编程方式在整个解决方案中都可以使用。
一:创建C#控制台应用程序:用于调用C++编写的DLL
(1)启动VS2015》文件》新建》项目,在弹出的新建项目对话框中按下图进行选择填写,先填写项目的名称,再修改解决方案的名称。
(2)项目创建后,整体效果如下图:
二:用C++创建DLL
下面仅仅创建了DLL,具体创建DLL的讲解请参考【在VS2015中用C++编写可被其它语言调用的动态库DLL】
(1)在解决方案上右击选择【添加】>【新建项目】
(2)在添加新项目对话框中,按下图进行选择填写
(3)在下图的创建向导对话框中进行如下选择
(4)在下图的创建向导对话框中进行如下选择
(5)项目创建后,整体效果如下图:
(6)在【头文件】上右击进行如下选择
(7)在添加新项对话框中进行如下选择填写
(8)在CreateDLL.cpp文件中添加对CreateDLL.h的引用并且添加下图所示测试代码
(9)在CreateDLL.h文件中添加下图所示测试代码
这里说明一下这个 “C” 这个就是为了能够以C的方式进行编译,所以才这样声明的,加了"C"之后就是让编译器按照C的语法去进行编译,而 “C” 后的关键字意思就是把你整个类按照C的格式输出函数。
(10)在【源文件】上右击进行如下选择
(11)在添加新项对话框中进行如下选择填写
(12)在CreateDLL.def文件中添加下图所示测试代码
这里对模块定义文件(.def)说明一下:
模块定义文件是一个有着.def文件扩展名的文本文件。它被用于导出一个DLL的函数,和__declspec(dllexport)很相似,但是.def文件并不是Microsoft定义的。一个.def文件中只有两个必需的部分:LIBRARY 和 EXPORTS。让我们先看一个基本的.def文件稍后我将解析之。
LIBRARY dll_tutorial DESCRIPTION "our simple DLL" EXPORTS Add @1 Function @2
第一行,''LIBRARY''是一个必需的部分。它告诉链接器(linker)如何命名你的DLL。
下面被标识为''DESCRIPTION''的部分并不是必需的,但是我喜欢把它放进去。该语句将字符串写入 .rdata 节[据 MSDN],它告诉人们谁可能使用这个DLL,这个DLL做什么或它为了什么(存在)。
再下面的部分标识为''EXPORTS''是另一个必需的部分;这个部分使得该函数可以被其它应用程序访问到并且它创建一个导入库。当你生成这个项目时,不仅是一个.dll文件被创建,而且一个文件扩展名为.lib的导出库也被创建了。
除了前面的部分以外,这里还有其它四个部分标识为:NAME, STACKSIZE, SECTIONS, 和 VERSION。我将不再在本文中涉及这些内容,但是如果你在Internet上搜索,我想你将找到一些东西(译注: MSDN2003上对模板定义文件各部分内容有详尽解释,请参阅)。另外,一个分号(;)开始一个注解
(13)先将解决方案切换到Release模式,再在CreateDLL项目名称上右击选择【生成】或【重新生成】
注:Release模式下生成的DLL才是最终的,Debug模式下生成的DLL有时会出问题
(14)在解决方案所在的目录中打开Release文件夹即可看到生成的DLL
(15)用Dependency Walker查看导出函数名是否正确,直接将CreateDLL.dll拖到Dependency Walker软件界面即可,如下图
(16)使用DLL函数查看器(3.5)查看导出函数名和参数数量是否正确,直接将CreateDLL.dll拖到DLL函数查看器(3.5)软件界面即可,如下图
三:用C#项目调用C++创建DLL
(1)将C#项目设置为启动项目,并且将解决方案设置为Debug模式
(2)在C#项目的program.cs文件中编写如下代码,代码中DLL的路径中的斜杠,测试发现左斜、右斜都可以,代码中的相对路径指的是:编译后的exe程序相对于DLL的路径
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace CSharpCallDLL { class Program { //””里的是路径 [DllImport(@"../../../Debug/CreateDLL.dll", EntryPoint = "test01", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)] extern static int test01(int a, int b, int c); [DllImport(@"../../../Debug/CreateDLL.dll", EntryPoint = "test02", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)] extern static int test02(int a, int b); static void Main(string[] args) { int r1 = test01(1, 2, 3); int r2 = test02(5, 2); Console.WriteLine("test01结果:" + r1.ToString()); Console.WriteLine("test02结果:" + r2.ToString()); Console.ReadKey(); } } }
(3)运行C#项目输出结果如下
(4)若想实现C#调用DLL时自动进入到C++项目设置的断点,需在C#工程右键【属性】->【调试】->【启动调试器】中选中【启动本机代码调试】
(5)在C++项目的源码中设置断点,在Debug模式下运行C#程序会自动跳到断点处,如下图,从中可以看到C#项目中的值已经传到C++项目中了