C#调用C(C++)dll示例教程,包含dll找不到函数入口的解决办法,包含C#dll调用方式不对出错的解决办法

本篇文章为了熟悉在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,到此结束,希望对新手有帮助。

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值