10.1 打开和关闭设备
10.1.1 设备的定义——在Windows中可以与之进行通信的任何东西。
(1)常见设备及用途
设备 |
用途 |
用来打开设备的函数 |
文件 |
永久存储任何数据 |
CreateFile(pszName为路径名或UNC路径名) |
目录 |
属性和文件压缩的设置 |
同上,如果指定FILE_FLAG_BACKUP_SEMANTICS标志,将打开一个目录。可以改变目录的属性(如正常或隐藏等)和他们的时间戳 |
逻辑磁盘驱动器 |
格式化驱动器 |
CreateFile(pszName为\.x:)其中的x是驱动器的盘符。可以用来格式化驱动器或检测驱动器媒介的大小 |
物理磁盘驱动器 |
访问分区表 |
CreateFile(pszName为\.PHYSICALDRIVEx:)打开物理驱动器中,其的x是物理驱动器的盘符。如\.PHYSICALDRIVE0,表示第一个物理驱动器 |
串口 |
通过电话线传输数据 |
CreateFile(pszName为"COMx") |
并口 |
将数据传输至打印机 |
CreateFile(pszName为"LPTx") |
邮件槽 |
一对多数据传输,通常是通过网络传到另一台运行的Windows的机器 |
邮件槽服务器:CreateMailslot(pszName为"\.mailslotmailslotname") 邮件槽客户端:CreateMailslot(pszName为"\servernamemailslotmailslotname") |
命名管道 |
一对一数据传输,通常是通过网络传到另一台运行Windows的机器上 |
服务器端:CreateNamePipe(pszName为 "\.pipepipename") 客户端:CreateFile(pszName为 "\servernamepipepipename") |
匿名管道 |
单机上的一对一数据传输(绝不会跨网络) |
CreatePipe用来打开服务器和客户端 |
套接字 |
报文或数据流的传输,通常是通过网络传到任何支持套按字的机器上(机器不一定要运行Windows操作系统 |
Socket、accept或AcceptEx |
控制台 |
文本窗口中的屏幕缓存 |
CreateConsoleScreenBuffer或GetStdHandle |
(2)设置与设备通信的参数,如SetCommConfig设置串口的波特率;SetMailslotInfo设置超时邮件槽的超时值。
(3)关闭通信:对于大多数设备,调用CloseHandle即可,但套接字用closesocket
(4)获取设备的类型:GetFileType,返回值如下
①FILE_TYPE_UNKNOWN:未知类型 ②FILE_TYPE_DISK:磁盘文件
③FILE_TYPE_CHAR:字符文件,一般指并口并设备或控制台
④FILE_TYPE_PIPE:命名管道或匿名管道
10.1.2 细看CreateFile
(1)CreateFile函数
参数 |
描述 |
pszName |
既可以是设备的类型,也可表示该类设备的某个实例 |
dwDesiredAccess |
数据传输的方式 0:只能改变设备的配置,不能读取数据或写入数据 GENERIC_READ:允许对设备进行只读访问 GENERIC_WRITE:只写访问(注意不可读) GENERIC_READ|GENERIC_WRITE:可读可写 |
dwShareMode |
指定设备的共享特权 ①0:独占对设备的访问。如果设备己经打开,CreateFile调用会失败。同样,如果我们成功打开设备,那样后续的CreateFile会失败。 ②FILE_SHARE_READ:只共享读(不能修改设备的数据) A、如果设备己被只写或独占方式打开,则我们的CreateFile会失败。 B、如果我们成功打开,则后续的使用GENERIC_WRITE调用CreateFile会失败。 ②FILE_SHARE_WRITE:只共享写(不能读取设备的数据) A、如果设备己被只读或独占方式打开,则我们的CreateFile会失败。 B、如果我们成功打开,则后续的使用GENERIC_READ调用CreateFile会失败。 ③FILE_SHARE_WRITE|FILE_SHARE_READ:共享读写 A、如果设备己被独占方式打开,则我们的CreateFile会失败。 B、如果我们成功打开,则后续的要求独占读取(即有dwShareMode=0, dwDesiredAccess=GENERIC_READ)、独占写入或独占读写调用CreateFile会失败。 ④FILE_SHARE_DELETE:此时当删除或移动文件时会标志为待删除,当所有打开的句柄都被关闭时,才真正的删除。 |
psa |
安全属性,通常设为NULL,默认时返回的句柄是不可继承的。 |
dwCreateDisposition |
对文件的意义比其他设备的类型更大 CREATE_NEW:创建新文件,如果存在同名文件,CREATEFILE失败 CREATE_ALWAYS:无论是否存在同名,都创建一个新文件,被覆盖旧文件 OPEN_EXISTING:打开己有的文件或设备,如果不存在,CREATEFILE失败。如果是打开文件之外的设备,必须设置该标志。 OPEN_ALWAYS:如果文件己存在,直接打开。如果不存在,则先创建再打开。 TRUNCATE_EXISITING:打开己有的文件并将文件大小截断为0 |
dwFlagsAndAttributes |
用于微调与设备之间的通信(如果是文件,则设置文件的属性)(见后面第2点的分析) |
hFileTemplate |
可以为NULL,也可以是一个文件句柄 ①如果hFileTemplate标识一个文件句柄,则dwFlagsAndAttributes参数会被忽略,转而使用hFileTemplate模板文件的属性,但此时hFileTemplate必须先以GENERIC_READ打开。 ②如果CreateFile要打开而不是创建新文件时,则hFileTemplate被忽略 |
返回值 |
成功:返回文件或设备句柄 失败:INVALID_HANDLE_VALUE(-1),注意不是NULL! |
(2)dwFlagsAndAttributes参数的详细分析
①CreateFile的高速缓存标志
参数 |
描述 |
FILE_FLAG_NO_BUFFERING |
表示访问文件时不使用数据高速缓存技术,这时数据会直接写入我们提供的缓冲中,因直接读写磁盘,所以必须遵循一定的规则: ①在访问文件的时候,使用的偏移量必须正好是磁盘卷扇区大小的整数倍。(可用GetDiskFreeSpace来确定扇区的大小) ②读取/写入文件的字节数必须正好是扇习大小的整数倍。 ③必须确保我们的缓存在进程地址空间中的起始地址正好是扇区大小的整数倍。 |
FILE_FLAG_SEQUENTIAL_SCAN |
①只有允许对文件数据进行缓存时,这个标志才有用。否则被忽略 ②读取文件时,系统会顺序访问文件,因使用了高级缓存技术,所以每次读取的数据量可能会超过我们要求的数量。此时如果重新设置文件指针,那缓存中的数据就浪费了。如果我们必须经常自己设置文件指针,可以指定FILE_FLAG_RANDOM_ACCESS,告诉系统不要提前读取文件数据。 ③文件越大,高速缓存器为分件分配的缓存就越大。从而可能导致的打开文件失败。 |
FILE_FLAG_WRITE_THROUGH |
写入时,禁止对文件进行缓存。即系统会将所有对文件的修改直接写入磁盘。但是系统仍然会在内部的缓存中保存文件的数据。 |
②CreateFile的其他标志
参数 |
描述 |
FILE_FLAG_DELETE_ON_CLOSE |
让文件在所有的句柄都被关闭后,删除该文件。这个标志通常与FILE_ATTRIBUTE_TEMPORARY属性一起使用。即会创建一个临时文件,并在关闭的时候自动删除该文件。 |
FILE_FLAG_BACKUP_SEMANTICS |
备份与恢复软件时,会检查调用线程的访问令牌是否具备对文件或目录的备份/恢复特权,如果有才能打开该文件。因为 |
FILE_FLAG_POSIX_SEMANTICS |
在POSIX(非Windows系统)的子系统要求在查找文件名的时区分大小写。 |
FILE_FLAG_OPEN_REPARSE_POINT |
告诉系统忽略文件的重解析属性(不推荐使用该标志) |
FILE_FLAG_OPEN_NO_RECALL |
当文件长时间没被访问时,系统会将文件内容移到脱机存储器(如磁带中),从而腾出硬盘空间,当文件被打开时,会从磁带恢复到磁盘。该标志禁用这种行为。 |
FILE_FLAG_OVERLAPPED |
以异步方式来访问设备(默认时的同步方式打开设备的) |
③文件属性的标志——创建新文件时(不适应用其他类型的设备)
参数 |
描述 |
FILE_ATTRIBUTE_ARCHIVE |
存档文件,用来将文件标志为“待备份”或“待删除”,当CreateFile创建新文件时,会自动设置这个标志。 |
FILE_ATTRIBUTE_ENCRYPTED |
文件是经过加密的 |
FILE_ATTRIBUTE_HIDDEN |
隐藏文件 |
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
内容索引服务不会对文件进行索引 |
FILE_ATTRIBUTE_OFFLINE |
文件虽然存在,但文件内容己被转移到脱机存储器中。 |
FILE_ATTRIBUTE_READONLY |
只读文件 |
FILE_ATTRIBUTE_SYSTEM |
文件是操作系统的一部分,或专供操作系统使用 |
FILE_ATTRIBUTE_TEMPORARY |
临时文件 ①CreateFile会将文件的数据尽量保存的内存中,而不是磁盘上。直至无法继续保存在内存中,才被迫写入硬盘。 ②如果没有指定FILE_FLAG_DELETE_ON_CLOSE将在关闭文件时,会将文件在缓存中的数据写入磁盘。否则会被删除掉。 |
10.2 使用文件设备
10.2.1 获取文件的大小
(1)GetFileSizeEx(hFile,pliFileSize)——返回文件的逻辑大小
①hFile文件句柄
②pliFileSize保存文件大小的LARGE_INTEGER结构体指针(文件大小64位表示),返回的是文件的逻辑大小。如100KB文件压缩后只占85KB。则返回100KB。
(2)GetCompressedFileSize(pszFileName,pdwFileSizeHigh)
①pszFileName:文件名(字符串形式)
②返回压缩后的大小,是文件的物理大小,即实际占用的磁盘空间。如以上例子得到85KB。其中文件大小的低32位是函数的返回值,高32位保存在pdwFileSizeHigh中
ULARGE_INTEGER ulFileSize; //64位 ulFileSize.lowPart=GetCompressedFileSize(TEXT("SomFile.dat"),&ulFileSize.HighPart);
10.2.2 设置文件指针的位置
(1)文件内核对象内部有一个文件指针,是一个64位偏移量,用来表示下一次同步(注意:异步操作时必须自己维护的一个OVERRLAPPED结构体,里面包含该信息)读取或写入操作时的位置。
(2)SetFilePointerEx函数——用来设置文件指针
参数 |
描述 |
HANDLE hFile |
文件句柄 |
LARGE_INTEGER liDistanceToMove |
指针移动的字节,系统会把这个值与文件指针的当前值相加,使用负数时文件指针向后移动。 |
LARGE_INTEGER pliNewFilePointer |
接收新指针的位置 |
DWORD dwMoveMethod |
用来指定移动文件指针时的起始位置 FILE_BEGIN:文件指针被为liDistanceToMove的值。 注意这里的liDistanceToMove被解释为无符号值 FILE_CURRENT:文件对象的文件指针+liDistanceToMove 注意这里的liDistanceToMove被解释为有符号值 FILE_END:文件的逻辑大小+liDistanceToMove。 注意这里的liDistanceToMove被解释为有符号值 |
【注意事项】
①当文件指针设为超过文件当前的大小是正当操作。除非在该位置写入数据或调用SetEndOfFile,否则这样做不会增加文件在磁盘上的实际大小
②SetFilePointerEx操作的文件用FILE_FLAG_NO_BUFFERING标志打开时,那么文件指针只能被设置为扇区大小的整数倍。
③Windows没有提供一个GetFilePointerEx函数。但可以向SetFilePointerEx函数的liDistanceToMove参数传为0来达到同样的效果。
10.2.3 设置文件尾SetEndOfFile——可强制使文件变大或变小
(1)根据文件指针当前所在位置来截断文件大小或增大文件大小。
(2)示例代码——强制文件大小设为1024字节
HANDLE hFile = CreateFile(…); LARGE_INTEGER liDistanceToMove = 1024; SetFilePointerEx(hFile,liDistanceToMove,NULL,FILE_BEGIN); SetEndOfFile(hFile); CloseHandle(hFile);