VS2010动态库dll的编译和MFC调用使用教程64位&32位

前言

本文主要介绍64位和32位dll的编译与MFC和控制台程序显式调用dll的教程。2019.09.05添加了复杂函数的导出教程。显示链接只需要.dll文件,但是这种调用方式不能调用dll中的变量或者类。至于优点无非就是方便与节省控件,既然选择了看这篇文章,优点吾不在赘述。
工具为VS2010/VS2015 (VS2010及更新版本都行)

1.xx.lib与xx.dll编译

1.1 新建dll工程

新建项目–win32项目–填写项目名(dlltest)–确定–下一步–应用程序类型:选择dll–附加选项:选择导出符号–完成。(如下图)
在这里插入图片描述
可以看到生成了一个dlltest.cpp 文件,这是dll应用程序的入口,注意它和普通工程的入口main函数不同,这个文件我们不需要修改。
在这个动态库中导出一个变量,一个类,一个函数,头文件dlltest.h如下:

#ifdef DLLTEST_EXPORTS
#define DLLTEST_API __declspec(dllexport)
#else
#define DLLTEST_API __declspec(dllimport)
#endif

class DLLTEST_API Cdlltest {
public:
	Cdlltest(void);
};

extern DLLTEST_API int ndlltest;

DLLTEST_API int fndlltest(void);

// 新增函数
//导出函数,加extern "C",是为了保证编译时,生成的函数名不变,这样才能动态调用dll时

extern "C" DLLTEST_API int test(void);

1.2 新增导出函数与dll编译(extern “C”)

在dlltest.cpp最后面添加代码:

DLLTEST_API int test(void)
{
	return 1109;
}

然后选择编译x64和86

在这里插入图片描述
在这里插入图片描述
然后点击生成解决方案(快捷键F7),因为dll没有入口函数不能直接运。下面图片即为正常编译。

在这里插入图片描述

1.3 新增导出函数与dll编译(xx.def)

.cpp文件代码不用改变。将.h中的extern “c” 删掉。.h代码为

#ifdef DLLTEST_EXPORTS
#define DLLTEST_API __declspec(dllexport)
#else
#define DLLTEST_API __declspec(dllimport)
#endif

class DLLTEST_API Cdlltest {
public:
	Cdlltest(void);
};

extern DLLTEST_API int ndlltest;

DLLTEST_API int fndlltest(void);
DLLTEST_API int test(void);

使用 .def 文件,在 C++ 文件中导出函数时,必须将修饰名放到 .def 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数,以避免编译器进行名称修饰。
如果需要将修饰名放到 .def 文件中,则可以通过使用 DUMPBIN 工具或 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与 DLL 的 .def 文件中的导出名相匹配。
在项目目录下右击新建dlltest.txt,输入代码如下,保存,重命名后缀为dlltest.def
在这里插入图片描述VS2010中右击项目-添加-现有项,点击dlltest.def;
项目—属性—连接器----如下图,添加dlltest.def。然后编译生成dll即可。
用depend.exe查看也发现导出函数名为test.
在这里插入图片描述.def文件的规则为:

(1)LIBRARY语句说明.def文件相应的DLL;

(2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);如
Add @ 1
Sub @ 2
  (3).def 文件中的注释由每个注释行开始处的分号 (😉 指定,且注释不能与语句共享一行。

2. dll的调用(Console)

显示调用主要使用WIN32 API函数LoadLibrary、GetProcAddress,举例如下:
新建win32 console工程,随意建就行。然后按照上述编译x64 dll一样,将此项目改为x64项目,将上述生成dll复制到工程目录下。

代码为:

#include "stdafx.h"
#include "windows.h"

typedef int (*FUN)();
int _tmain(int argc, _TCHAR* argv[])
{
	HINSTANCE hInstance= LoadLibrary (TEXT("dlltest.dll"));
	//HINSTANCE hInstance= LoadLibraryA(dllname);
	if(hInstance==NULL)
	{
		;
	}
	FUN test= (FUN)GetProcAddress(hInstance,"test");
	if(test!=NULL)
	{
		int a = test();
		printf("%d",a);
	}

	FreeLibrary( hInstance );
	return 0;
}

3. dll的调用(MFC)

MFC的调用和console没啥区别。新建MFC工程,随便建就行(吾建了Dialog)。然后添加个按钮及其相应函数,添加个Editbox。界面如下图。
同样改为x64平台,dll复制到工程目录。
在这里插入图片描述

在按钮响应函数中添加代码:

void CmfcusedllDlg::OnBnClickedButton1()
{
	const char* dllname = "dlltest.dll";
	const char* funname = "test";
	HINSTANCE hInstance= LoadLibrary (TEXT("dlltest.dll"));
	if(hInstance==NULL)
	{
		;
	}
	FUN test= (FUN)GetProcAddress(hInstance,"test");
	if(test!=NULL)
	{
		int a = test();
			CString d;
	d.Format(_T("%d"),a);

		SetDlgItemText(IDC_EDIT1,d);
	}

	FreeLibrary( hInstance );
}

运行结果下图:

在这里插入图片描述

4.未进行.def和extern设置后果

使用时,程序找到.dll的地址,但是在GetProcAddress时,得不到test()函数,结果为NULL。
在这里插入图片描述

此时使用depends.exe查看。如下图,可发现导出函数名为 ?test@YA而非test。

在这里插入图片描述

而添加了extern C或者定义了def的导出函数名就为test了,如下图

在这里插入图片描述
平时经常使用dumpbin.exe来查看dll的导出函数。
Dumpbin可在xx\Microsoft Visual Studio 14.0\VC\bin中找到。该exe采用命令行的方式运行。基本上就是cd到dumpbin.exe目录下,直接dumpbin.exe -exports x.dll文件即可,上述图片是吾运行的例子。
dumpbin.exe -exports F:\VS_DOC\VS_DOC\vs2010\mfcusedll\mfcusedll\dlltest.dll
吾习惯于depends.exe,功能较为强大。这个百度一搜就能搜到,官网免费下载。吾也提供了下载资源,点开吾的上传资源里面应该有

在这里插入图片描述在这里插入图片描述

来自 一匹大懒虫,如果文章对你有益,希望能关注下,谢谢

在这里插入图片描述

5. 复杂函数的导出

有人可能会问,一个如此简单函数学起来很容易,那么如果是那种带有函数调用的呢,如果是那种多层函数调用的呢。其实也是非常简单的。
以LM迭代法为实例,(因为项目稍微有点涉密,所以具体代码不会展现,只会展现教程,相应的dll会放出来)。LM 和 Gauss-Newton iteration 类似,只不过引入了一个damp。

新建工程lmcalibrapi
文件
lmcalibrapi.h
中代码:

#ifdef LMCALIBRAPI_EXPORTS
#define LMCALIBRAPI_API extern "C" __declspec(dllexport)
#else
#define LMCALIBRAPI_API __declspec(dllimport)
#endif


//增加的方法
extern "C" LMCALIBRAPI_API int test();

extern "C" LMCALIBRAPI_API double** jacobin(double dataxyz[][4],int row, int col,const double* xk0);
//矩阵计算
extern "C" LMCALIBRAPI_API double calculate_A5(double **src, int n);
extern "C" LMCALIBRAPI_API double ** calculate_A_adjoint5(double **src, int n); 
extern "C" LMCALIBRAPI_API double** calculate_A_inverse5(double **src,int n);  //计算5x5矩阵的逆矩阵
extern "C" LMCALIBRAPI_API double** Inverse(double *matrix1[], int n, double d);  //利用余子式求逆矩阵
extern "C" LMCALIBRAPI_API double Determinant(double* matrix[], int n);  //求矩阵的行列式
extern "C" LMCALIBRAPI_API double AlCo(double* matrix[], int jie, int row, int column);  //求矩阵的代数余子式
extern "C" LMCALIBRAPI_API double Cofactor(double * matrix[], int jie, int row, int column);  //求矩阵的余子式
extern "C" LMCALIBRAPI_API double** transpose(double *matrix1[],int row,int col);
extern "C" LMCALIBRAPI_API double** multiply(double** A/*5 55*/, double** B/*55 5*/, int ma,int na,int mb,int nb);
extern "C" LMCALIBRAPI_API double** geneFCol(double dataxyz[][4]/*[dx,dy,dz,d]*/,int row, int col,const double* xk0,double R);

extern "C" LMCALIBRAPI_API double* LM(double dataxyz[][4],double* xk0,int length,double eps,double mu,double v);

文件
lmcalibrapi.cpp
中代码:

#include <stdafx.h>
#include <stdlib.h>
#include <stdio.h>
#include "lmcalibrapi.h"
#include "math.h"
//cpp文件中可以直接就写算法的具体实现了
LMCALIBRAPI_API double* LM(double dataxyz[][4],double* xk0,int length,double eps,double mu,double v)
{
//添加算法代码
}
……
……
……

头文件中需要导出什么就extern “C” …… 啥。简单的,吾建议都导出就行了。

6. 该dll的使用教程

有人问这个dll去哪里下载,一匹大懒虫回复lmcalibrapi.dll
最近有点忙,使用教程后面更新……

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CoomCon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值