一、制作DLL库
1、构建visual studio工程
可以直接打开vs构建DLL工程,方法如下:
New --> Project --> Dynamic-Link Library(DLL) --> 取名,选路径 --> OK
另外一种方法就是通过cmake构建(建议大家掌握cmake构建工程,这对于跨平台编译来说非常有用),首先建立好源文件myDll.hpp、myDll.cpp,然后建立CMakeLists.txt文件并编写:
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(myDLL)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) #将myDll.hpp所在目录包含进来
FILE(GLOB SRC_DLL ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) #将所有cpp文件加入到SRC_DLL变量中
add_library(myDLL SHARED ${SRC_DLL}) #生成dll库
if(MSVC)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/myDll.hpp DESTINATION include_path) #include_path为库头文件输出的目录,由自己指定
INSTALL(TARGETS myDLL RUNTIME DESTINATION lib_path) #lib_path为dll和lib输出的目录,由自己指定
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/Release/myDLL.lib DESTINATION lib_path)
endif()
然后建立一个build目录,进入build目录,输入命令:
#win32平台
cmake path_cmake #path_cmake为CMakeLists.txt所在目录
#x64平台
cmake -A x64 path_cmake #path_cmake为CMakeLists.txt所在目录
此时vs工程就在build/myDLL目录下。
2、编写myDll.hpp和myDll.cpp文件
// myDll.hpp file
#pragmaonce
#ifndef DLLEXPT
#defineDLLEXPT __declspec (dllexport)
#endif
int DLLEXPT Add(int a,int b);
// myDll.cpp file
#include"myDll.hpp"
int Add(int a,int b){
return a+b;
}
然后就可以在vs上编译生成dll库了,主要生成的有3个文件:myDll.hpp、myDll.dll、myDll.lib文件。注:在myDll.hpp文件中声明接口时一定要在前面加入DLLEXPT,否则不会生成lib文件。
二、调用DLL库
下面讲一下怎么调用dll库,dll调用方式分为两种:显式调用、隐式调用,为了理解这两种调用方式首先我们来了解一下生成的头文件、lib文件、dll文件。头文件是函数、方法的一些声明,这个文件主要便于调用方在c/c++文件中调用这里面的函数或方法;dll文件是函数和方法的具体实现文件,有点类似于cpp文件;lib文件不止是包含了关于函数和方法的声明,还包含了这些函数、方法的实现文件名(也就是dll文件名)。
1、隐式调用
这种方式下,程序启动时就会将dll文件加载到程序中,dll存在于整个程序运行周期中。这种方式的调用只需要分为两步:
1)包含库的头文件,本例中也就是包含myDll.hpp文件。这一步主要是方便调用方使用myDll.hpp里面声明的函数或方法。
2)将lib文件编译进应用程序。这一步主要是告诉编译器myDll.hpp(或者myDll.lib)里面声明的函数方法位于哪个实现文件中(这里是myDll.dll),程序启动时就会链接这个实现文件。将lib文件编译进应用程序有两种方法,第一种就是配置一下vs:properties->Configuration Properties->Linker->Input->Additional Dependencies导入lib文件路径即可;第二种就是直接在应用程序源码中#pragma comment(lib,"myDll.lib")。
一个调用案列如下:
// main.cpp file
#include"myDll.hpp"
#pragma comment(lib,"myDll.lib") #告诉编译器编译时要编译myDll.lib文件
extern "C"_declspec(dllimport) int Add(int a,int b); #也可不要这句,如果写了之后会加速加载dll文件,猜测原因是只加载dll里面的部分函数的实现。
void main(){
int sum=Add(2,3);
printf("The sum is %i\n",sum);
}
注:在程序运行时需要将dll文件放入动态链接库搜索目录下,比如工程所在的目录或Windows系统目录,否则程序运行时会搜索不到dll文件。
2、显式调用
顾名思义,显式调用dll文件就是将dll的调用过程显式的写入应用程序中,显示调用也就是苹果系统里面的“懒加载”,应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性。
显式调用的过程是这样的:在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用GetProcAddress()获取想要引入的函数,在应用程序退出之前,用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。调用案例如下:
// main.cpp file
#include <windows.h>
void main(){
typedef int(*pAdd)(int a,int b);
HINSTANCE hDLL;
pAdd Add
HDLL=LoadLibrary("myDll.dll");//加载动态链接库myDll.dll文件;
Add=(pAdd)GetProcAddress(hDLL,"Add");
int sum=Add(2,3);
Printf("The sum is %i\n",sum);
FreeLibrary(hDLL);//卸载myDll.dll文件;
}