动态库(非MFC动态库)
Posted Autumn の Box
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态库(非MFC动态库)相关的知识,希望对你有一定的参考价值。
一、C语言动态库
1、创建C语言动态库,并封装函数:
1)创建新工程:Win32 Dynamic-Link Library
2)添加SourceFiles文件:Cdll.c
Cdll.c中的内容:
//创建C的动态库 //_declspec(dllexport)声明导出 _declspec(dllexport)int Cdll_add(int add1,int add2){ return add1+add2; } _declspec(dllexport)int Cdll_sub(int sub1,int sub2){ return sub1-sub2; }
3)编译、链接
注意:调用动态库中的函数时,须执行函数导出,库函数的导出有两种方法:
①方法一:用_declspec(dllexport)声明导出
②方法二:模块定义文件.def
如:LIBRARY 库文件名(无后缀)
EXPORTS
函数名1 @1
函数名2 @2
Build后在当前工程的Debug文件夹下生成Cdll.dll和Cdll.lib文件
这里生成的Cdll.dll文件中保存了函数的实际偏移地址和对应编号,Cdll.lib文件中并非函数的源代码而是存放的动态库dll中的函数偏移地址编号
将Cdll.dll文件置于工作区下的bin文件夹中,将Cdll.lib文件置于工作区下的lib文件夹中
2、调用C语言动态库中的函数:
①静态调用(隐式链接)
注意:静态调用(隐式链接)调用动态库中的函数时,在声明函数时须在函数原型前加_declspec(dllimport)声明导入
但我们这里是用C程序来调用C语言动态库中的函数,无须函数声明
1)创建工作工程:Win32 Console Application
2)添加SourceFiles文件:UseCdll.c
UseCdll.c中的内容:
//静态调用C的动态库 //告诉链接器去哪儿抓偏移地址编号 #pragma comment(lib,"../lib/Cdll.lib") int main(){ int sum,sub; sum=Cdll_add(5,3); sub=Cdll_sub(5,3); printf("sum=%d,sub=%d\\n",sum,sub); return 0; }
3)编译、链接
注意:调用动态库的情况下,须将生成的dll文件(即Cdll.dll)与执行文件(即UseCdll.exe)放在同一目录下,程序才可运行
这里因为之前已将Cdll.dll文件统一置于工作区下的bin文件夹中,因此在本次调用时须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UseCdll.exe
②动态调用(显式链接)
二、C++动态库
1、创建C++动态库,并封装函数:
注意:调用动态库中的函数时需要执行函数导出,库函数的导出有两种方法:
①方法一:用_declspec(dllexport)声明导出
②方法二:模块定义文件.def
如:
LIBRARY 库文件名(无后缀)
EXPORTS
函数名1 @1
函数名2 @2
注:.def 文件中的注释格式为“;注释”,且为单行注释。
①“_declspec(dllexport)声明导出”方式创建C++动态库
1)创建新工程:Win32 Dynamic-Link Library
2)添加SourceFiles文件:CPPdll.cpp
CPPdll.cpp中的内容:
//用"声明导出_declspec(dllexport)"方式创建C++动态库 //函数导出 _declspec(dllexport)int CPPdll_add(int add1,int add2){ return add1+add2; } _declspec(dllexport)int CPPdll_sub(int sub1,int sub2){ return sub1-sub2; }
3)编译、链接
Build后,在当前工程的Debug文件夹下生成CPPdll.dll和CPPdll.lib文件
CPPdll.dll文件保存了函数的实际偏移地址和对应编号,CPPdll.lib文件并非函数源代码而是存放的动态库dll中的函数名和偏移地址编号
将CPPdll.dll文件置于工作区下的bin文件夹中,将CPPdll.lib文件置于工作区下的lib文件夹中
②“模块定义文件.def”方式创建C++动态库
1)创建新工程:Win32 Dynamic-Link Library
2)添加SourceFiles文件:CPPdll2.def
CPPdll2.def中的内容:
LIBRARY CPPdll2 EXPORTS CPPdll_add @1 CPPdll_sub @2
3)添加SourceFiles文件:CPPdll2.cpp
CPPdll2.cpp中的内容:
//用"模块定义文件导出.def"方式创建C++动态库 //函数导出 int CPPdll_add(int add1,int add2){ return add1+add2; } int CPPdll_sub(int sub1,int sub2){ return sub1-sub2; }
4)编译、链接
Build后,在当前工程的Debug文件夹下生成CPPdll2.dll和CPPdll2.lib文件
CPPdll2.dll文件保存了函数的实际偏移地址和对应编号,CPPdll2.lib文件并非函数源代码而是存放的动态库dll中的函数名和偏移地址编号
将CPPdll2.dll文件置于工作区下的bin文件夹中,将CPPdll2.lib文件置于工作区下的lib文件夹中
2、调用C++动态库中的函数:
①静态调用(隐式链接)
1)创建工作工程:Win32 Console Application
2)添加SourceFiles文件:UseCPPdll.cpp
UseCPPdll.cpp中的内容:
//用"隐式链接"方式调用C++动态库 //对应的在创建C++动态库时使用的是"声明导出_declspec(dllexport)"方式 #include <stdio.h> int CPPdll_add(int add1,int add2); int CPPdll_sub(int sub1,int sub2); //告诉编译器到哪去抓函数的导出偏移地址编号 #pragma comment(lib,"../lib/CPPdll.lib") /*****************************************/ //C++编译器调用C语言动态库中的函数 extern "C"int Cdll_add(int add1,int add2); extern "C"int Cdll_sub(int sub1,int sub2); #pragma comment(lib,"../lib/Cdll.lib") /*****************************************/ int main(){ int sum=CPPdll_add(5,6); int sub=CPPdll_sub(5,6); printf("sum=%d,sub=%d\\n",sum,sub); /*********************************/ sum=Cdll_add(5,8); sub=Cdll_sub(5,8); printf("sum=%d,sub=%d\\n",sum,sub); /********************************/ return 0; }
3)编译、链接
注意:调用动态库的情况下,须将生成的dll文件(即CPPdll.dll)与执行文件(即UseCPPdll.exe)放在同一目录下,程序才可运行
这里因为之前已将CPPdll.dll文件统一置于工作区下的bin文件夹中,因此在本次调试时,须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UseCPPdll.exe
4)C++编译器在调用动态库中的函数时,须进行函数声明:
#include <stdio.h>
int CPPdll_add(int add1,int add2);
int CPPdll_sub(int sub1,int sub2);
5)C++编译器在调用C语言动态库中的函数时,会对函数名进行换名,须使用extern “C”来抑制C++编译器的换名
extern "C"int Cdll_add(int add1,int add2);
extern "C"int Cdll_sub(int sub1,int sub2);
#pragma comment(lib,"../lib/Cdll.lib")
6)这里C++编译器在调用动态库中的函数时,未见将函数导入,是因为将函数声明放在了调用程序内部,如果单独放在头文件中,则须将函数导入:
//UseCPPdll.h
_declspec(dllimport)int CPPdll_add(int add1,int add2);
_declspec(dllimport)int CPPdll_sub(int sub1,int sub2);
若调用的是C语言动态库(.c生成的.dll、.lib)中的函数,还须在int前加extern "C"
②动态调用(显式链接)
注意:C++程序在动态调用(显式链接)C++动态库中的函数时,会对函数进行换名,故推荐用“模块定义文件.def”的方式导出函数
1)创建工作工程:Win32 Console Application
2)添加SourceFiles文件:UseCPPdll2.cpp
UseCPPdll2.cpp中的内容:
//用"显示连接"方式调用C++动态库 //用此方法调用时,C++编译器会对库中函数的调用进行换名 //因此在创建C++动态库时,推荐使用"模块定义导出.def"方式将函数导出 #include <windows.h> #include <stdio.h> typedef int(*DLL_ADD)(int m,int n); typedef int(*DLL_SUB)(int m,int n); int main(){ //找到dll文件并使文件中的内容进入内存 HINSTANCE hDll=LoadLibrary("CPPdll2.dll"); printf("hDll:%d\\n",hDll); //获取函数的绝对地址并进行函数调用 DLL_ADD myAdd=(DLL_ADD)GetProcAddress(hDll,"CPPdll_add"); printf("myAdd:%p\\n",myAdd); int sum=myAdd(5,5); printf("sum=%d\\n",sum); DLL_SUB mySub=(DLL_SUB)GetProcAddress(hDll,"CPPdll_sub"); printf("mySub:%p\\n",mySub); int sub=mySub(5,5); printf("sub=%d\\n",sub); FreeLibrary(hDll); return 0; }
3)编译、链接
注意:调用动态库的情况下,须将生成的dll文件(即CPPdll2.dll)与执行文件(即UseCPPdll2.exe)放在同一目录下,程序才可运行
这里因为之前已将CPPdll2.dll文件统一置于工作区下的bin文件夹中,因此在本次调试时,须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UseCPPdll2.exe
动态加载:
1.定义函数指针类型:typedef ...
2.加载动态库
HMODULE LoadLibrary (
LPCTSTR lpFileName // 动态库文件名(按路径规则搜索)或者用绝对/相对路径(按指定路径加载)
);
成功返回动态库实例句柄(HINSTANCE),失败返回NULL。
3.获取函数地址
FARPROC GetProcAddress (
HMODULE hModule, // 动态库实例句柄
LPCSTR lpProcName, // 函数名(注意C++换名问题)
);
成功返回函数地址,失败返回NULL。
4.卸载动态库
BOOL FreeLibrary (
HMODULE hModule // 动态库实例句柄
);
成功返回TRUE,失败返回FALSE。
5.可执行程序调用LoadLibrary时加载动态库,调用FreeLibrary时卸载动态库
3、在C++动态库中封装类
注意:动态库中类的导出只能用“_declspec(dllexport)”方式,不能使用“模块定义文件.def”方式
1)创建新工程:Win32 Dynamic-Link Library
2)添加HeaderFiles文件:dllClass.h
dllClass.h中的内容:
#ifndef DLLCLASS_H #define DLLCLASS_H //宏开关 //定义关于库中类的“导出”(我们编写库时为导出)或“导入”(用户使用库时需导入)的宏定义 #ifdef DLLCLASS_EXPORTS #define EXT_CLASS _declspec(dllexport) #else #define EXT_CLASS _declspec(dllimport) #endif class EXT_CLASS CMath{ public: int Add(int add1,int add2); int Sub(int sub1,int sub2); }; #endif
类导出的宏开关:
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport)
#else
#define EXT_CLASS _declspec(dllimport)
#endif
class EXT_CLASS CMath{
......
}
3)添加SourceFiles文件:dllClass.cpp
dllClass.cpp中的内容:
//在库中封装类 //在.cpp源文件中实现函数的功能,编译连接后生成的.dll文件用户才不可见具体的功能实现过程 //必须在头文件前打开宏开关,设为导出:_declspec(dllexport) #define DLLCLASS_EXPORTS #include "dllClass.h" #include <windows.h> #include <stdio.h> //入口函数 BOOL WINAPI DllMain(HINSTANCE hDll,DWORD fdwReason,LPVOID pParam){ switch(fdwReason){ case DLL_PROCESS_ATTACH: //动态库被别的进程加载 //申请资源、初始化工作 printf("Loading...\\n"); break; case DLL_PROCESS_DETACH: //动态库被别的进程卸载 //善后处理 printf("UnLoading...\\n"); break; } return TRUE; } int CMath::Add(int add1,int add2){ return add1+add2; } int CMath::Sub(int sub1,int sub2){ return sub1-sub2; }
4)编译、链接
Build后,在当前工程的Debug文件夹下生成dllClass.dll和dllClass.lib文件
dllClass.dll文件保存了函数的实际偏移地址和对应编号,dllClass.lib文件并非函数源代码而是存放的动态库dll中的函数名和偏移地址编号
将dllClass.dll文件置于工作区下的bin文件夹中,将dllClass.lib文件置于工作区下的lib文件夹中
4、调用C++动态库中的类:
1)创建工作工程:Win32 Console Application
2)添加SourceFiles文件:UsedllClass.cpp
UsedllClass.cpp中的内容:
//调用dllClass.dll库 #include <stdio.h> #include "../dllClass/dllClass.h" #pragma comment(lib,"../lib/dllClass.lib") int main(){ CMath math; int sum=math.Add(5,2); int sub=math.Sub(5,2); printf("sum=%d,sub=%d\\n",sum,sub); return 0; }
3)编译、链接
在UsedllClass.cpp未见有对宏开关的定义语句“#define DLLCLASS_EXPORTS”,故宏开关自动设为导入:_declspec(dllimport)
注意:调用动态库的情况下,须将生成的dll文件(即dllClass.dll)与执行文件(即UsedllClass.exe)放在同一目录下,程序才可运行
这里因为之前已将dllClass.dll文件统一置于工作区下的bin文件夹中,因此在本次调试时,须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UsedllClass.exe
三、动态库的入口函数:
入口函数不是动态库所必须的,常用于动态库内部初始化或善后处理
BOOL WINAPI DllMain (
HANDLE hDLL, // 动态库实例句柄
DWORD fdwReason, // 被调用的原因
LPVOID lpvReserved // 保留值
);
返回TRUE表示动态库加载成功,FALSE表示失败。
fdwReason取值:
DLL_PROCESS_ATTACH - 进程加载,在主线程中调用LoadLibrary
DLL_PROCESS_DETACH - 进程卸载,在主线程中调用FreeLibrary
DLL_THREAD_ATTACH - 线程加载,在子线程中调用LoadLibrary
DLL_THREAD_DETACH - 线程卸载,在子线程中调用FreeLibrary
以上是关于动态库(非MFC动态库)的主要内容,如果未能解决你的问题,请参考以下文章