VC++制作DLL具体解释

1.    DLL的基本概念

应用程序(exe)要引用目标代码(.obj)外部的函数时,有两种实现途径——静态链接和动态链接。

  1.    静态链接

链接程序搜索相应的库文件(.lib),然后将这个对象模块复制到应用程序(.exe)中来。Windows之所不使用静态链接库。是由于非常多基础库被非常多应用程序使用。假设每一个应用程序一份拷贝,将带来内存的极大浪费。

2.    动态链接

链接程序搜索到相应的库文件(.lib)。然后依据函数名得到相应的函数入口地址,就可以进行编译链接。

直到真正执行的时候,应用程序才会从lib文件里记录的DLL名字去搜索同名的DLL。然后将DLL的执行代码内存映射到exe中来。动态链接库的优点是多个应用程序能够共用一份DLL的代码段内存。可是数据段则是每一个调用进程一份拷贝。

2.    静态链接库

静态链接库的使用比較简单。一般使用例如以下方式创建。

VC++制作DLL具体解释

然后就像普通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

VC++制作DLL具体解释

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
//
The following ifdef block is the standard way of creating macros which make exporting  
//
from a DLL simpler. All files within this DLL are compiled with the WIN32DLL_EXPORTS 
//
symbol defined on the command line. this symbol should not be defined on any project 
//
that uses this DLL. This way any other project whose source files include this file see  
//
WIN32DLL_API functions as being imported from a DLL, whereas this DLL sees symbols 
//
defined with this macro as being exported. 
#ifdef
WIN32DLL_EXPORTS 
#define
WIN32DLL_API __declspec(dllexport) 
#else 
#define
WIN32DLL_API __declspec(dllimport) 
#endif 
   
//
This class is exported from the Win32DLL.dll 
class WIN32DLL_API
CWin32DLL { 
public
    CWin32DLL(void); 
    //
TODO: add your methods here. 
    int Add(int x,int y); 
}; 
   
extern WIN32DLL_APIint nWin32DLL; 
extern “C”
WIN32DLL_API
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);

VC++制作DLL具体解释

假设要保证导出的函数名是不带修饰的。一定要将指定函数为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库的。

VC++制作DLL具体解释

    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:
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.
//
//       
For example:
//
//       
extern "C" BOOL PASCAL EXPORT ExportedFunction()
//       
{
//           
AFX_MANAGE_STATE(AfxGetStaticModuleState());
//           
// normal function body here
//       
}
//
//       
It is very important that this macro appear in each
//       
function, prior to any calls into MFC.  This means that
//       
it must appear as the first statement within the
//       
function, even before any object variable declarations
//       
as their constructors may generate calls into the MFC
//       
DLL.
//
//       
Please see MFC Technical Notes 33 and 58 for additional
//       
details.

  

    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是描写叙述方法的地址索引。能够改动,也能够不使用。系统会生成默认的。事实上不只函数有,变量也有。

VC++制作DLL具体解释

假设同一时候使用了.def和__declspec(dllexport)导出,编译器会优先使用.def文件的导出。

.def文件的导出默认是C编译的,即和extern “c” __declspec(dllexport)的导出效果一样。

导入函数的方法和前面使用的一样。

以下仅仅说一下导入变量的方法。

  1. 直接通过keyword__declspec(dllimport) int nDLLValue;
  2. 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

  3.    MFC扩展DLL

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
oldHInst = AfxGetResourceHandle();
HINSTANCE
hInst = LoadLibrary(
"ExDll.dll");
AfxSetResourceHandle(hInst);


CMyDlg
dlg;
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在设置上必须一致

  1. 是否动态链接到MFC库。
  2. 执行时库也必须一致。

    MD/MDd;MT/MTd。

  3. 字符集是否一致。是否Unicode之类的必须一致。
  4. 导入导出的声明必须一致。
  5. 须同样的修饰名extern “C”。
  6. 必须同样的调用方式,_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的,必须使用资源切换。

源代码下载

上一篇:git提交报错


下一篇:SQL Server 索引结构