较大的应用程序由许多模块组成,这些模块分别完成相对独立的功能,它们相互配合完成整个软件系统的工作。可能会有一些具有一般功能的模块,在构建其他软件系统时仍然会用到。在构造软件系统时,如果将所有模块的源代码都静态编译成整个应用程序EXE文件,会出现一些问题:一个缺点是增加了应用程序的规模,会占用更多的磁盘空间,程序运行时也会消耗大量的内存空间,造成系统资源的浪费;另一个缺点是,在编写大型EXE程序时,每次修改和重建都必须对所有源代码进行调整和编译,增加了编译过程的复杂性,也不利于阶段性的单元测试。
Windows平台提供了一个完全不同的有效的编程和运行环境。您可以将独立的程序模块创建为较小的DLL(动态链接库)文件,并分别编译和测试它们。在运行时,只有当EXE程序调用这些DLL模块时,系统才会将它们加载到内存空间中。这种方法不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块能够被多个应用程序同时使用。Windows本身以DLL模块的形式实现了一些主要的系统功能。
一般来说,dll是一个磁盘文件。DLL,DRV。丰,SYS和许多带有。EXE扩展名可以是dll。它由全局数据、服务函数和资源组成,在运行时由系统加载到调用进程的虚拟空间中,成为调用进程的一部分。如果与其他dll没有冲突,这个文件通常映射到进程的虚拟空间中的同一个地址。DLL模块包含各种导出函数,用来对外提供服务。DLL可以有自己的数据段,但不能有自己的堆栈,使用与调用它的应用程序相同的堆栈模式;一个DLL在内存中只有一个实例;DLL实现代码封装;DLL的编译与具体的编程语言和编译器无关。
在Win32环境中,每个进程都复制自己的读/写全局变量。如果要与其他进程共享内存,必须使用内存映射文件或声明一个共享数据段。DLL模块所需的堆栈内存是从正在运行的进程的堆栈中分配的。Windows在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。Windows操作系统只将DLL映射到需要它的进程的虚拟地址空间。DLL函数中的代码创建的任何对象(包括变量)都归调用它的线程或进程所有。
呼叫模式
1.静态调用方式:编译器系统在应用结束时完成加载DLL和卸载DLL的编码(如果有其他程序在使用该DLL,则该DLL在Windows中的应用记录减1,直到所有相关程序都使用完该DLL后才释放。它简单实用,但不够灵活,不能满足一般要求。
隐式调用:您需要添加。动态连接库生成到应用程序项目时生成的LIB文件。如果要用DLL里的函数,解释一下就行了。隐式调用不需要调用LoadLibrary()和FreeLibrary()。当程序员构建DLL文件时,链接器会自动生成相应的LIB导入文件。该文件包含每个DLL导出函数的符号名和可选标识号,但不包含实际代码。LIB文件作为DLL的替代文件被编译到应用程序项目中。
程序员通过静态链接编译生成应用程序时,应用程序中的调用函数与LIB文件中导出的符号相匹配,这些符号或标识号进入生成的EXE文件。LIB文件还包含相应的DL L文件名(但不是完整的路径名),链接器将其存储在EXE文件中。
当应用程序在运行过程中需要加载DLL文件时,Windows根据这些信息找到并加载DLL,然后通过符号名或标识号动态链接DLL函数。加载应用程序EXE文件时,应用程序调用的所有DLL文件都将被加载到内存中。可执行程序链接到输入库文件(。lib文件)包含DLL输出函数的信息。操作系统在加载可执行程序时加载DLL。可执行程序通过函数名直接调用DLL的输出函数,调用方法与程序中其他函数相同。
2.动态调用方法:程序员使用API函数加载和卸载DLL来调用DLL。使用起来比较复杂,但是可以更有效的利用内存。是编译大型应用的重要途径。
显式调用:是指在应用程序中使用MFC提供的LoadLibrary或AfxLoadLibrary在自己的动态链接库中显式调用。动态链接库的文件名就是上面两个函数的参数,然后用GetProcAddress()得到你要引入的函数。从那时起,您可以像使用该应用程序的自定义函数一样调用这个导入的函数。在应用程序退出之前,应该用MFC提供的FreeLibrary或AfxFreeLibrary释放动态连接库。直接调用Win32的LoadLibary函数,指定DLL的路径作为参数。LoadLibary返回应用程序在调用GetProcAddress函数时使用的HINSTANCE参数。GetProcAddress函数将符号名或标识号转换成DLL中的地址。程序员可以决定何时加载或不加载DLL文件,显式链接决定在运行时加载哪些DLL文件。在使用DLL之前,程序必须加载DLL以获取一个DLL模块的句柄,然后调用GetProcAddress函数获取输出函数的指针,并在退出之前卸载DLL (free LoadLibrary)。
Windows将按照下列搜索顺序查找DLL:
包含EXE文件的目录
进程的当前工作目录
Windows系统目录
Windows目录
Path环境变量中列出的一系列目录
MFC中的DLL
非MFC DLL:是指没有MFC的类库结构,直接用C语言编写的DLL。它的输出函数一般使用标准的C接口,可以被非MFC或MFC编写的应用程序调用。
常规DLL:和下面的扩展DLL一样,是用MFC类库写的。明显的特点是源文件中有一个继承CWinApp的类。可以细分为静态连接MFC和动态连接MFC。
静态连接MFC的动态连接库只有专业版和企业版的VC支持。这个类似DLL的应用程序中的输出函数可以被任何Win32程序使用,包括使用MFC的应用程序。输入函数具有以下形式:
extern ' C ' EXPORT yourportedfunction();
没有extern 'C '修饰,输出函数只能从C代码调用。
DLL应用程序是从CWinApp派生的,但是没有消息循环。
动态链接到MFC的常规DLL应用程序中的输出函数可由任何Win32程序使用,包括使用MFC的应用程序。但是,DLL的所有函数输出都应该以下面的语句开始:
AFX _ MANAGE _ STATE(AfxGetStaticModuleState())
该语句用于正确切换MFC模块状态。
用支持DLL技术的所有语言编写的应用程序都可以调用常规DLL。在这个动态链接库中,它必须有一个继承自CWinApp的类,而DLLMain函数是MFC提供的,所以不需要显式编写。
扩展DLL:用来复用从MFC继承的类,也就是说这种类型的动态链接库可以用来输出从MFC继承的类。它的输出函数只能由使用MFC并动态链接到MFC的应用程序使用。你可以从MFC中继承你想要的更适合自己使用的类,提供给你的应用。你也可以随意提供MFC或MFC继承类对象指针给你的应用程序。扩展DLL是使用MFC的动态连接版本创建的,它只被用MFC类库编写的应用程序调用。扩展dll不同于常规dll,因为它没有从CWinApp继承的类对象,所以您必须为DLLMain函数添加初始化代码和结束代码。
与常规dll相比,有以下不同之处:
1.它没有从CWinApp派生的对象;
2.它必须有一个DLLMain函数;
3.DLLMain调用AfxInitExtensionModule函数,必须检查该函数的返回值。如果它返回0,DLLMAIN也返回0;
4.如果它想要输出CRuntimeClass类型的对象或资源,它需要提供一个初始化函数来创建一个CDynLinkLibrary对象。而且,需要输出初始化函数;
5.使用扩展DLL的MFC应用程序必须有一个从CWinApp派生的类。而且一般扩展DLL的初始化函数都是在InitInstance中调用的。
DLL入口函数
1.每个DLL都必须有一个入口点,DLLMain是一个默认的入口函数。DLLMain负责初始化和结束。每当一个新进程或该进程的一个新线程访问DLL,或者每个访问DLL的进程或线程不再使用DLL或结束时,将调用DLLMain。但是,使用TerminateProcess或TerminateThread结束进程或线程不会调用DLLMain。
DLLMain的函数原型:
BOOL API entry DLLMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID
LP保留)
{
开关(ul_reason_for_call)
{
案例DLL_PROCESS_ATTACH:
.
案例DLL_THREAD_ATTACH:
.
案例DLL_THREAD_DETACH:
.
案例DLL_PROCESS_DETACH:
.
返回TRUE
}
}
参数:
HMoudle:是调用动态库时传递给自己的一个句柄(实际上是_DGROUP段的选择器);
Ul_reason_for_call:是说明动态库调整原因的标志。当进程或线程加载或卸载