程序的所有代码和数据都存在于进程空间之中。 程序是在进程空间内运行的。进程空间是应用程序运行的基本环境,没有进程空间就根本无法运行程序。 在EXE文件中,程序的数据引用关系和过程调用关系是用相对地址表示的,当程序加载到进程空间中的绝对地址上时,操作系统需要将对相对地址的引用和调用关系调整为对绝对地址的引用和调用关系,这一过程称为“重定位”。需要重定位的地方称为重定位项,它是保存在EXE文件的表头信息当中。 程序的运行是需要堆栈的,因为堆栈是过程调用必须具备的基本设施,也是过程中局部数据和过程参数生死存亡的地方。操作系统会根据记录在EXE文件头中的堆栈大小值,确定堆栈空间的地址位置和大小,并首先映射最小堆栈空间的那部分内存。 如果,一个EXE程序与某些DLL有固定的引用关系,操作系统将把相关的DLL程序调入当前进程空间。EXE和DLL以及DLL之间的引用或调用关系是用引入表和引出表来描述的,它们也保存在EXE或DLL的文件的表头信息当中。操作系统根据引入表和引出表信息,将程序模块间的引用和调用连接起来,这一过程称为“动态连接” 一个DLL文件只是程序的一部分,它并不是完整的可执行程序。因此,DLL不能单独地运行,Windows也不会为DLL模块创建进程空间。一个DLL模块必须被加载到一个EXE程序的进程空间中,才能发挥其程序功能。 一个DLL文件也含有重定位信息,操作系统将DLL加载到进程空间中时,同样需要对DLL模块进行重定位。如果,被加载的DLL与其他DLL文件又有固定的引用关系,则加载该DLL模块时将同时加载其引用的DLL模块,并完成动态连接过程。 一个DLL文件中记录的堆栈大小总是为零,因为DLL只是为进程服务的,调用DLL模块时,使用的是进程的堆栈。 对于单线程的应用程序,进程只有一个堆栈,就是进程的主线程使用的堆栈。 对于多线程的应用程序,每个正在运行的线程都有自己的堆栈。在多线程的情况下,DLL模块在那个线程中被调用,则DLL就使用该线程的堆栈。但所有这一切都还是在同一进程空间中进行的。 如果,你使用Windows API的LoadLibrary函数动态地加载一个DLL文件,其返回的模块句柄的值,就是指向该DLL加载到进程空间中的地址值。如果,你反复调用LoadLibrary加载同一个DLL,你会发现他们返回的模块句柄值是相同的,Windows只是增加该DLL的引用计数。其实,在不同Windows进程之间,DLL是共享的,只是,在不同的进程空间有不同的映射地址。 一个DLL模块的文件扩展名不一定都是*.DLL。象DELPHI的BPL包文件,ActiveX对象的OCX文件,以及设备驱动程序的DRV和VXD文件都是DLL。当然,一个EXE模块的文件扩展名也不一定要是*.EXE,只是,非*.EXE的可执行文件是不能通过双击鼠标来执行的,需要由编写程序语句来调入并运行。 DLL只有在进程空间内才能运行,这是一个基本的原则,请你一定牢记。 但有时,在Windows中的一个DLL本身已经是一个完整的程序,就缺少运行的进程空间。这时,Windows就用Rundll32命令来运行该DLL程序。Rundll32是一个简单的EXE文件,就在WINDOWS目录中,它仅仅为DLL程序提供了可运行的进程空间。 此外,在多层体系结构应用程序开发中,存在于DLL模块中的商业对象,也是需要运行的进程空间的。如果,你的客户端应用程序和DLL应用服务器在同一台机器中,则DLL应用服务器中的商业对象是直接在你客户端程序的进程空间中运行的,这就是所说的In-Process模式。而对于Out-of-Process的对象调用模式,一般要求应用服务器是一个可执行的EXE文件。 当然,如果应用服务器和客户端程序是在不同的机器中,应用服务器肯定要具备一个进程空间才能工作。例如,你无法通过直接的DCOM连接远端机器上的DLL应用服务器,这是因为DLL没有可运行的进程空间的缘故。不过,你可以通过Socket连接远端机器上的DLL应用服务器,这是因为DLL实际是在SCKTSRVR.EXE的进程空间中运行的,而SCKTSRVR.EXE是监听Socket连接的服务程序。 在MTS模式的分布式多层应用程序开发中,所有的商业对象必须存在于DLL文件的应用服务器中。这些DLL中的商业对象共同存在于MTS的进程空间中,因此,MTS才可在自己的进程空间中调度和管理这些商业对象,以完成对象的事务控制、对象缓冲和连接共享等强大功能。