分析了Windows下不同的动态内存分配方式.doc_第1页
分析了Windows下不同的动态内存分配方式.doc_第2页
分析了Windows下不同的动态内存分配方式.doc_第3页
分析了Windows下不同的动态内存分配方式.doc_第4页
分析了Windows下不同的动态内存分配方式.doc_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

分析了Windows下不同的动态内存分配方式这里的动态内存包含以下两个方面的内容: 1.内存。这里的内存指的是进程的虚拟内存空间。在Win32环境下,每一个进程拥有独立的,大小为4G(0x0000 0000 0xFFFF FFFF)的虚拟内存空间。 2.动态。这里的动态指的是进程虚拟内存空间中的动态内存区域。在一个进程的虚拟内存空间中,只有动态内存可以在运行是被应用程序自由的分配/使用/释放。在Win32环境下,我们可以使用多种方式来分配/使用/释放动态内存,这些方式包括:1.Win32 API. 这些API包括VirtualXXX(),HeapXXX(),LocalAlloc(),GlobalAlloc()。2.C Run-Time Library.这些函数包括malloc(),free()。3.C+提供的关键词new和关键词delete。有这么多的内存分配方式,我们在学习和实际项目中编码过程中常常会为使用那种方式而感到迷惑。他们的内部实现是否相同?他们之间有什么本质的区别?他们各自的使用场合又是怎样的? 本文试图通过深入探究他们的本质,为正确理解和使用他们提供一些依据。首先,我们最好从全局的高度把握他们之间的关系。这里有一张图很好的描述了他们之间的层次关系:这张图给了我们一个全景,仅从这张图我们就可以清楚地看到他们之间的层次关系: 第一层:Win32 API作为系统的接口,提供了一组操作虚拟内存的接口; 第二层:Heap作为虚拟内存的一部分,Win32 API又提供了一组操作Heap内存的接口,但是这些接口是建立在操作虚拟内存的接口的基础上。 第三层:Windows平台下的C Run-Time Library 又利用Heap API来实现malloc和free。由此我们可以看出,这些动态内存操作方式之间存有单一的层次关系,位于这个层次的最低层的是Virtual Memory API,可以说这些方式都是建立在Virtual Memory API的基础上。下面就从Virtual Memory API开始,逐层分析他们之间的区别:一.Virtual Memory API 作为Windows系统提供的最核心的对虚拟内存操作的接口,也作为其他几种方式的基础,Virtual Memory API应该在几种方式中是最通用,也是功能最强大的一种方式。如果想对Virtual Memory API的使用深入的了解,可以参阅Programming Application for Windows(By Jeffrey Richter)二.Heap Memory API 我们在学习进程内存空间映象的时候,也提到了Heap这个概念,那个时候Heap指的是一段由应用程序在运行时动态分配的内存段(Segment),和其他的内存段(代码段,数据段,栈段等)构成了进程的内存空间。而这里的Heap指的是进程拥有的一种对象(Windows中有很多对象,例如WINDOW,ICON,BRUSH),当我们创建一个Heap对象的时候,我们就可以获得这个对象的Handle,然后我们就可以使用这个handle来使用动态内存,最后销毁这个对象。三.LocalAlloc/GlobalAlloc 这两个函数是Win16 API中遗留下来的两个函数,Win32 API为了保持兼容性才包含了这两个函数。这两个函数内部是通过Heap Memory API来操作一个特殊的Heap对象:进程的默认堆对象。每一个进程在初始化的时候,都会创建一个默认的Heap对象,在进程结束的时候销毁这个默认的Heap对象。LocalAlloc和GblobalAlloc的区别仅表现在Win16环境下,在Win16环境下,内存的地址是通过段:段内偏移量来获取的,LocalAlloc()只能在同一段内分配内存,而GlobalAlloc可以跨越段边界访问内存。 在Win32环境下内存访问不存在这样的限制,所以他们表现出相同的功能。由于Heap Memory API完全可以实现他们两个的功能,所以在Win32下不推荐使用这两个函数。四.malloc/free 这两个函数是使用频率最高的两个函数,由于他们是标准C库中的一部分,所以具有极高的移植性。这里的移植性指的是使用他们的代码可以在不同的平台下编译通过,而不同的平台下的C Run-Time Library的具体实现是平台相关的,在Windows平台的C Run-Time Library中的malloc()和free()是通过调用Heap Memory API来实现的。值得注意的是C Run-Time Library拥有独立的Heap对象,我们知道,当一个应用程序初始化的时候,首先被初始化的是C Run-Time Library,然后才是应用程序的入口函数,而Heap对象就是在C Run-Time Library被初始化的时候被创建的。对于动态链接的C Run-Time Library,运行库只被初始化一次,而对于静态连接的运行库,每链接一次就初始化一次,所以对于每个静态链接的运行库都拥有彼此不同的Heap 对象。这样在某种情况下就会出问题,导致程序崩溃,例如一个应用程序调用了多个DLL,除了一个DLL外,其他的DLL,包括应用程序本身动态连接运行库,这样他们就使用同一个Heap对象。而有一个DLL使用静态连接的运行库,它就拥有一个和其他DLL不同的Heap 对象,当在其他DLL中分配的内存在这个DLL中释放时,问题就出现了。五.关键词new/关键词delete 这两个词是C+内置的关键词(keyword)。当C+编译器看到关键词new的时候,例如: CMyObject*pObj=newCMyObject;编译器会执行以下两个任务:1。在堆上动态分配必要的内存。这个任务是由编译器提供的一个全局函数void* :operator new(size_t)来完成的。值得注意的是任何一个类都可以重载这个全局函数。如果类重载了这个函数的化,被类重载的那个会被调用。2。调用CMyClass的构造函数来初始化刚刚生成的对象。当然如果分配的对象是C+中的基本数据类型则不会有构造函数调用。如果要深入全局函数void* :operator new(size_t)的话,我们会发现,它的具体实现是通过调用malloc来分配内存的。有了这样的分析,我们对这些动态内存分配方式有了一个更高一级的认识,在我们的代码中就可以正确使用他们。v1.11。修改了文中关于关键词new/delete的内容,以前把关键词new和函数void* operator new(size_t)混淆了。第六章 Windows高级程序设计本节介绍Windows高级程序设计技术,掌握了这些技术,用户就可以开发较为大型的应用程序,并且能够使用Windows标准的网络程序接口进行网络程序设计了。本节主要介绍下面几方面内容: Windows内存管理; 动态连接库(DLL); Windows Sockets网络程序设计。6.1 Windows内存管理内存管理对于编写出高效率的Windows程序是非常重要的,这是因为Windows是多任务系统,它的内存管理和单任务的DOS相比有很大的差异。DOS是单任务操作系统,应用程序分配到内存后,如果它不主动释放,系统是不会对它作任何改变的;但Windows却不然,它在同一时刻可能有多个应用程序共享内存,有时为了使某个任务更好地执行,Windows系统可能会对其它任务分配的内存进行移动,甚至删除。因此,我们在Windows应用程序中使用内存时,要遵循Windows内存管理的一些约定,以尽量提高Windows内存的利用率。6.1.1 Windows内存对象Windows应用程序可以申请分配属于自己的内存块,内存块是应用程序操作内存的单位,它也称作内存对象,在Windows中通过内存句柄来操作内存对象。内存对象根据分配的范围可分为全局内存对象和局部内存对象;根据性质可分为固定内存对象,可移动内存对象和可删除内存对象。固定内存对象,特别是局部固定内存对象和DOS的内存块很类似,它一旦分配,就不会被移动或删除,除非应用程序主动释放它。并且对于局部固定内存对象来说,它的内存句柄本身就是内存对象的16位近地址,可供应用程序直接存取,而不必象其它类型的内存对象那样要通过锁定在内存某固定地址后才能使用。可移动内存对象没有固定的地址,Windows系统可以随时把它们移到一个新地址。内存对象的可移动使得Windows能有效地利用自由内存。例如,如果一个可移动的内存对象分开了两个自由内存对象,Windows可以把可移动内存对象移走,将两个自由内存对象合并为一个大的自由内存对象,实现内存的合并与碎片回收。可删除内存对象与可移动内存对象很相似,它可以被Windows移动,并且当Windows需要大的内存空间满足新的任务时,它可以将可删除内存对象的长度置为0,丢弃内存对象中的数据。可移动内存对象和可删除内存对象在存取前必须使用内存加锁函数将其锁定,锁定了的内存对象不能被移动和删除。因此,应用程序在使用完内存对象后要尽可能快地为内存对象解锁。内存需要加锁和解锁增加了程序员的负担,但是它却极大地改善了Windows内存利用的效率,因此Windows鼓励使用可移动和可删除的内存对象,并且要求应用程序在非必要时不要使用固定内存对象。不同类型的对象在它所处的内存堆中的位置是不一样的,图6.2说明内存对象在堆中的位置:固定对象位于堆的底部;可移动对象位于固定对象之上;可删除对象从堆的顶部开始分配。图6.1 内存对象分配位置示意图6.1.2 局部内存对象管理局部内存对象在局部堆中分配,局部堆是应用程序独享的自由内存,它只能由应用程序的特定实例访问。局部堆建立在应用程序的数据段中,因此,用户可分配的局部内存对象的最大内存空间不能超过64K。局部堆由Windows应用程序在模块定义文件中用HEAPSIZE语句申请,HEAPSIZE指定以字节为单位的局部堆初始空间尺寸。Windows提供了一系列函数来操作局部内存对象。6.1.2.1 分配局部内存对象LocalAlloc函数用来分配局部内存,它在应用程序局部堆中分配一个内存块,并返回内存块的句柄。LocalAlloc函数可以指定内存对象的大小和特性,其中主要特性有固定的(LMEM_FIXED),可移动的(LMEM_MOVEABLE)和可删除的(LMEM_DISCARDABLE)。如果局部堆中无法分配申请的内存,则LocalAlloc函数返回NULL。下面的代码用来分配一个固定内存对象,因为局部固定内存对象的对象句柄其本身就是16位内存近地址,因此它可以被应用程序直接存取。代码如下:char NEAR * pcLocalObject; if (pcLocalObject = LocalAlloc(LMEM_FIXED, 32) /* Use pcLocalObject as the near address of the Locally allocated object, It is not necessary to lock and unlock the fixed local object */.else /* The 32 bytes cannot be allocated .React accordingly. */6.1.2.2 加锁与解锁上面程序段分配的固定局部内存对象可以由应用程序直接存取,但是,Windows并不鼓励使用固定内存对象。因此,在使用可移动和可删除内存对象时,就要经常用到对内存对象的加锁与解锁。不管是可移动对象还是可删除对象,在它分配后其内存句柄是不变的,它是内存对象的恒定引用。但是,应用程序无法通过内存句柄直接存取内存对象,应用程序要存取内存对象还必须获得它的近地址,这通过调用LocalLock函数实现。LocalLock函数将局部内存对象暂时固定在局部堆的某一位置,并返回该地址的近地址值,此地址可供应用程序存取内存对象使用,它在应用程序调用 LocalUnlock函数解锁此内存对象之前有效。怎样加锁与解锁可移动内存对象,请看如下代码:HLOCAL hLocalObject;char NEAR *pcLocalObject;if (hLocalObject = LocalAlloc(LMEM_MOVEABLE, 32) if (pcLocalObject = LocalLock(hLocalObject) /*Use pcLocalObject as the near address of the locally allocated object */.LocalUnlock(hLocalObject);else /* The lock failed. React accordingly. */else /* The 32 bytes cannot be allocated. React accordingly. */应用程序在使用完内存对象后,要尽可能早地为它解锁,这是因为Windows无法移动被锁住了的内存对象。当应用程序要分配其它内存时,Windows不能利用被锁住对象的区域,只能在它周围寻找,这会降低Windows内存管理的效率。6.1.2.3 改变局部内存对象局部内存对象分配之后,还可以调用LocalReAlloc函数进行修改。LocalReAlloc函数可以改变局部内存对象的大小而不破坏其内容:如果比原来的空间小,则Windows将对象截断;如果比原来大,则Windows将增加区域填0(使用LMEM_ZEROINIT选项),或者不定义该区域内容。另外,LocalReAlloc函数还可以改变对象的属性,如将属性从LMEM_MOVEABLE改为LMEM_DISCARDABLE,或反过来,此时必须同时指定LMEM_MODIFY选项。但是,LocalReAlloc函数不能同时改变内存对象的大小和属性,也不能改变具有LMEM_FIXED属性的内存对象和把其它属性的内存对象改为LMEM_FIXED属性。如何将一个可移动内存对象改为可删除的,请看下面的例子:hLocalObject = LocalAlloc(32, LMEM_MOVEABLE);.hLocalObject = LocalReAlloc(hLocalObject, 32, LMEM_MODIFY| LMEM_KISCARDABLE);6.1.2.4 释放与删除分配了的局部内存对象可以使用LocalDiscard和LocalFree函数来删除和释放,删除和释放只有在内存对象未锁住时才有效。LocalFree函数用来释放局部内存对象,当一个局部内存对象被释放时,其内容从局部堆移走,并且其句柄也从有效的局部内存表中移走,原来的内存句柄变为不可用。LocalDiscard 函数用来删除局部内存对象,它只移走对象的内容,而保持其句柄有效,用户在需要时,还可以使用此内存句柄用LocalReAlloc函数重新分配一块内存。另外,Windows还提供了函数LocalSize用于检测对象所占空间;函数LocalFlags用于检测内存对象是否可删除,是否已删除,及其锁计数值;函数LocalCompact用于确定局部堆的可用内存。6.1.3 全局内存对象管理全局内存对象在全局堆中分配,全局堆包括所有的系统内存。一般来说,应用程序在全局堆中进行大型内存分配(约大于1KB),在全局堆还可以分配大于64K的巨型内存,这将在后面介绍。6.1.3.1 分配全局内存对象全局内存对象使用GlobalAlloc函数分配,它和使用LocalAlloc分配局部内存对象很相似。使用GlobalAlloc的例子我们将和GlobalLock一起给出。6.1.3.2 加锁与解锁全局内存对象使用GlobalLock函数加锁,所有全局内存对象在存取前都必须加锁。GlobalLock将对象锁定在内存固定位置,并返回一个远指针,此指针在调用GlobalUnlock之前保持有效。GlobalLock和LocalLock稍有不同,因为全局内存对象可能被多个任务使用,因此在使用GlobalLock加锁某全局内存对象时,对象可能已被锁住,为了处理这种情况,Windows增加了一个锁计数器。当使用GlobalLock加锁全局内存对象时,锁计数器加1;使用GlobalUnlock解锁对象时,锁计数器减1,只有当锁计数器为0时,Windows才真正解锁此对象。GlobalAlloc和GlobalLock的使用见如下的例子:HGLOBAL hGlobalObject;char FAR * lpGlobalObject;if (hGlobalObject = GlobalAlloc(GMEM_MOVEABLE, 1024) if (lpGlobalObject = GlobalLock(hGlobalObject) /* Use lpGlobalObject as the far address of the globally allocated object. */.GlobalUnlock (hGlobalObject);else /* The lock failed .React accordingly. */else /* The 1024 bytes cannot be allocated. React accordingly. */6.1.3.3 修改全局内存对象修改全局内存对象使用GlobalReAlloc函数,它和LocalReAlloc函数很类似,这里不再赘述。修改全局内存对象的特殊之处在于巨型对象的修改上,这一点我们将在后面讲述。6.1.3.4 内存释放及其它操作全局内存对象使用GlobalFree函数和GlobalDiscard来释放与删除,其作用与LocalFree和LocalDiscard类似。GlobalSize函数可以检测内存对象大小;GlobalFlags函数用来检索对象是否可删除,是否已删除等信息;GlobalCompact函数可以检测全局堆可用内存大小。6.1.3.5 巨型内存对象如果全局内存对象的大小为64KB或更大,那它就是一个巨型内存对象,使用GlobalLock函数加锁巨型内存对象将返回一个巨型指针。分配一个128KB的巨型内存对象,使用下面的代码段:HGLOBAL hGlobalObject;char huge * hpGlobalObject;if (hGlobalObject = GlobalAlloc(GMEM_MOVEABLE, 0x20000L) if (hpGlobalObject = (char huge *)GlobalLock(hGlobalObject) /* Use hpGlobalObject as the far address of the globally allocated object. */.GlobalUnlock (hGlobalObject);else /* The lock failed. React accordingly. */else /* The 128K cannot be allocated. React accordingly. */巨型内存对象的修改有一点特殊性,当对象大小增加并超过64K的倍数时,Windows可能要为重新分配的内存对象返回一个新的全局句柄,因此,巨型内存对象的修改应采用下面的形式:if (hTempHugeObject = GlobalReAlloc(hHugeObject,0x20000L,GMEM_MOVEABLE)hHugeObject = hTempObject;else /* The object could not be Reallocated. React accordingly. */6.1.4 段Windows采用段的概念来管理应用程序的内存,段有代码段和数据段两种,一个应用程序可有多个代码段和数据段。代码段和数据段的数量决定了应用程序的内存模式,图6.2说明了内存模式与应用程序代码段和数据段的关系。代码段数单段多段数据段数单段小内存模式中内存模式多段压缩内存模式大内存模式图6.2 内存模式图段的管理和全局内存对象的管理很类似,段可以是固定的,可移动的和可删除的,其属性在应用程序的模块定义文件中指定。段在全局内存中分配空间,Windows鼓励使用可移动的代码段和数据段,这样可以提高其内存利用效率。使用可删除的代码段可以进一步减小应用程序对内存的影响,如果代码段是可删除的,在必要时Windows将其删除以满足对全局内存的请求。被删除的段由Windows监控,当应用程序利用该代码段时,Windows自动地将它们重新装入。6.1.4.1 代码段代码段是不超过64K字节的机器指令,它代表全部或部分应用程序指令。代码段中的数据是只读的,对代码段执行写操作将引起通用保护(GP)错误。每个应用程序都至少有一个代码段,例如我们前面几章的例子都只有一个代码段。用户也可以生成有多个代码段的应用。实际上,多数Windows应用程序都有多个代码段。通过使用多代码段,用户可以把任何给定代码段的大小减少到完成某些任务所必须的几条指令。这样,可通过使某些段可删除,来优化应用程序对内存的使用。中模式和大模式的应用程序都使用多代码段,这些应用程序的每一个段都有一个或几个源文件。对于多个源文件,将它们分开各自编译,为编译过的代码所属的每个段命名,然后连接。段的属性在模块定义文件中定义,Windows使用SEGMENTS语句来完成此任务,如下面的代码定义了四个段的属性:SEGMENTSMEMORY_MAIN PRELOAD MOVEABLEMEMORY_INIT LOADONCALL MOVEABLE DISCARDABLEMEMORY_WNDPROC PRELOAD MOVEABLEMEMORY_ABOUT LOADONCALL MOVEABLE DISCARDABLE用户也可以在模块定义文件中用CODE语句为所有未显式定义过的代码段定义缺省属性。例如,要将未列在SEGMENTS语句中的所有段定义为可删除的,可用下面的语句:CODE MOVEABLE DISCARDABLE。6.1.4.2 数据段每个应用程序都有一个数据段,数据段包含应用程序的堆栈、局部堆、静态数据和全局数据。一个数据段的长度也不能超过64K。数据段可以是固定的或可移动的,但不能是可删除的。如果数据段是可移动的,Windows在将控制转向应用程序前自动为其加锁,当应用程序分配全局内存,或试图在局部堆中分配超过当前可分的内存时,可移动数据段可能被移动,因此在数据段中不要保留指向变量的长指针,当数据段移动时,此长指针将失效。在模块定义文件中用DATA语句定义数据段的属性,属性的缺省值为MOVEABLE和MULTIPLE。MULTIPLE属性使Windows为应用程序的每一个实例拷贝一个应用程序数据段,这就是说每个应用程序实例中数据段的内容都是不同的。6.1.5 内存管理程序示例Memory应用程序Memory示例了部分内存管理,它是一个使用了可删除代码段的中模式Windows应用程序。Memory程序有四个C语言源程序,在模块定义文件中显示定义了四个代码段,相应地模块定义文件和makefile文件有地些修改,读者可通过比较Memory程序和5.1.2节的例子来体会它们之间的不同。另外,读者在编译和连接应用程序Memory后,可用Visual C+提供的Windows Heap Walker (HEAPWALK.EXE)来观察Memory运行时的各个段。/模块1:MEMORY_MAIN#include windows.h#include memory.hHANDLE hInst;/* MODULE: memory1.c FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) PURPOSE: calls initialization function, processes message loop*/int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)HANDLE hInstance;HANDLE hPrevInstance;LPSTR lpCmdLine;int nCmdShow; MSG msg; if (!hPrevInstance)if (!InitApplication(hInstance) return (FALSE); if (!InitInstance(hInstance, nCmdShow) return (FALSE); while (GetMessage(&msg, NULL, NULL, NULL) TranslateMessage(&msg);DispatchMessage(&msg); return (msg.wParam);/模块2:MEMORY_INIT#include windows.h#include memory.h/* MODULE: memory2.c FUNCTION: InitApplication(HANDLE) PURPOSE: Initializes window data and registers window class*/BOOL InitApplication(hInstance)HANDLE hInstance; WNDCLASS wc; wc.style = NULL; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = COLOR_WINDOW+1; wc.lpszMenuName = MemoryMenu; wc.lpszClassName = MemoryWClass; return (RegisterClass(&wc);/* MODULE: memory2.c FUNCTION: InitInstance(HANDLE, int) PURPOSE: Saves instance handle and creates main window*/BOOL InitInstance(hInstance, nCmdShow) HANDLE hInstance; int nCmdShow; HWND hWnd; hInst = hInstance; hWnd = CreateWindow(MemoryWClass, Memory Sample Application, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );if (!hWnd) return (FALSE); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return (TRUE);/模块3:MEMORY_WNDPROC#include windows.h#include memory.h/* MODULE: memory3.c FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM) PURPOSE: Processes messages MESSAGES:WM_COMMAND - application menu (About dialog box)WM_DESTROY - destroy window*/long FAR PASCAL _export MainWndProc(hWnd, message, wParam, lParam)HWND hWnd;UINT message;WPARAM wParam;LPARAM lParam; FARPROC lpProcAbout; switch (message) case WM_COMMAND: if (wParam = IDM_ABOUT) lpProcAbout = MakeProcInstance(About, hInst);DialogBox(hInst, AboutBox, hWnd, lpProcAbout);FreeProcInstance(lpProcAbout);break; elsereturn (DefWindowProc(hWnd, message, wParam, lParam);case WM_DESTROY: PostQuitMessage(0); break;default: return (DefWindowProc(hWnd, message, wParam, lParam); return (NULL);/模块4:MEMORY_ABOUT#include windows.h#include memory.h/* MODULE: memory4.c FUNCTION: About(HWND, unsigned, WORD, LONG) PURPOSE: Processes messages for About dialog box MESSAGES:WM_INITDIALOG - initialize dialog boxWM_COMMAND - Input received*/BOOL FAR PASCAL _export About(hDlg, message, wParam, lParam)HWND hDlg;unsigned message;WORD wParam;LONG lParam; switch (message) case WM_INITDIALOG: return (TRUE);case WM_COMMAND: if (wParam = IDOK | wParam = IDCANCEL) EndDialog(hDlg, TRUE);return (TRUE); break; return (FALSE);下面是模块定义文件中的一小段,它在编译每个模块时,使用/NT选项为每个段进行命名。MEMORY1.OBJ:MEMORY1.C $(MEMORY1_DEP)$(CC) $(CFLAGS) $(CCREATEPCHFLAG) /c /NT MEMORY_MAIN MEMORY1.CMEMORY2.OBJ:MEMORY2.C $(MEMORY2_DEP)$(CC) $(CFLAGS) $(CUSEPCHFLAG) /c /NT MEMORY_INIT MEMORY2.CMEMORY3.OBJ:MEMORY3.C $(MEMORY3_DEP)$(CC) $(CFLAGS) $(CUSEPCHFLAG) /c /NT MEMORY_WNDPROC MEMORY3.CMEMORY4.OBJ:MEMORY4.C $(MEMORY4_DEP)$(CC) $(CFLAGS) $(CUSEPCHFLAG) /c /NT MEMORY_ABOUT MEMORY4.C6.2 动态连接库DLL使用动态连接库是Windows的一个很重要的特点,它使得多个Windows应用程序可以共享函数代码、数据和硬件,这可以大大提高Windows内存的利用率。动态连接库是一个可执行模块,它包含的函数可以由Windows应用程序调用执行,为应用程序提供服务。它和我们以前用的C函数库相比,在功能上是很类似的,其主要区别是动态连接库在运行是连接,C函数库(静态连接库)是在生成可执行文件时由连接器(LINK)连接。静态连接库中的代码在应用程序生成以后已经连接到应用程序模块之中,但动态连接库中的代码只有在应用程序要用到该代码段时才动态调入DLL中的相应代码。为了让应用程序在执行时能够调入DLL中正确的代码,Windows提供了动态连接库的引入库。Windows在连接生成应用程序时,如果使用动态连接库函数,连接器并不拷贝DLL中的任何代码,它只是将引入库中指定所需函数在DLL中位置的信息拷贝在应用程序模块中,当应用程序运行时,这些定位信息在可执行应用程序和动态连接库之间建立动态连接。静态库、引入库和动态库之间的区别如表6.1所示。表6.1 静态库、引入库和动态库之间的区别库类型连接时间范例库函数范例说明静态库连接时MLIBCEW.LIBstrcpy函数代码引入库连接时LIBW.LIBTextOut定位信息动态库运行时GDI.EXETextOut函数代码DLL不能独立执行,也不能使用消息循环。每个DLL都有一个入口点和一个出口点,具有自己的实例句柄、数据段和局部堆,但DLL没有堆栈,它使用调用程序的堆栈。DLL也包括有.C文件,.H文件,.RC文件和.DEF文件,另外,在连接时一般要加入SDK库中的LIBENTRY.OBJ文件。6.2.1 创建动态连接库要创建动态连接库,至少有三个文件: C语言源文件; 一个模块定义文件(.DEF); makefile文件。有了这些文件后,就可以运行Microsoft的程序维护机制(NMAKE),编译并连接源代码文件,生成DLL文件。6.2.1.1 创建C语言源文件和其它C应用程序一样,动态连接库可包含多个函数,每个函数要在被其它应用程序或库使用之前用FAR声明,并且在库的模块定义文件中用EXPORTS语句引出。下面给出一个完整的C语言源文件:/* PROGRAM: Dlldraw.c PURPOSE: Contains library routines for drawing*/#include windows.h #include stdlib.h#include dlldraw.h/* FUNCTION: LibMain(HANDLE, WORD, WORD, LPSTR) PURPOSE: Is called by LibEntry. LibEntry is called by Windows when the DLL is loaded. The LibEntry routine is provided in the LIBENTRY.OBJ in the SDK Link Libraries disk. (The source LIBENTRY.ASM is also provided.) LibEntry initializes the DLLs heap, if a HEAPSIZE value is specified in the DLLs DEF file. Then LibEntry calls LibMain. The LibMain function below satisfies that call. The LibMain function should perform additional initialization tasks required by the DLL. In this example, no initialization tasks are required. LibMain should return a value of 1 if the initialization is succes

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论