本篇文章为了熟悉在C#环境下如何调用C(C++)写的dll。
【转帖注明出处】
一、首选创建一个C的DLL工程,生产dll文件用来备用,步骤如下:
1、VS2010创建C++项目内选择Win32的应用程序,名称为CreateCDll,在应用程序设置界面内选择DLL,如下图:
2、在上图中在附件选项内选择【导出符号】点击完成,便生成了C++的dll工程,如下图
可以看到选择了导出符号的好处是工程自动帮我们创建了CREATECDLL_API的宏,这个宏是什么呢,就是我们要导出到DLL用的关键字可以按F12看CREATECDLL_API的原定义:
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 CREATECDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// CREATECDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef CREATECDLL_EXPORTS
#define CREATECDLL_API __declspec(dllexport)
#else
#define CREATECDLL_API __declspec(dllimport)
#endif
上图中源文件内有三个文件,第一个CreateCDll.cpp就是我们要写的DLL接口函数源文件(工程自动帮我们创建了三个范例一个变量一个函数一个类,为了简单我们可以删除这三段代码),dllmain.cpp类似于dll的main入口,stdafx.cpp就不做介绍了,好了我们开始在CreaeCDll.cpp内添加我们的接口函数,为了测试我们简单的写两个函数,一个是求两个数的和函数,第二个是比较两个数的大小,并输出最大值,好到此源码如下:
// CreateCDll.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "CreateCDll.h"
// 求和
CREATECDLL_API int fnAdd(int num1,int num2)
{
return num1 + num2;
}
// 求最大值
CREATECDLL_API int fnMax(int num1,int num2)
{
return (num1 > num2)?num1:num2;
}
3、OK到此我们两个接口已经写好,我们把工程编译一下,可以看到在工程的Debug目录下已经生产了DLL和LIB文件,如下图:
4、好了第三步我们已经生产了DLL,下面我们要创建C#的工程来调用刚生成的DLL,我们创建一个C#窗口应用程序名称为ImportCDll,保存,添加如下界面:
5、把刚生成的dll文件拷贝到C#工程的DEBUG目录下,然后在C#内完成如下代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ImportCDll
{
public partial class Form1 : Form
{
// 导入求和dll内函数接口
[DllImport("CreateCDll.dll",EntryPoint="fnAdd")]
private static extern int fnAdd(int num1, int num2);
// 导入求最大值dll内函数接口
[DllImport("CreateCDll.dll", EntryPoint = "fnMax")]
private static extern int fnMax(int num1, int num2);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
// 求和按钮事件
private void button1_Click(object sender, EventArgs e)
{
textBox3.Text = string.Format("{0}",fnAdd(Convert.ToInt32(textBox1.Text),Convert.ToInt32(textBox2.Text)));
}
// 求最大值按钮事件
private void button2_Click(object sender, EventArgs e)
{
textBox4.Text = string.Format("{0}", fnMax(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text)));
}
}
}
6、如上代码已经完成相应操作参见代码内注释,需要注意的是需要添加:using System.Runtime.InteropServices;引用才能使用dll,好了编译运行,当我们点击求和或者最大值的时候发现报错了如下图:
提示找不到函数入口???怎么回事很纳闷!!!为了确认是否dll内真的没有我们两个函数的入口,我们使用PE Explorer来打开我们的CreateCDll.dll文件查看里面的函数入口名称,发现如下图:
可以发现我们函数名字被改变了分别变成了:?fnAdd@@YAHHH@Z(对应fnAdd)和?fnMax@@YAHHH@Z(对应fnMax),怎么回事,原来编译器在编译的时候会自动的在我们的函数上加上标志字符,因此我们需要在做dll文件的时候使用def文件来告诉编译器不要改变我们的接口名称,怎么做呢?如下。
7、如上的错误需要在刚才的C的dll工程内添加def文件来告诉编译器不要修改我们的函数接口名称,在工程上右击【添加】---->【新建项目】,选择【模块定义文件(.def)】,如下图:
输入mydef,点击添加,在mydef文件下添加如下代码:
LIBRARY "CreateCDll"
EXPORTS
fnAdd
fnMax
LIBRARY 后面添加你的dll名称,注意不需要加.dll;在 EXPORTS下面直接添加你的函数名称,不需要引号见上面代码示例。
好了到此我们再把dll的工程编译一下,然后用PE Explorer看一下编译好的dll的函数名称,发现如下:
名称已经正确好,OK再次拷贝到我们C#工程的debug目录下,再次编译,运行。
8、运行重新编译,再次点击求和或者最大值按钮,又报错了…………,报错界面如下:
经过百度-google了一下,发现在dll调用的时候需要把【CallingConvention】的调用方式值修改为【CallingConvention.Cdecl】,默认好像是采用【StdCall】的方式;经修改代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ImportCDll
{
public partial class Form1 : Form
{
// 导入求和dll内函数接口
[DllImport("CreateCDll.dll", EntryPoint = "fnAdd", CallingConvention = CallingConvention.Cdecl)]
private static extern int fnAdd(int num1, int num2);
// 导入求最大值dll内函数接口
[DllImport("CreateCDll.dll", EntryPoint = "fnMax", CallingConvention = CallingConvention.Cdecl)]
private static extern int fnMax(int num1, int num2);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
// 求和按钮事件
private void button1_Click(object sender, EventArgs e)
{
textBox3.Text = string.Format("{0}", fnAdd(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text)));
}
// 求最大值按钮事件
private void button2_Click(object sender, EventArgs e)
{
textBox4.Text = string.Format("{0}", fnMax(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text)));
}
}
}
9、再次编译运行,OK了效果如下:
OK,到此结束,希望对新手有帮助。