前言
本文主要介绍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
最近有点忙,使用教程后面更新……