[导读]自从微软推出16位的Windows操作系统起,此后每种版本的Windows操作系统都非常依赖于动态链接库(DLL)中的函数和数据,实际上Windows操作系统中几乎所有的内容都由DLL以一种或另外一种形式代表着,例如显示的字体和图标存储在GDI DLL中、显示Windows桌面和处理用户的输入所需要的代码被存储在一个User DLL中、Windows编程所需
DllMain 函数
Windows 在加载 DLL 的时候,需要一个入口函数,就如同控制台或 DOS 程序需要 main 函数、 WIN 32
程序需要 WinMain 函数一样。在前面的例子中, DLL 并没有提供 DllMain 函数,应用工程也能成功引用 DLL ,
这是因为 Windows 在找不到 DllMain 的时候,系统会从其它运行库中引入一个不做任何操作的缺省 DllM ain
函数版本,并不意味着 DLL 可以放弃 DllMain 函数。
根据编写规范, Windows 必须查找并执行 DLL 里的 DllMain 函数作为加载 DLL 的依据,它使得 DLL
得以保留在内存里。这个函数并不属于导出函数,而是 DLL 的内部函数。这意味着不能直接在应用工程中 引
用 DllMain 函数,DllMain 是自动被调用的。
BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll,DWORD reason ,LPVOID flmpload) { switch(reason) { ……………//初始化代码; } return true; } |
//Dlltest.h #pragma comment(lib,"MyDll.lib") extern "C"_declspec(dllimport) int Max(int a,int b); extern "C"_declspec(dllimport) int Min(int a,int b); //TestDll.cpp #include<stdio.h> #include"Dlltest.h" void main() { int a; a=min(8,10) printf("比较的结果为%d\n",a); } |
#include <studio.h> #include<widows.h> void main(void) { typedef int(*pMax)(int a,int b); typedef int(*pMin)(int a,int b); HINSTANCE hDLL; PMax Max HDLL=LoadLibrary("MyDll.dll");//加载动态链接库MyDll.dll文件; Max=(pMax)GetProcAddress(hDLL,"Max"); A=Max(5,8); Printf("比较的结果为%d\n",a); FreeLibrary(hDLL);//卸载MyDll.dll文件; } |
使用显式链接应用程序编译时不需要使用相应的Lib文件。另外,使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号,如将GetProcAddress(hDLL,"Min")改为GetProcAddress(hDLL, MAKEINTRESOURCE(2))(函数Min()在DLL中的顺序号是2),这样调用DLL中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。
另一种详细解释如下:
前几天有个朋友问道这个问题,结果因为以前从没搞过这个,对vs2005也不熟悉,竟花了2个小时才搞定, 。
特地拿来与大家分享,希望能给像我这样的菜鸟们一些帮助,O(∩_∩)O
【第一步】创建自己的dll
1.打开vs2005,选择菜单【File-New-Project】,在弹出对话框中选择[Visual C++]下的[Win32]-[Win32 Console Application],输入工程名后确认。
2.在弹出的对话框中选择[next],在Application Settiongs中选择Application type为Dll,Additional options选择Empty project,然后点Finish。
这时就创建了一个空的可以生成dll文件的工程。
3.在工程中添加一个头文件(这里为dll_test.h),在头文件中写入如下内容:
1 #ifndef _DLL_TUTORIAL_H
2 #define _DLL-TUTORIAL_H
3
4 #include<iostream>
5
6 #if defined DLL_EXPORT
7 #define DECLDIR _declspec(dllexport)
8 #else
9 #define DECLDIR _declspec(dllimport)
10 #endif
11
12 extern "C"
13 {
14 DECLDIRint Add(int a, int b);
15 DECLDIR void Function(void);
16 }
17
18 #endif
这里要说明的是:
在VC中有两个方法来导出dll中定义的函数:
(1) 使用__declspec,这是一个Microsoft定义的关键字。
(2) 创建一个模板定义文件(Module-Definition File,即.DEF)。
第一种方法稍稍比第二种方法简单,在这里我们使用的是第一种方法。
__declspec(dllexport)函数的作用是导出函数符号到在你的Dll中的一个存储类里去。
当下面一行被定义时我定义DECLDIR宏来运行这个函数。
#defineDLL_EXPORT
在此情况下你将导出函数Add(int a,int b)和Function().
4.创建一个源文件(名字为dll_test.cpp),内容如下:
1 #include <iostream>
2 #define DLL_EXPORT
3 #include "dll_test.h"
4
5 extern "C"
6 {
7 // 定义了(DLL中的)所有函数
8 DECLDIR int Add( int a, int b )
9 {
10 return( a + b );
11 }
12
13 DECLDIR void Function( void )
14 {
15 std::cout << "DLL Called!" << std::endl;
16 }
17 }
18
【第二步】使用创建好的DLL
现在已经创建了DLL,那么如何在一个应用程序中使用它呢?
当DLL被生成后,它创建了一个.dll文件和一个.lib,这两个都是使用dll时需要用到的。
在具体介绍之前先看一下dll的链接方式。
(1)隐式连接
这里有两个方法来载入一个DLL,一个方法是只链接到.lib文件,并将.dll文件放到要使用这个DLL的项目路径中。
因此,创建一个新的空的Win32控制台项目并添加一个源文件。将我们创建好的DLL放入与新项目相同的目录下。同时我们还必须链接到dll_test.lib文件。
可以在项目属性中设置,也可以在源程序中用下面的语句来链接:
#pragma comment(lib, "dll_test.lib")
最后,我们还要在新的win32控制台项目中包含前面的dll_test.h头文件。可以把这个头文件放到新建win32控制台项目的目录中然后在程序中加入语句:
#include "dll_test.h"
新项目代码如下:
#include<iostream>
#include "DLLTutorial.h"
int main()
{
Function();
std::cout<<Add(32, 56)<< endl;
return 0;
}
(2)显示链接
稍微复杂一点的加载DLL的方法需要用到函数指针和一些Windows函数。但是,通过这种载入DLL的方法,不需要DLL的.lib文件或头文件,而只需要DLL即可。
下面列出一些代码:
/****************************************************************/
#include <iostream>
#include <windows.h>
typedef int (*AddFunc)(int,int);
typedef void (*FunctionFunc)();
int main()
{
AddFunc _AddFunc;
FunctionFunc_FunctionFunc;
HINSTANCEhInstLibrary = LoadLibrary("DLL_Tutorial.dll");
if (hInstLibrary== NULL)
{
FreeLibrary(hInstLibrary);
}
_AddFunc =(AddFunc)GetProcAddress(hInstLibrary, "Add");
_FunctionFunc =(FunctionFunc)GetProcAddress(hInstLibrary, "Function");
if ((_AddFunc ==NULL) || (_FunctionFunc == NULL))
{
FreeLibrary(hInstLibrary);
}
std::cout<< _AddFunc(23, 43) << std::endl;
_FunctionFunc();
std::cin.get();
FreeLibrary(hInstLibrary);
return(1);
}
/*******************************************************************/
首先可以看到,这里包括进了windows.h头文件,同时去掉了对dll_test.h头文件的包含。原因很简单:因为windows.h包含了一些Windows函数,
它也包含了一些将会用到的Windows特定变量。可以去掉DLL的头文件,因为当使用这个方法载入DLL时并不需要其头文件。
下面你会看到:以下面形式的一小块古灵精怪的代码:
typedef int(*AddFunc)(int,int);
typedef void (*FunctionFunc)();
这是函数指针。因为这是一个关于DLL的自学指南,深入探究函数指针超出了本指南的范围;因此,现在我们只把它们当作DLL包含的函数的别名。
我喜欢在尾部用“Func”命名之。(int,int)部分是这个函数的参数部分,比如,Add函数要获得两个整数;因此,你需要它们
(译注:指(int,int)部分)作为函数指针的参数。Function函数没有参数,因此你让它为空。main()部分中的前面两行是声明函数指针以使得你可
以认为它们等同于DLL内部的函数。我只是喜欢预先定义它们。
一个HINSTANCE是一个Windows数据类型:是一个实例的句柄;在此情况下,这个实例将是这个DLL。你可以通过使用函数LoadLibrary()获得DLL的
实例,它获得一个名称作为参数。
在调用LoadLibrary函数后,你必需查看一下函数返回是否成功。你可以通过检查HINSTANCE是否等于NULL(在Windows.h中定义为0或Windows.h包
含的一个头文件)来查看其是否成功。如果其等于NULL,该句柄将是无效的,并且你必需释放这个库。换句话说,你必需释放DLL获得的内存。
如果函数返回成功,你的HINSTANCE就包含了指向DLL的句柄。一旦你获得了指向DLL的句柄,你现在可以从DLL中重新获得函数。
为了这样作,你必须使用函数GetProcAddress(),它将DLL的句柄(你可以使用HINSTANCE)和函数的名称作为参数。你可以让函数指针获得由
GetProcAddress()返回的值,同时你必需将GetProcAddress()转换为那个函数定义的函数指针。举个例子,对于Add()函数,你必需将GetProcAddress()
转换为AddFunc;这就是它知道参数及返回值的原因。现在,最好先确定函数指针是否等于NULL以及它们拥有DLL的函数。
这只是一个简单的if语句;如果其中一个等于NULL,你必需如前所述释放库。一旦函数指针拥有DLL的函数,你现在就可以使用它们了,但是这里有一个
需要注意的地方:你不能使用函数的实际名称;你必需使用函数指针来调用它们。在那以后,所有你需要做的是释放库如此而已。
现在你知道了DLL的一些基本知识。你知道如何创建它们,你也知道如何用两种不同的方法链接它们。这里仍然有更多的东西需要我们学习,但我把它们留给你们自己探索了和更棒的作者来写了。