之前写过一篇自己生成静态库的文章,《VS2010/2013下生成并使用静态库》
现在需要生成动态dll给别人用,所以就记录一下生成的过程。
Step 1:新建一个Visual C++项目
随便选择Win32控制台或者Win32项目都可以。下一步才是真正选择生成的项目类型,即使下一步你选错了,最后在项目属性内还是可以更改,后面说更改的方法。
然后点击“确定”,再点击“下一步”,直到看到下面这个界面:
在“应用程序类型”里面选择“DLL”即可。可以看到这一步才是决定项目真正类型的关键地方。
Step 2:编写代码
项目新建好之后可以看到里面有一个dllmain.cpp文件,这就是dll项目的入口。这个文件里面的内容不需要做任何改动,直接保留即可。
然后你把自己需要的代码都写好,写完之后你需要写一个接口,因为你要生成dll给别人调用,那么到最后你要给别人的将是一个.h头文件,一个dll文件,一个和dll文件相关联的lib文件。这个接口就是一个.h头文件和一个对应的.cpp源文件。新建一个头文件,内容如下(头文件的名字和具体的函数自己编写,和具体项目相关):
api_pregnancy.h
/*****************************************************************************
* 名称: api_pregnancy.h
* 模块: XXX
* 描述: 用于XXX的函数接口
*
* 作者: XXX
* 日期: 2017.08.22
* 版本: 1.0
*
* Copyright (c) 2017 XXX.
* All rights reserved.
*****************************************************************************
*
* @change:
* @change:
*
*****************************************************************************/
#ifndef _API_PREGNANCY_H_
#define _API_PREGNANCY_H_
#ifdef PREG_API
#define PREG_API_EXPORT _declspec(dllexport)
#else
#define PREG_API_EXPORT _declspec(dllimport)
#endif // PREG_API
/*****************************************************************
** 函数功能:XXX
** 输入参数:const char* filePath //图像文件
** 输出结果:double& ratio //检测线和对照线颜色深浅的比值
** 创建作者:XXX
** 函数版本:1.0 2017.08.22
** 修改说明:
******************************************************************/
bool PREG_API_EXPORT api_pregnancy_recognization(const char* filePath, double& ratio);
#endif // !_API_PREGNANCY_H_
下面来解释上面文件内的内容。
(1)
#ifndef _API_PREGNANCY_H_
#define _API_PREGNANCY_H_
===== 剩下其他的代码写在这里 =====
#endif // !_API_PREGNANCY_H_
上面这三个语句是一体的,用于替换默认的#pragma once,两者的目的都是为了保证该头文件在被多次包含的时候不会出现定义冲突的错误。使用#pragma once或者我用的#ifndef方式都是可以的,两者的区别感兴趣可以百度一下。
需要注意的是,这些语句都要放在头文件最开始的地方(注释除外),也就是说你所有的代码都得写在这些语句的后面。#pragma once就把所有语句写在它的后面,#ifndef方式就想上面提示的那样所有的代码放在#ifndef和#endif中间。
(2)
#ifdef PREG_API
#define PREG_API_EXPORT _declspec(dllexport)
#else
#define PREG_API_EXPORT _declspec(dllimport)
#endif // PREG_API
这段代码的作用就是定义dll函数的接口方式了,_declspec(dllexport)表示你要把自己的代码生成dll给别人用,需要导出函数给别人。_declspec(dllimport)表示你在调用别人的dll里面的函数。而这段代码里面把两者都包含,纯粹是为了方便,需要切换的时候只需要在项目属性里面改一下预处理器的定义即可,就不需要再修改每个函数前面的接口方式了。这个该怎么理解呢?看下面:
void _declspec(dllexport) api_test(int a, int b);
如果不考虑我上面那种方式,你要导出你的函数api_test给别人用,只需要像这样写就可以了。看起来这样一行代码比前面定义一大堆东西简洁多了嘛。嗯,各有千秋吧。这是你的导出函数只有一个的情况,如果有2个,10个,20个呢,你每个函数前面都要写 _一次declspec(dllexport),而某一天你突然都需要把这些改成_declspec(dllimport),那不是意味着你要在20个函数面前都修改一次?!这样多麻烦。所以就有了我上面那种定义方式,你把 _declspec(dllexport)和_declspec(dllimport)都用同一个宏表示,然后给一个预处理器定义PREG_API,当定义了PREG_API的时候就代表使用_declspec(dllexport)方式,当没有定义PREG_API的时候,就用_declspec(dllimport)方式,而你需要做的只是在预处理器里面加上或者删除这个定义而已,是不是方便多了?
这个预处理器的名字PREG_API你可以随便取,这只是我的项目里面取的名字而已。
(3)
/*****************************************************************
** 函数功能:XXX
** 输入参数:const char* filePath //图像文件
** 输出结果:double& ratio //检测线和对照线颜色深浅的比值
** 创建作者:XXX
** 函数版本:1.0 2017.08.22
** 修改说明:
******************************************************************/
bool PREG_API_EXPORT api_pregnancy_recognization(const char* filePath, double& ratio);
写好了头文件,我们再来看cpp源文件。
api_pregnancy.cpp
#include "api_pregnancy.h"
bool PREG_API_EXPORT api_pregnancy_recognization(const char* filePath, double & ratio)
{
//你的函数具体实现代码
}
cpp文件就非常简单了,只是把头文件中声明的函数在这里定义一次而已,注意函数定义和声明的名字要完全一样,包括加在函数名前面的那个导出宏PREG_API_EXPORT在cpp中的函数定义中也是要包含的。
Step 3:生成dll
点击“生成解决方案”,耐心等待生成就行了。
注意:要生成dll给别人用,请选择Release模式。
然后我们打开项目解决方案的文件夹,会看到和项目文件夹同层目录下有一个Release文件,这就是上面生成的dll的存放位置:
打开Release文件夹,看到里面有一个dll文件和一个lib文件,这两个文件都是要用的。【在动态库的情形下,有一个dll为什么还会有一个lib呢?其实这个lib是和这个dll相关联的,存放的是一些头文件中的导出函数位于dll的什么位置等信息,相当于头文件和dll的一个中间桥梁吧,没有这个是找不到dll中的对应函数实现的。】
好了,现在把生成的dll文件,lib文件,以及你的接口函数所在的头文件给别人就可以了。