Windows核心编程之DLL基础
来源:互联网 发布:软件项目 文档 编辑:程序博客网 时间:2024/05/21 06:55
一.DLL基础
相信大部分C/C++程序员都用过静态或动态链接库,在Linux和Windows中也大量的使用着静态库和动态库技术。系统提供的接口,标准库接口,都是以库的形式提供;java的JNI是用动态库的技术实现的。今天主要讲的是windows中动态库的一些基础知识点。在windows中动态链接库习惯称为DLL。
最初接触到动态库的实现是在看 <深入理解计算机系统> 的链接一章,作者详细的讲解了动态链接库的链接过程,加载过程和符号解析,重定位过程。有兴趣的可以去看看这一本书,之后在学windows核心编程的DLL章节时,就会发现在不同的系统中,动态库有不同的表现形式,但本质的东西都是一样的。
使用DLL的好处:
(1)模块化,简化了项目管理。通过把逻辑上独立的功能放在一个DLL中实现,增加了模块化。同时,不同的团队或者个人可并行实现自己的模块,提高了开发效率同时简化 了项目的管理。
(2)动态扩展程序的特性:在运行时可检测一下参数来决定当前环境下应该提供什么功能,然后加载相应的DLL。
(3)节省内存:这是动态库最重要的一个特性。一个DLL可以被加载进内存多次,即有多个该DLL的实例,但在同一时刻,物理内存中只会加载一次该DLL,即所有加载该DLL 的应用都共享该DLL在物理内存中的页面。所以动态链接库也叫共享库。
DLL的这种共享特性也会带来一些问题,例如一个DLL在内存中有多个实例,如果其中一个实例改变了DLL中的全局变量,则会影响到其他的实例。这个问题是通过写时复制技术来避免的,即当有一个实例要改变全局变量时,就为该实例单独复制一份DLL到内存中,该实例之后改变的就是这个复制出来的DLL了。
二.构建DLL模块
一个DLL中的函数,变量和类要被其他模块使用,必须将它们导出。通常,我们把要导出的符号单独放在一个头文件中声明,声明这些符号是要导出的;另一方面,其他模块要使用该DLL导出的符号,必须将这些符号导入。
看一个实际的DLL模块的头文件:
#pragma once#ifdef LOG4CPP_EXPORTS#define LOG4CPP_API __declspec(dllexport)#else#define LOG4CPP_API __declspec(dllimport)#endifnamespace myLog4cpp{#ifdef __cplusplusextern "C" {#endif //__cplusplusvoid LOG4CPP_API mylog(int num1 , int num2) ;#ifdef __cplusplus}#endif //__cplusplus}//namespace myLog4cpp
如果定义了宏LOG4CPP_EXPORTS,则LOG4CPP_API为: __declspec(dllexport),这是DLL导出的声明方法;
如果没有定义宏LOG4CPP_EXPORTS,则LOG4CPP_API为:__declspec(dllimport),这是DLL导入的声明方法;
所以在DLL模块中,应该定义宏LOG4CPP_EXPORTS,而在使用该DLL的项目中,不能定义宏LOG4CPP_EXPORTS。通过这种方式,可以实用同一个头文件来声明符号的导出和导入。
另一种导出DLL符号的方法是创建.def文件,并在.def中包含EXPORTS段:
EXPORTS
mylog
在上面给出的头文件中,还有一个extern "C"的使用,因为C++编译器会改变函数名字,这样会导致C++导出的接口不能被使用C语言的项目使用。extern "C"就是用来避免这种情形的。当用extern "C"时,编译器会把函数接口编译成适合C使用的函数接口。
三.何为导出
当Microsoft的C/C++编译器看到用__declspec(dllexport)修饰的符号时,会在生成的.obj文件中嵌入一些额外的信息,当链接器在链接DLL的所有.obj文件时,会解析这些信息。在链接DLL的时候,链接器会检测这些与导出的符号有关的嵌入信息,并生成一个.lib文件,这个.lib文件列出了该DLL导出的符号。当其他模块用到该DLL时,只需要有这个.lib文件就可以进行编译链接。除了创建这个.lib文件之外,链接器还会在生成的DLL中嵌入一个导出符号表,这个导出符号表列出了导出的变量,函数和类的符号名。链接器还会保存相对虚拟地址(RVA),即在该DLL内以0为基地址的地址。之后DLL被加载到进程的地址空间时,可以根据这个RVA地址进行重定位。
四.何为导入
当连接器在解决导入符号的时候,会在生成的可执行模块中嵌入一个特殊的段,即导入段。导入段列出了该模块所需的DLL模块,以及它从每个DLL模块中引用的符号。
四.DLL的载入
DLL有两种载入方式:隐式载入方式和显式载入方式。DLL的载入是指该DLL的文件映像被映射到调用进程的地址空间中,进程中的所有线程这时就可以调用该DLL中的函数了,对进程中的线程来说,该DLL中的代码和数据就像是一些附加的代码和数据。
在隐式载入中,DLL的载入是在应用程序加载到进程的地址空间时发生的。
在显式载入中,DLL的载入是在应用程序的运行时动态加载。直到需要某个功能时,程序才把这个功能所在的DLL加载进来,所以这种方式是最灵活的。window提供了几个函数:
HMODULE LoadLibrary(PCTSTR pszDLLPathName); //载入DLL
VOID FreeLibrary(HMODULE hInstDll) ; //卸载DLL
FARPROC GetProcAddress(HMODULE hInstDll,PCSTR pszSymbolName); //获取想要引用的符号地址
- Windows核心编程之DLL基础
- Windows核心编程:DLL基础
- Windows核心编程 DLL基础
- windows核心编程---DLL基础
- Windows核心编程--DLL基础--摘录
- Windows核心编程(十八)DLL基础
- Windows核心编程 第十九章 DLL基础
- Windows 核心编程之Dll 延时加载
- Windows 核心编程之Dll注入
- windows核心编程之DLL注入
- DLL基础——Windows核心编程学习手札之十九
- 《Windows核心编程5》第19章-DLL基础
- WINDOWS核心编程——DLL基础和实操
- 《Windows核心编程》读书笔记十九章 DLL基础
- 《Windows核心编程》之“线程基础”
- windows核心编程 DLL技术
- Windows核心编程Dll注入之远程线程
- 《Windows核心编程》之“DLL注入”(一)
- ftp图片服务器搭建!ftp上传到图片服务器,通过http协议读取图片
- node.js学习之输出Hello World
- 结构体的内存结构以及位域的认识
- 面试题总结 —— JAVA高级工程师
- python实现翻转给定列表中的元素
- Windows核心编程之DLL基础
- B
- 化学品问题 oj137
- 当U盘启动时,出现grub>如果将启动项引导到硬盘?
- (一)maven 安装配置
- 算法学习资料
- Redis五种数据类型介绍
- NYOJ 312 20岁生日
- Java高级工程师常见面试题