比较大的应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的EXE程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试。
Windows系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试。在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种 方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。Microsoft Windows自己就将一些主要的系统功能以DLL模块的形式实现。例如IE中的一些基本功能就是由DLL文件实现的,它可以被其它应用程序调用和集成。
底层库提供的是一种机制,而不是满足某一个人的策略。
一般来说,DLL是一种磁盘文件(通常带有DLL扩展名),它由全局数据、服务函数和资源组成(说白了,就是一堆功能函数,有的是为了自己需要,有的是需要提供给外部使用——_declspec(dllexport)抛出去),在运行时被系统加载到进程的虚拟空间中,成为调用进程的 一部分。如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。Windows 在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。
编写动态链接库文件时,不要有中文!动态链接库工程其实就是一套API函数(可以被外部程序使用)的集成!
动态库工程会提供让函数导出来以便其他应用、工程使用的机制。在DLL代码中,必须像下面这样明确声明导出函数:_declspec(dllexport) int MyFunction(int n);
在开发调试阶段,每次对DLL工程的更新以后,一定要将新的.lib 文件和 .dll文件重新拷贝到外部应用程序的目录下。为了方便别人使用,使使用者对dll中的函数一目了然,还要编写一个.h文件,注明要抛出来的函数。
简述一下win32环境的DLL:
Debug对应Debug版本,Release对应Release版本,一定要注意版本、路径、配置动态库等对应。
1、编写win32程序下的DLL工程(注意:添加新项目一定要添加的是.c文件),并生成解决方案,在debug目录下找到如下文件并拷贝:
#include"stdio.h" #include"stdlib.h" _declspec(dllexport) int add(int a, int b) { return a + b; }
#include"stdio.h" #include"stdlib.h" void main() { printf("%d\n", add(5, 3)); }把上述拷贝的文件粘贴在 以上.c文件的同级目录下。
3、右键测试工程——属性——连接器——输入——
一个较为复杂的socketdll.c展示:(里面含有很多其他的技术细节)
#define _CRT_SECURE_NO_WARNINGS #include"stdlib.h" #include"string.h" #include"stdio.h" //理解handle句柄(底层动态库的一套资源)的概念 typedef struct SCK_HANDLE { char version[64]; char ip[128]; int port; char *p; //结构体套一级指针,注意内存的释放问题 int plen; }SCK_HANDLE;//动态库过程内部的数据类型,不想让(也不需要)外部上层应用测试程序使用 //数据类型的封装 //------------------第一套API接口---Begin--------------------------------// //客户端初始化——获取handle——handle是底层库的资源,记录着函数运行的上下文信息 _declspec(dllexport) int cltSocketInit(void **handle /*out*/) { printf("cltSocketInit begin\n"); int ret = 0; SCK_HANDLE *phandle=NULL; //SCX_HANDLE是动态库工程的变量,所以要用malloc,要在堆上分配 phandle = (SCK_HANDLE *)malloc(sizeof(SCK_HANDLE)); if (phandle == NULL) { printf("func cltSocketInit err:%d\n",ret); return ret; } memset(phandle, 0, sizeof(SCK_HANDLE)); //把指针所指向的内存空间 赋值成 0; strcpy(phandle->ip, "192.168.1.128"); phandle->port = 8081; *handle = phandle; //间接赋值,把形参甩出去 printf("cltSocketInit end\n"); return 0; } //客户端发报文 _declspec(dllexport) int cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/) { int ret = 0; SCK_HANDLE *phandle = NULL; phandle = (SCK_HANDLE *)handle; phandle->p = (char *)malloc(buflen*sizeof(char)); if (phandle == NULL) { ret = -1; printf("cltSocketSend err:%d\n", ret); return ret; } memcpy(phandle->p, buf, buflen); //别人传过来的有可能不是C风格的字符串,所以按内存块拷贝 phandle->plen = buflen; return 0; } //客户端收报文 _declspec(dllexport) int cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/) { int ret = 0; SCK_HANDLE *phandle = NULL; phandle = (SCK_HANDLE *)handle; memcpy(buf, phandle->p, phandle->plen); //别人传过来的有可能不是C风格的字符串,所以按内存块拷贝 buflen = phandle->plen; return 0; } //客户端释放资源 _declspec(dllexport) int cltSocketDestory(void *handle/*in*/) { int ret = 0; SCK_HANDLE *hdl = NULL; hdl = (SCK_HANDLE *)handle; if (hdl->p) //先释放结构体 成员域的 指针 所指向的内存空间 { free(hdl->p); } free(hdl); //再释放整个结构体内存 return 0; } //------------------第一套api接口---End-----------------------------------//