动态库的创建与使用
我们都知道库的作用有两个:1.文件共享的作用 2.知识产权的保护,虽然说库的功能都是一样的,但是动态库的制作比静态库稍微麻烦一些。到底哪些地方比较麻烦呢?
1)首先创建一个Win32动态链接库的工程。
2)在头文件中给出函数的声明,在源文件中给出函数的实现。
3)在打开工程的debug文件后我们发现有一个.dll文件,这就是传说中的静态库文件。但是这个文件暂时还不能使用。我们打开这个文件发现这里面都是二进制代码我们不能看懂。但是有专门针对二进制文件的工具。打开工具栏,点击depends点开后将这个.dll文件拖入,我们就可以看见相应的信息,但遗憾的是我们在这里什么都没有看见。
这就需要我们在头文件函数声明处加上一定的信息。将函数声明为导出函数。如下:
当函数声明为导出函数时,库里面就会重新生成一个 .dll文件,还会多出一个.lib文件(如果没有声明为导出函数,则不会出现.lib文件)。再打开depends工具我们就会发现一个函数而不是什么都没有。
使用方法:1)创建一个普通的文件,将动态库里面的.dll文件和.lib文件拷贝复制到新创建普通文件的debug里面。再把相对应的头文件也拷贝进去(动态库的头文件)
2)动态库使用方式分为两种,一种为静态加载方式,一种为动态加载方式。
静态加载方式:在生成动态库的同时,也生成了静态库文件,这个静态库文件的生成是为了方便静态加载动态库。在程序里面引入#pragma comment(lib,"静态库名.lib")之后就可以使用了。但是只能使用导出了的函数。没有生成的导出函数相当于库中的私有成员,只能在内部使用。
将主程序里面的.exe文件拷贝出来运行,我们发现程序不能运行,原因是不能找到相应的动态库。但是静态库的.exe文件提出来后可以随便运行,这样我们就发现了静态库与动态库的一些不同之处:静态库是一次编译到处执行,他把所用到的所有库文件信息全都包含进去,所以无论把文件放在任何地方,他的运行都不会依赖于其他文件。但动态库不可以,他的运行必须依赖于动态库和静态库。
当程序开始运行时,整个静态库全部被加载到了程序里面,如果程序结束,这个库也就自然被释放掉,只是程序的运行必须要依赖于动态库的文件。动态加载更灵活一些。但其加载方式就要相应的复杂一些。
动态加载方式:动态加载方式依赖于三个函数的应用。一个叫LoadLibrary()加载库函数,一个是从加载的库函数里获取一个处理函数的地址的函数GetProcAddress(),另一个是释放库文件的函数FreeLibrary().如果我们对这些函数不熟悉的话,我们可以查看MSDN进行查看。
要想动态加载,我们首先得加上一个头文件:#include<windows.h>
给出一个文件句柄,使用LoadLibrary(“动态库的名字.dll”)函数。条件判断后,如果加载成功,相当于文件被打开了,然后在文件里获取所需要的函数(函数必须导出过)出来。判断获取是否成功,如果成功就调动这个函数。处理完成后再调动库函数FreeLibrary()释放库文件。
这样就加载完成。但是我们发现在调用GetProcAddress()函数时调用的函数名有些看不懂,这是C++文件内部对函数的命名,我们可以这样查看:打开工具栏点击depends,然后把动态库文件拖进去查看他的名字。但是这种方式稍微麻烦了一点,我们可以换另外一种方式。
在写导出函数时给前面加上extern "C",即声明成C的扩展方式。扩展后即可以用普通的函数名来调用。这样使调用变得简便,但是必须保证函数不能重载(C语言不支持函数重载)。
动态加载完毕,如果去掉静态库文件程序照常运行。它的加载只需要.dll文件即可。
下面我们对动态库的创建与使用做一下补充:动态链接库的全局变量与类的导出。
首先使用静态加载的方式创建好动态库,在头文件里面声明一个导出的全局变量,假如是 extern int __declspec(dllexport) g_state,然后在源文件里面定义这个变量。然后我们需要在这个动态库的工程里面新建一个文件(名字跟动态链接库的名字一样,后缀名为.def,叫做模块定义),添加如下代码。
点击加载文件,选择所有文件,找到刚刚的.def文件,加载到工程内部。
重新编译,将.lib文件和.dll文件重新拷贝到工程之下。这时导出的这个全局变量是一个地址,要用的时候得把他转换成相应的函数。注意在库里面需要把全局变量声明为导出全局变量,在引用的时候也必须声明成导出全局变量。
导出类的时候也是一样的将类导出(只要导出类,类中的所有成员都会成为导出成员)。