VC++制作DLL具体解释
1. DLL的基本概念
应用程序(exe)要引用目标代码(.obj)外部的函数时,有两种实现途径——静态链接和动态链接。
1. 静态链接
链接程序搜索相应的库文件(.lib),然后将这个对象模块复制到应用程序(.exe)中来。Windows之所不使用静态链接库。是由于非常多基础库被非常多应用程序使用。假设每一个应用程序一份拷贝,将带来内存的极大浪费。
2. 动态链接
链接程序搜索到相应的库文件(.lib)。然后依据函数名得到相应的函数入口地址,就可以进行编译链接。
直到真正执行的时候,应用程序才会从lib文件里记录的DLL名字去搜索同名的DLL。然后将DLL的执行代码内存映射到exe中来。动态链接库的优点是多个应用程序能够共用一份DLL的代码段内存。可是数据段则是每一个调用进程一份拷贝。
2. 静态链接库
静态链接库的使用比較简单。一般使用例如以下方式创建。
然后就像普通project一样,加入头文件的声明以及源文件的实现。
编译该project就能够得到StaticLib.lib文件了。
调用者调用.lib库也很easy。仅仅须要包括头文件声明以及指明.lib库路径就可以。如:
#include "..\StaticLib\StaticLib.h"
#pragma comment (lib, "..\\Lib\\staticlib.lib")
或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib库路径。
3. 动态链接库
Visual C++支持三种DLL。它们各自是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。他们之间的差别简单概括例如以下:
非MFC动态库:即Win32DLL,不採用MFC库函数,其导出函数为标准的C接口,能被非MFC和MFC编写的应用程序所调用。
MFC规则DLL:包括一个继承自CWinApp的类,但其无消息循环。能够使用MFC,可是接口不能为MFC。
MFC扩展DLL:採用MFC的动态链接版本号创建,它仅仅能被用MFC类库所编写的应用程序所调用。
1. 非MFC动态库
创建Win32DLL
DLL生成向导提供一些简单的演示样例,使得建立Win32DLL变得更简单。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// // // // // // #ifdef #define #else #define #endif // class WIN32DLL_API public : CWin32DLL( void ); // int Add( int x, int y); }; extern WIN32DLL_API int nWin32DLL; extern “C” int fnWin32DLL( void ); |
调用程序有两种方式来调用DLL。
1. 隐式链接到DLL
须要完毕3步,头文件、.lib文件和DLL。
详细实现例如以下:
#include
"..\StaticLib\StaticLib.h"
#pragma
comment(lib, "..\\Lib\\staticlib.lib")
或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib库路径。
DLL的搜索路径见文末.
2. 显式链接到DLL
首先LoadLibary指定的DLL。然后GetProcAddress得到指定函数的入口指针。而且通过函数入口指针来訪问DLL的函数。最后通过FreeLibrary制裁DLL。
1 typedef int (*PADDFUN)(void);
2
3 HINSTANCE hModule = LoadLibrary("Win32DLL.dll");
4
5 PADDFUN pAddFun = (PADDFUN)GetProcAddress(hModule, "GetValue");
6
7 pAddFun = (PADDFUN)GetProcAddress(hModule, MAKEINTRESOURCE(5));
8
9 FreeLibrary(hModule);
假设要保证导出的函数名是不带修饰的。一定要将指定函数为C编译器编译。
否则函数名须要以被修饰过的以“?”開始的函数名来获取函数的入口指针。上图为Dependency Walker查看到的。
除了直接以函数名获取入口地址外,还能够用索引获取函数入口地址。
GetProcAddress获取的是入口地址。所以除了能够获取函数的入口地址,相同能够获取变量的地址。
2. MFC规则DLL
MFC规则的DLL有两种。一种链接MFC动态库的,一种是链接MFC静态库的。
选择MFC动态库还是静态库与调用者有关系。由于调用者必须与DLL链接MFC库一致,否则会导致库调用的冲突。
假设不是追求追求生成的exe和DLL占较小的空间,推荐使用MFC静态库。
MFC规则DLL的导出基本上同Win32DLL一样,相同不同意导出继承自MFC库的类。
不同点主要体如今MFC规则DLL中能够使用MFC库,事实上WIN32DLL假设包括了MFC头文件以及链接库,也是能够使用MFC库的。
1. 链接MFC动态库
链接MFC动态库资源的切换。这一点须要注意,而且VC默认生成的代码中也用大篇幅的凝视提示了,而且也给出了例如以下主要的解释。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//TODO: // // // // // // // // // // // // // // // // // // // // // |
2. 链接MFC静态库
链接MFC动态库基本上和链接MFC静态库除了上面介绍的不同,导出和加入文件之类的全然一样。所以以下重点解说链接MFC静态库。
DLL导出变量、函数以及类有两种方法。前面使用的都是通过keyword来导出。另外另一种方法,即通过模块定义(.def)文件来导出。
我们来看一下模块定义(.def)文件的基本格式:
; MFCDLL.def : Declares the module parameters for the DLL.
LIBRARY "MFCDLL"
EXPORTS
; Explicit exports can go here
ShowDlg @2
nDllValue DATA
凝视是通过;来完毕的。
keywordLIBRARY,描写叙述DLL的名字,并将此信息写入记录DLL信息的.lib文件里。
所以假设在Linker\Output File中改动了生成的DLL的名字,注意也一定要与LIBRARY中描写叙述的一致。当然也能够直接凝视掉LIBRARY,这样生成的DLL信息就直接与Linker\Output File中指定的名字同样。另外LIBARAY后面描写叙述的名字,能够加引號。也能够不加引號。
ShowDlg,这个是须要导出的函数名。@2,这里的2是描写叙述方法的地址索引。能够改动,也能够不使用。系统会生成默认的。事实上不只函数有,变量也有。
假设同一时候使用了.def和__declspec(dllexport)导出,编译器会优先使用.def文件的导出。
.def文件的导出默认是C编译的,即和extern “c” __declspec(dllexport)的导出效果一样。
导入函数的方法和前面使用的一样。
以下仅仅说一下导入变量的方法。
- 直接通过keyword__declspec(dllimport) int nDLLValue;
- int* pnDLLValue = (int*)GetProcAddress(hModule, "nDllValue");
pnDLLValue = (int*)GetProcAddress(hModule, MAKEINTRESOURCE(3));
提供按序号导入的原因是这样导入的速度更快。不用去按名字比对查找。
这里仅仅介绍了.def文件导出导入的经常使用的一些方法,可是基本上够用。假设想更深入的了解.def文件还涉及到非常多知识点。能够參考:
http://blog.csdn.net/henry000/article/details/6852521
http://msdn.microsoft.com/zh-cn/library/28d6s79h.aspx
http://msdn.microsoft.com/zh-cn/library/54xsd65y.aspx
http://msdn.microsoft.com/zh-cn/library/d91k01sh.aspx
MFC 扩展 DLL 是通常实现从现有 Microsoft 基础类库类派生的可重用类的 DLL。
MFC 扩展 DLL 具有下列功能和要求:
- client可运行文件必须是用定义的 _AFXDLL 编译的 MFC 应用程序。
- 扩展 DLL 也可由动态链接到 MFC 的规则 DLL 使用。
- 扩展 DLL 应该用定义的 _AFXEXT 编译。 这将强制同一时候定义 _AFXDLL。并确保从 MFC 头文件里拉入正确的声明。 它也确保了在生成 DLL 时将 AFX_EXT_CLASS 定义为__declspec(dllexport),这在使用此宏声明扩展
DLL 中的类时是必要的。 - 扩展 DLL 不应实例化从 CWinApp 派生的类。而应依赖client应用程序(或 DLL)提供此对象。
- 但扩展 DLL 应提供 DllMain 函数,并在那里运行不论什么必需的初始化。
扩展 DLL 是使用 MFC 动态链接库版本号(也称作共享 MFC 版本号)生成的。
仅仅实用共享 MFC 版本号生成的 MFC 可运行文件(应用程序或规则 DLL)才干使用扩展 DLL。 client应用程序和扩展 DLL 必须使用同样版本号的 MFCx0.dll。 使用扩展 DLL,能够从 MFC 派生新的自己定义类,然后将此“扩展”版本号的 MFC 提供给调用 DLL 的应用程序。
扩展 DLL 也可用于在应用程序和 DLL 之间传递 MFC 派生的对象。 与已传递的对象关联的成员函数存在于创建对象所在的模块中。 因为在使用 MFC 的共享 DLL 版本号时正确导出了这些函数,因此能够在应用程序和它载入的扩展 DLL 之间任意传递 MFC 或 MFC 派生的对象指针。
client必须定义_AFXDLL 编译,事实上就是说client必须使用MFC动态库。即共享MFC库。另外。扩展DLL中显示对话框,和动态链接MFC的DLL一样须要进行资源的切换,仅仅是两个DLL的DllMain函数不同,导致切换资源的方法不同。扩展DLL的切换方法。MFC扩展DLL的导入导出。基本上和静态链接MFC的DLL一样。
HINSTANCE oldHInst = AfxGetResourceHandle();
HINSTANCE hInst = LoadLibrary("ExDll.dll");
AfxSetResourceHandle(hInst);
CMyDlg dlg;
dlg.DoModal();
AfxSetResourceHandle(oldHInst);
另外,还能够使用构造函数、析构函数来自己主动完毕资源的切换,详见演示样例代码。
MFC扩展DLL的使用比較复杂,尤其是涉及资源导出之类的,所以假设没必要,尽量少用FMC扩展DLL,可以用MFC常规DLL代表的尽量取代。
想具体了解扩展DLL的请參考:
http://msdn.microsoft.com/zh-cn/library/1btd5ea3.aspx
http://msdn.microsoft.com/zh-cn/library/h5f7ck28(VS.80).aspx
4. 纯资源DLL
一个纯资源 DLL 是一个 DLL,它包括资源如图标、 位图、 字符串和对话框。 使用一个纯资源 DLL 是共享一组同样的多个程序之间的资源的好办法。 它也是一个好的方法,以提供资源被针对多种语言进行本地化的应用程序。
要创建纯资源 DLL,请创建一个新的 Win32 DLL (非 MFC) 项目,并将资源加入到项目中。
- 选择中的 Win32 项目新项目对话框中,在 Win32 项目向导中指定 DLL 的项目类型。
- 为 DLL 创建新资源脚本包括资源 (如字符串或菜单) 并保存.rc 文件。
- 在项目 菜单上。单击 加入现有项,然后将新的.rc 文件插入到该项目。
- 指定 /NOENTRY 链接器选项。
/ NOENTRY 防止链接器将 _main 的參考链接到 DLL ; 若要创建纯资源 DLL。必须使用此选项。
- 生成 DLL。
使用纯资源 DLL 的应用程序应调用 LoadLibrary 到显式链接到 DLL。
若要訪问的资源。调用泛型函数 FindResource 和 LoadResource,当中从事不论什么种类的资源,或调用以下的特定资源的函数之中的一个:
- FormatMessage
- LoadAccelerators
- LoadBitmap
- LoadCursor
- LoadIcon
- LoadMenu
- LoadString
应用程序应调用句完毕时使用的资源。
能够调用与资源切换同样的方式完毕资源的切换。
1
2
3
4
5
6
7
8
9
10
11
|
HINSTANCE HINSTANCE "ExDll.dll" ); AfxSetResourceHandle(hInst);
CMyDlg dlg.DoModal(); AfxSetResourceHandle(oldHInst); |
也能够调用指定资源函数获取指定资源的句柄。
注意项
1. DLL搜索路径
- 当前进程的可运行模块所在的文件夹。
- 当前文件夹。
- Windows 系统文件夹。 GetSystemDirectory 函数检索此文件夹的路径。
- Windows 文件夹。
GetWindowsDirectory 函数检索此文件夹的路径。
- PATH 环境变量中列出的文件夹。
上面是EXE默认的搜索DLL路径。
可是有有时我们希望更改DLL存放的文件夹。那么就须要在搜索路径上做一些改动了。
假设去改上的模块文件夹、当前文件夹,会导致程序中使用文件夹上的不便,所以不建议改动上面这些文件夹。Windows事实上提供了改动DLL搜索路径的API。
void SetDllDirectory( LPCTSTR lpPathName);
调用这个函数之后,DLL的搜索路径改变为:
- The directory from which the application loaded.
- The directory specified by the lpPathName parameter.
- The system directory. Use the GetSystemDirectory function to get the path of this directory. The name of this directory is System32.
- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
- The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- The directories that are listed in the PATH environment variable.
HMODULE LoadLibraryEx( LPCTSRlpFileName,HANDLEhFile, DWORD dwFlags);
以參数dwFlags为 _WITH_ALTERED_SEARCH_PATH调用上面的函数时,DLL搜索路径例如以下:
- The directory specified by the lpFileName path. In other
words, the directory that the specified executable module is in. - The current directory.
- The system directory. Use the GetSystemDirectory function to get the path of this directory.
- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
Windows Me/98/95: This directory does not exist.
- The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- The directories that are listed in the PATH environment variable.
通过上面两个改动DLL搜索路径的API,我们能够发现。LoadLibraryEx会将一个新添的搜索路径放在其它全部搜索路径之前。而SetDllDirectory则将搜索路径放在第2位。
通过实验也发现,LoadLibraryEx的DLL搜索时间明显少于SetDllDirectory设置之后的DLL搜索时间。所以建议使用LoadLibraryEx改动DLL搜索路径。
2. DLL中的静态变量
假设是在一个进程中,几个模块共同调用同一个DLL。那么DLL中的静态变量是全局共用的。
3. 关于释放DLL内存的问题
Windows同意一个进程有多个Head。我们知道每一个DLL会有自己的数据区。也就是说每一个DLL都会有自己的Head。
Windows有一个规则。即谁的Head谁负责,也就是说每一个DLL必须得自己负责Head上的内存申请以及释放。
简单的内存申请释放非常easy发现。可是有一些vector、CString、CStringArray等,它们的内部实现事实上都是有动态申请内存的。所以假设导出函数的參数涉及到这些类型时。也会导致内存释放的问题。
4. Client与DLL在设置上必须一致
- 是否动态链接到MFC库。
- 执行时库也必须一致。
MD/MDd;MT/MTd。
- 字符集是否一致。是否Unicode之类的必须一致。
- 导入导出的声明必须一致。
- 须同样的修饰名extern “C”。
- 必须同样的调用方式,_cdecl,_stdcall,_fastcall必须导入导出时一致。
以上仅仅列举了一些easy出现的错误。
总之假设在Client端链接出错时。就应该考虑Client与DLL的一致性问题了。
5. 动态链接到MFC共享DLL
If this DLL is dynamically linked against the MFC DLLs,any functions exported from this DLL which call into MFC must have the AFX_MANAGE_STATE macro added at the very beginning of the function.
即仅仅要是动态链接到MFC共享DLL的,必须使用资源切换。
VC++制作DLL具体解释的更多相关文章
- VC++制作DLL详解
1. DLL的基本概念 应用程序(exe)要引用目标代码(.obj)外部的函数时,有两种实现途径——静态链接和动态链接. 1. 静态链接 链接程序搜索对应的库文件(.lib),然后将这个对 ...
- c++builder调用VC的dll以及VC调用c++builder的dll
解析__cdecl,__fastcall, __stdcall 的不同:在函数调用过程中,会使用堆栈,这三个表示不同的堆栈调用方式和释放方式. 比如说__cdecl,它是标准的c方法的堆栈调用方式,就 ...
- VC++动态链接库(DLL)编程深入浅出(zz)
VC++动态链接库(DLL)编程深入浅出(zz) 1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用 ...
- Delphi制作DLL
一.开使你的第一个DLL专案 1.File->Close all->File->New﹝DLL﹞ 代码: //自动产生Code如下 library Project2; //这有段废话 ...
- 用VC制作应用程序启动画面
摘 要:本文提供了四种启动画面制作方法. 使用启动画面一是可以减少等待程序加载过程中的枯燥感(尤其是一些大型程序):二是 可以用来显示软件名称和版权等提示信息.怎样使用VC++制作应用程序的启动画面呢 ...
- 用Delphi制作DLL
一.开使你的第一个DLL专案 1.File->Close all->File->New﹝DLL﹞代码: //自动产生Code如下 library Project2; //这有段 ...
- VC调用DLL
VC调用DLL 调用DLL有两种方法:静态调用和动态调用. (一).静态调用其步骤如下: 1.把你的youApp.DLL拷到你目标工程(需调用youApp.DLL的工程)的Debug目录下; 2. ...
- VC++动态链接库(DLL)编程深入浅出(四)
这是<VC++动态链接库(DLL)编程深入浅出>的第四部分,阅读本文前,请先阅读前三部分:(一).(二).(三). MFC扩展DLL的内涵为MFC的扩展,用户使用MFC扩展DLL就像使用M ...
- VC++动态链接库(DLL)编程深入浅出(转帖:基础班)
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静 ...
随机推荐
- [视频监控]用状态机图展示Layout切换关系
监控系统通常会提供多种Layout给用户,用于满足不同需求,如:高清显示单路视频或者同时观察多路监控情况. 文中系统只提供了单路.2x2(2行2列共4路).8路(4行4列布局,从左上角算起,有个核心显 ...
- Selenium2Library系列 keywords 之 _SelectElementKeywords 之 get_selected_list_label(self, locator)
def get_selected_list_label(self, locator): """Returns the visible label of the selec ...
- Struts2动态调用DMI及错误解决方法
在Strust2中action可以定义自己的方法,调用方法有两种方式,一种方式是struts.xml中指定method来表示需要用到的方法, 但是这种方法缺点在于如果你的Action中有很多方法则要多 ...
- HTML5的manifest缓存
要使用manifest缓存,我们首先需要写一个manifest文件.这个文件有严格的格式要求,下面是个例子CACHE MANIFEST#我是注释,这个文件名叫test.manifestCACHE:/t ...
- Android相关图书推荐
疯狂Android讲义(第3版 附光盘) 作 者 李刚 著 出 版 社 电子工业出版社 出版时间 2015-06-01 版 次 3 页 数 780 印刷时间 2015-0 ...
- BITED数学建模七日谈之二:怎样阅读数学模型教材
今天进入我们数学建模七日谈的第二天:怎样阅读数学建模教材? 大家再学习数学建模这门课程或准备比赛的时候,往往都是从教材开始的,教材的系统性让我们能够很快,很深入地了解前人在数学模型方面已有的研究成果, ...
- Static块详解
首先,我们先看一段程序,代码如下: public class Father { public Father() //构造方法 { System.out.println(" 父类构造方法&qu ...
- 红包算法思考和总结 -- by jason.zhi
参考: http://mp.weixin.qq.com/s?__biz=MzI2NjA3NTc4Ng==&mid=402360599&idx=1&sn=69318b235e0e ...
- mac 添加安卓设备的支持
1. 把android设备插到mac电脑上 2. 首先可以看一下之前在该mac电脑上有没有添加过这个 , 命令 : adb devices 如果显示出,下面字样,说明之前添加过了,下面就可以 ...
- Android 用MediaCodec实现视频硬解码(转)
本文向你讲述如何用android标准的API (MediaCodec)实现视频的硬件编解码.例程将从摄像头采集视频开始,然后进行H264编码,再解码,然后显示.我将尽量讲得简短而清晰,不展示 那些不相 ...