先简单复习一下Windows中的逻辑驱动器、目录和路径等基本概念。
Windows中的文件组织方式与DOS操作系统类似,也是采用分层次的结构:计算机中可以安装有多个物理驱动器,每个物理驱动器可以分为多个主分区和扩展分区,每个主分区就是一个逻辑驱动器,而每个扩展分区可以划分成多个逻辑驱动器,逻辑驱动器组成了我们熟悉的C盘与D盘等盘符。
如图10.6所示,假如计算机上安装了2个硬盘和2个光驱,每个硬盘都分为一个主分区和一个扩展分区,其中第一个硬盘的扩展分区中又分为3个逻辑驱动器,第二个硬盘的扩展分区分为2个逻辑驱动器,那么计算机中的逻辑驱动器就会从C盘排列到K盘为止。
对于每个逻辑驱动器,可以给它取一个标号叫做“卷标”(Volume Label),卷标是当做一个目录项存放在逻辑驱动器的根目录中的。在每个逻辑驱动器中可以有多个文件,文件可以存放在各个目录中,目录是按照多层树状结构来安排的,每个逻辑驱动器中有个顶层目录叫做“根目录”,根目录下可以安排多个子目录,每个子目录中也可以包含多个下层子目录,一个逻辑驱动器中能够存放文件和子目录的数量只受限于驱动器的空间大小。
虽然同一个子目录中的文件名必须是惟一的,但不同的子目录中可以存在同名的文件,所以只有文件名的话并不能惟一确定一个文件,要惟一确定文件还需要指出文件的位置,除了指出文件位于的逻辑驱动器外,还要指出从根目录开始一直到文件所在目录为止的所有子目录名,这就是路径。
对一个进程来说,Windows维护一个当前驱动器,并为每个逻辑驱动器维护一个当前路径,如果不指定路径,表示要操作的文件就位于当前驱动器的当前路径下。如果要操作非当前路径下的文件,就必须明确指出包含全路径的文件名。比如指定一个文件System.ini,如果当前目录下有这个文件,那么操作的对象就是这个文件,如果当前目录下并没有这个文件,即使其他目录中存在多个同名的文件,程序也无法知道它究竟对应哪个文件。
Win32中有一部分函数专门用来完成与逻辑驱动器及目录有关的操作,在本节中将具体讨论这些函数的使用方法。
10.3.1 逻辑驱动器操作
1. 卷标操作
为一个驱动器创建、修改以及删除卷标都使用SetVolumeLabel函数。如果要创建或修改卷标(如果原来没有卷标则为创建,原来存在卷标则为修改)可以这样使用:
szPath db 'c:',0
szVolume db 'System',0
invoke SetVolumeLabel,addr szPath,szVolume
本例中,C盘的卷标会被设置为System,第一个参数指出了要设置卷标的逻辑驱动器的根目录,如果要设置C盘的卷标,目录名既不能写为“c:”也不能写为“c:windows”,必须写为“c:”,否则函数调用会失败;第二个参数则指向包含卷标字符串的缓冲区。
删除一个逻辑驱动器的卷标有两种方法。方法一是:
szPath db 'c:',0
szVolume db 0
invoke SetVolumeLabel,addr szPath,szVolume
方法二是:
szPath db 'c:',0
invoke SetVolumeLabel,addr szPath,NULL
这两种方法的执行结果是一样的。如果函数执行成功,返回值是TRUE,否则返回FALSE。
获取卷标可以使用下面介绍的GetVolumeInformation函数。
2. 逻辑驱动器的检测
要检测系统中当前存在多少个逻辑驱动器可以使用GetLogicalDrives函数,函数返回了所有可用的盘符。GetLogicalDrives函数没有输入参数,它返回一个32位的整数,用其中的每一位代表是否存在一个逻辑驱动器。由于系统中可用的盘符仅有26个(A:~Z:),所以32位已经可以反映出所有的逻辑驱动器以及它们的盘符分布情况了,返回值的第0位到第25位分别代表驱动器A:~Z:是否存在,如系统中存在A,C,D,E和F 5个逻辑驱动器的时候,返回值的二进制数值为00000000000000000000000000111101b,也就是16进制的0000003dh。
如果认为GetLogicalDrives函数返回的数据要进行位测试比较麻烦,可以使用另一个函数:GetLogicalDriveStrings,这个函数返回字符串类型的逻辑驱动器列表:
invoke GetLogicalDriveStrings,dwBufferSize,lpBuffer
lpBuffer指向一个缓冲区,函数在这里返回“A:”,0,“B:”,0,“C:”,0,0格式的字符串,凡是存在的逻辑驱动器都会列在这个字符串中,字符串列表以一个附加的0结束;dwBufferSize指出缓存区的大小,如果缓冲区不够大,后面的数据会被截尾。
获取了逻辑驱动器的分布情况后,有时候还必须了解某个逻辑驱动器的类型,因为它可能是各种类型的盘——软盘、硬盘、光驱和内存中的虚拟盘等都是以逻辑驱动器的模样出现的。虽然文件操作函数中可以不必理会文件究竟位于什么样的驱动器上,只要指定全路径的文件名就可以透明地工作,但有时候必须检测盘的类型,比如,希望对软件进行保护,要求文件必须位于光盘上,那么就需要检查文件所在的逻辑驱动器是否是光盘;另外,需要建立一个临时文件的时候,如果建立在只读的光盘上是不会成功的,为了保证创建的成功,需要预先检测一下程序是否运行于硬盘上。
检测驱动器类型的工作可以用GetDriveType函数来完成:
szPath db 'c:',0
invoke GetDriveType,addr szPath
该函数惟一的一个参数指向存放有逻辑驱动器根目录的字符串的缓冲区,函数的返回值是逻辑驱动器的类型,它可能是下面取值中的一种:
● 0—驱动器类型无法检测。
● 1—指定的根目录不存在。
● DRIVE_REMOVABLE—可移动介质,如软盘。
● DRIVE_FIXED—固定盘,如硬盘中的逻辑驱动器。
● DRIVE_REMOTE—远程驱动器,如网络上映射的驱动器。
● DRIVE_CDROM—光盘。
● DRIVE_RAMDISK—内存虚拟盘。
如果需要更详细的情况,可以使用GetVolumeInformation函数,这个函数可以返回逻辑驱动器的卷标、序列号和文件系统类型等属性:
invoke GetVolumeInformation,lpRootPathName,
lpVolumeNameBuffer,dwVolumeNameSize,
lpVolumeSerialNumber,lpMaximumComponentLength,
lpFileSystemFlags,
lpFileSystemNameBuffer,dwFileSystemNameSize
参数lpRootPathName指向需要检测的驱动器根目录字符串,如果要检测的是网络上的驱动器,那么字符串可以是“\服务器名共享名”格式。
后面的各个参数指向一些用来返回数据的缓冲区。
lpVolumeNameBuffer指向一个字符串缓冲区,用来返回驱动器的卷标,缓冲区的长度由dwVolumeNameSize参数指出。
lpVolumeSerialNumber指向一个双字变量,函数在这里返回逻辑驱动器的序列号。序列号是驱动器被格式化的时候由系统随机生成的一个32位数,它保存在位于驱动器第一个扇区的引导记录中。在程序的运行中检测并记录软盘的序列号就可以检测到软盘是否被更换。
lpFileSystemFlags指向一个双字变量,函数在这里返回最大允许的文件名长度,在Windows系统中一般这个数值是255。
lpFileSystemFlags也指向一个双字变量,函数在这里返回一些逻辑驱动器的属性标志,返回值可能是下面数值的组合:
● FS_CASE_IS_PRESERVED——文件系统在保存文件名的时候保持它的大小写(如DOS就不是这样,它把所有的文件名转换成大写后保存到目录区)。
● FS_CASE_SENSITIVE——支持区分大小写的文件名(在Windows中文件名不区分大小写,如Abc.exe和aBc.ExE指的是同一个文件)。
● FS_UNICODE_STORED_ON_DISK——允许存放Unicode格式的文件名。
● FS_PERSISTENT_ACLS——支持ACL(访问控制列表),ACL用于安全性管理,它是一个为个人或组委派或否认特定访问权限的条目列表。NTFS文件系统支持ACL,而FAT系统不支持。
● FS_FILE_COMPRESSION——支持文件压缩。
● FS_VOL_IS_COMPRESSED——支持卷压缩。
lpFileSystemNameBuffer指向一个字符串缓冲区,用来接收文件系统字符串,函数在这里返回类似于“FAT”、“FAT32”或“NTFS”类型的字符串,dwFileSystemNameSize参数指出了这个缓冲区的长度。
检测逻辑驱动器剩余空间的GetDiskFreeSpace函数也是一个常用的函数,它的用法如下:
invoke GetDiskFreeSpace, lpRootPathName,
lpSectorsPerCluster,lpBytesPerSector,
lpNumberOfFreeClusters,lpTotalNumberOfClusters
同样,参数lpRootPathName指向需要检测的驱动器根目录字符串,后面的参数指向一些双字变量,用来接收返回的数据:
● lpSectorsPerCluster参数——返回每簇的扇区数。
● lpBytesPerSector参数——返回每扇区的字节数。
● lpNumberOfFreeClusters参数——返回驱动器中未使用的簇的数量。
● lpTotalNumberOfClusters参数——返回驱动器中簇的总数。
驱动器的总容量可以通过算式计算出来:簇总数×每簇扇区数×每扇区字节数,驱动器中空闲的字节数则等于:未使用的簇×每簇扇区数×每扇区字节数。
10.3.2 目录操作
1. 创建和删除目录
创建目录使用CreateDirectory函数,例如:
szDir db ‘c:dir1dir2’,0
...
invoke CreateDirectory,addr szDir,NULL
这两句代码在c:dir1目录下创建一个名为dir2的新子目录。如果创建成功,函数返回TRUE,否则返回FALSE。
在创建目录的时候要注意几个要点:首先是要创建目录的上层目录必须存在,上面的例子中,假如c:dir1目录不存在,函数不会创建到dir2的目录;其次是与新建目录同名的目录或文件不能存在,假如c:dir1目录中已经存在一个名为dir2的目录或文件,那么创建工作就会失败。由于文件名和目录名在磁盘目录区中的存放格式是一样的,惟一的不同是目录项的属性不同(在10.2.3节中,已经发现判别找到的目录项是文件还是目录的惟一办法就是检测FILE_ATTRIBUTE_DIRECTORY属性),所以,连同名文件的存在也是不允许的。
删除目录使用RemoveDirectory函数,删除上面创建的dir2目录的方法是:
szDir db ‘c:dir1dir2’,0
...
invoke RemoveDirectory,addr szDir
如果删除成功,函数返回TRUE,否则返回FALSE。
删除目录也要注意几个要点:首先,被删除的是参数中指出的最后一级目录,如前面代码只删除dir2目录,而不会将dir1目录和dir2目录一起删掉;其次,在删除目录之前必须删除目录中的所有文件以及子目录,函数无法删除一个不为空的目录;最后,函数执行以后目录是被“真正”删除掉了,不会像用手工删除一样还可以在回收站中恢复回来。
2. 一些特殊目录
这里列出了Windows操作系统中的一些特殊目录:
● 当前目录——所有未指定路径的文件名均默认使用这个目录。
● Windows目录——Windows操作系统的安装目录。
● 系统目录——Windows安装目录下存放系统文件的目录,Windows 9x下是System目录,Windows NT下是System32目录。
● 临时目录——存放临时文件的目录,系统可以在磁盘空间不足的时候自动删除里面的文件。
这些目录在编程的时候是经常需要检测的,如要编写系统程序的时候常常要把dll文件拷贝到系统目录或Windows目录中去;而要建立临时文件的时候最好使用系统指定的临时目录,以便自动回收使用的空间。Win32中专门设置了几个函数来获取这些目录的位置:
invoke GetCurrentDirectory,dwBufferSize,lpBuffer ;获取当前目录
invoke GetTempPath,dwBufferSize,lpBuffer ;获取临时目录
invoke GetWindowsDirectory,lpBuffer,dwBufferSize ;获取Windows目录
invoke GetSystemDirectory,lpBuffer,dwBufferSize ;获取系统目录
参数lpBuffer指向一个缓冲区,用来接收返回的路径字符串,dwBufferSize指出了缓冲区的大小,一般把缓冲区的大小设置为MAX_PATH。
这几个函数有些奇怪是:GetWindowsDirectory和GetSystemDirectory函数的参数和通常的习惯一致,把缓冲区指针lpBuffer放在前面,而GetCurrentDirectory和GetTempPath函数却把dwBufferSize参数放在前面,不注意的话很容易搞错;另外,GetTempPath函数返回的路径的最后竟然包括“”,在笔者的Windows XP操作系统中,它的返回值是“C:DOCUME~1ADMINI~1LOCALS~1Temp”,而其他3个函数返回的路径最后并不带“”,读者在路径后面添加文件名的时候最好首先检测一下字符串的最后是不是已经包含了一个“”字符,否则会构造出一个类似于“c:temp\abc.dat”之类的无效的文件名。
当程序运行的时候,默认的当前路径是执行程序所在的目录,但这不是绝对的,比如使用GetOpenFileName等函数弹出一个打开文件的系统对话框,用户在里面指定了其他目录后,当前路径就会被设置到那个目录中。
读者也可以自己调用SetCurrentDirectory函数修改当前路径:
szDir db ‘c:dir1dir2’,0
...
invoke SetCurrentDirectory,addr szDir
这几句代码将当前路径设置到“c:dir1dir2”目录中。
原文始发于微信公众号(汇编语言):第10章 内存管理和文件操作rnrnrn10.3 驱动器和目录(1)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论