动态链接库与静态链接库

来源:互联网 发布:php获取url文件名 编辑:程序博客网 时间:2024/06/06 15:22
动态链接库

动态链接库是一些独立的文件,包含能被可执行程序或其他dll调用完成某项功能的函数。只有在其他模块调用动态链接库时,它才发挥作用。


WindowsAPI所有函数包含在dll中,三个重要的dll,
Kernel32.dll:内存、进程、线程各函数
User32.dll:执行用户界面任务(如窗口创建和消息传送)
GDI32.dll:画图和显示文本

静态库与动态链接库的区别

静态库:函数和数据被编译一个二进制文件(.lib)。(静态链接:加载即完成整个程序虚址到内存映射)

使用静态库,编译链接可执行文件时,链接器从库中复制这些函数和数据并把他们和应用程序其他模块组合起来创建最终的可执行文件(.exe) 。发布时只需exe文件


使用动态库,提供2个文件:一个引入库lib和一个dll。引入库包含被dll导出的函数和变量的符号名,dll包含实际的函数和数据。

编译链接可执行文件时,只需链接引入库,dll中函数和变量并不复制可执行文件,运行时再加载dll,访问导出的函数。(动态链接加载,程序地址可重定位)



使用动态链接库好处
dll可以采用多种语言编写
增强产品的功能(界面插件)
提供二次开发平台(提供动态链接库)
简化项目管理
节省磁盘空间和内存(多应用程序访问一个动态链接库,加载内存只需一次)
有助于资源的共享(资源共享提供给其他应用程序)
实现应用程序的本地化(多语言版本)

进程如何访问dll

创建加载完成后,os分析该进程需要哪些dll,搜索dll分配内存加载并将dll代码和数据页面并映射到进程空间。其他进程再访问dll,dll无需再加载,只需进程虚址重新变换。(加载时链接)


动态链接库被多进程访问(多进程共享dll内存页面)
dll加载方式    dumpbin 可以查看dll导出函数和可执行文件依赖的dll(程序加载时链接)


隐式链接 (引入lib or .h)工程需设置lib
缺点:多个dll需要全部加载,可能造成启动时间慢,不是全部需要都加载浪费内存资源

Dll1.h

#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllexport)  //c语言标准调用  默认不加extern "C" 即C++调用
#endif

DLL1_API int add(int a,int b);
DLL1_API int substract(int a,int b);

//class /*DLL1_API*/ Point   //导出类
//{
// public:
// DLL1_API void output(int x,int y); //导出类的成员函数
//};

Dll1.cpp

#include"Dll1.h"
//#include<Windows.h>
//#include<stdio.h>

int add(int a,int b)
{
return a+b;
}
substract(int a,int b)
{
return a-b;
}


/*
void Point::output(int x, int y)
{
HWND hwnd = GetForegroundWindow();
HDC hdc = GetDC(hwnd);
char buf[20];
memset(buf,0,20);
sprintf(buf,"x=%d,y=%d",x,y);
TextOut(hdc,0,0,buf,strlen(buf));
    ReleaseDC(hwnd,hdc);
}
*/

CDllTestDlg.cpp

#include “Dll1.h”

//隐式链接

//1.声明引用外部cpp文件函数,假设库没有头文件,即函数实现时即有_declspec(dllexport)
//extern int add(int a,int b);
//extern int substract(int a,int b);


// 2.告诉编译器这2个函数从.lib引入,编译器可以生成运行效率更高的代码,但实际多数不知道库导出了哪些函数
//_declspec(dllimport) int add(int a,int b);
//_declspec(dllimport) int substract(int a,int b);


void CDllTestDlg::OnBtnAdd() 
{
// TODO: Add your control notification handler code here
CString str;
str.Format("5+3=%d",add(5,3));
MessageBox(str);
}


void CDllTestDlg::OnBtnSubstract() 
{
// TODO: Add your control notification handler code here
CString str;
str.Format("5-3=%d",substract(5,3));
MessageBox(str);
}

void CDllTestDlg::OnBtnOutput() 
{
// TODO: Add your control notification handler code here
// Point pt;
  //  pt.output(3,5);
}


工程无需设置lib(运行时链接)

模块定义文件  解决函数名称改编问题 
定义def文件  
LIBRAY Dll1
EXPORTS
add
substact

cpp:

HINSTANCE hInst;hInst=LoadLibrary("Dll1.dll");

typedef int (*ADDPROC)(int a,int b);
ADDPROC Add =(ADDPROC*)GetProcAddess(hInst,"add");
if(Add)
{
   Add(5,3);
}

FreeLibrary(hInst);

动态链接,使用dumpbin看不到使用dll


函数名称改编
C++编译器为支持类函数的重载,按照一定规则在链接导出函数时候篡改函数名称
缺点:C语言生成的客户端或其他编译器生成的客户端调用C++ dll中函数时,可能找不到
使用extern "C" 修饰导出函数,函数名称不会发生改编。但是不能导出类的成员函数。所以可使用在导出全局函数。解决了C、C++语言的相互调用问题。

调用约定

_stdcall pascal调用约定 delphin语言调用约定 C标准调用约定

不加修饰默认是C++调用约定
extern "C"  限制C++编译器编译时对全局函数名称改编,但约定改变,名称依旧改变。


注意点
dll提供者应提供dll头文件,否则可用dumpbin工具猜测
dll函数必须导出才可以被外部可执行模块调用,可以包含由头文件声明导出宏和由宏修饰的原型,cpp文件函数无需再由_declspec(dllexport)修饰
0 0
原创粉丝点击