本文主要是介绍PoEdu-Windows班-004 文件操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
C语言方式:
- 打开文件:fopen
- 读取文件:fread、fgets、fscanf
- 写入文件:fwrite、fputs、fprintf
- 关闭文件:fclose
- 定位文件位置:fseek
- 求文件的大小:ftell
打开方式:
- “r” 以只读方式打开文件,该文件必须存在。
- “r+” 以可读写方式打开文件,该文件必须存在。
- ”rb+“ 读写打开一个二进制文件,允许读写数据(可以任意修改),文件必须存在。
- “w” 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
- “w+” 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
- “a” 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
- ”a+“ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。(原来的EOF符不保留)
- “wb” 只写打开或新建一个二进制文件;只允许写数据(若文件存在则文件长度清为零,即该文件内容会消失)。
- “wb+” 读写打开或建立一个二进制文件,允许读和写(若文件存在则文件长度清为零,即该文件内容会消失)
- “wx” 创建文本文件,只允许写入数据.[C11]
- “wbx” 创建一个二进制文件,只允许写入数据.[C11]
- ”w+x“ 创建一个文本文件,允许读写.[C11]
- “wb+x” 创建一个二进制文件,允许读写.[C11]
- “w+bx” 和"wb+x"相同[C11]
- “rt” 只读打开一个文本文件,只允许读数据
- “wt” 只写打开或建立一个文本文件,只允许写数据
- “at” 追加打开一个文本文件,并在文件末尾写数据
- “rb” 只读打开一个二进制文件,只允许读数据
- “wb” 只写打开或建立一个二进制文件,只允许写数据
- “ab” 追加打开一个二进制文件,并在文件末尾写数据
- “rt+” 读写打开一个文本文件,允许读和写
- “wt+” 读写打开或建立一个文本文件,允许读写
- “at+” 读写打开一个文本文件,允许读,或在文件末追加数据
- “rb+” 读写打开一个二进制文件,允许读和写
- “ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据
C++方式:
使用CFile类来操作文件。
这个类对于文件的操作进行了封装,大致上和C语言的没什么区别。
文件打开的方式:
- CFile::modeCreate直接构造去创建一个新的文件如果这个文件存在,则删除这个文件里所有内容
- CFile::modeNoTruncate 联合创建属性,如果这个文件已创建,则不删除原文件内容,因而这个文件是可以当做一个已经存在,或者不存在而被新建的文件来保证打开。这是非常有意义的,例如,打开来设置一个存在或者不存在的文件都是可以的。这个属性对于CStdioFile也是非常好的
- CFile::modeRead 打开为只读属性.
- CFile::modeWrite 打开为只写属性.
- CFile::modeReadWrite 打开为读写属性.
- CFile::modeNoInherit 防止这个文件来源于子进程.
- CFile::shareDenyNone 在这个文件读写处理之外打开这个文件
- CFile::shareDenyRead 以独占方式打开,拒绝其他读操作
- CFile::shareDenyWrite 以独占方式打开,拒绝其他写操作
- CFile::shareExclusive 以独占方式打开文件,拒绝其他读写操作访问这个文件Opens the filewith exclusive mode, denying other如果这个文件已打开进行读写操作则构造失败
- CFile::shareCompat 这个属性在 32 bit MFC是不可用的. This flag maps to CFile::shareExclusive 当应用在CFile::Open.
- CFile::typeText 设置文本模式特别处理回车换行 (used inderived classes only).
- CFile::typeBinary 设置二进制模式(used in derivedclasses only).
获取文件大小API:
Windows方式:
打开文件:CreateFile
读取文件:ReadFile
写入文件:Write
关闭文件:CloseHandle
CreateFile函数详解:
函数功能
CreateFile 函数用于创建或打开一个文件或者 I/O设备。最常使用的 I/O设备如下:
- 文件
- 文件流
- 文件夹
- 物理磁盘
- 逻辑磁盘驱动器
- 控制台程序缓冲区
- 磁带
- 通信资源
- 邮槽
- 管道
此函数返回一个指向用于访问来自不同类型I/O的文件或设备的句柄,其访问权限取决于它所访问的文件、设备和我们所指定的标记位、属性。
为了使之可以处理事务型 I/O,请使用 CreateFileTransacted函数。
参数解析
lpFileName
- 指定要打开、创建的文件或设备的名字。你可以在名字中使用斜杠(/)或者反斜杠(\)
- 在此函数的 ANSI 版本中,名字长度被限制为 MAX_PATH 个字符。为了将此限制扩展到 32767 个宽字符,需要调用此函数的 Unicode 版本,并且在添加在路径名中添加 “\?\” 前缀。获取更多的信息,参见 Naming Files, Paths, and Namespaces
- 想获取关于特殊设备的名字,参见 Defining an MS-DOS Device Name
- 为了创建一个文件流,需要指定文件名,一个冒号加上流文件的名字。获取更多信息,参见 File Streams
- 指定以何种权限打开文件或设备,可以归纳为:读访问权、写访问权、读写访问权、非读非写访问权
- 最常使用的值为 GENERIC_READ, GENERIC_WRITE, 或者二者都用(GENERIC_READ | GENERIC_WRITE)。获取更多信息,参见 Generic Access Rights, File Security and Access Rights, File Access Rights Constants 和 ACCESS_MASK
- 如果参数值为 0,那么程序可以在不访问文件或设备情况下,询问某些元数据,如文件、目录或者设备属性。此外,即使 GENERIC_READ 请求,也会被拒绝
- 你无法请求一个与共享模式冲突的访问权限。共享模式是在参数 dwShareMode 中设置的
- 获取更多信息,参见本文备注以及 Creating and Opening Files
- 设置文件或者设备的共享模式,包括读、写、读写、删除、全部权限或者以上什么权限都没有(参考下面的表格)。此参数不影响对属性和扩展属性的访问请求
- 如果此参数为 0 且 CreateFile 函数执行成功,那么此文件或设备无法被共享,且在其句柄被关闭前,无法被再次打开。更多信息,参见本文备注。
- 你无法设置一个与访问模式相冲突的共享模式。此时如果 CreateFile 函数执行失败,那么 GetLastError 函数会返回 ERROR_SHARING_VIOLATION
- 为了允许进程去共享一个已经在另一个进程中打开的文件或句柄,那么可对以下一个或多个取值进行组合,且各取值间需要可互相兼容。更多有关此参数与 dwDesiredAccess 参数的合法取值组合的信息,参见 Creating and Opening Files
- 注意:在句柄被关闭之前,每个句柄的共享选项都会一直生效,且与进程的运行上下文(process context)无关。
值 | 含义 |
0 | 防止其他进程以删除、读取或写入权限打开一个文件或者设备 |
FILE_SHARE_DELETE(0x00000004) | 1.允许随后的对文件或设备的打开操作获取删除权限 |
FILE_SHARE_READ(0x00000001) | 1.允许后续对文件或设备的读权限打开请求 |
FILE_SHARE_WRITE(0x00000002) | 1.允许后续对文件或设备的写权限打开请求 |
- 指向 SECURITY_ATTRIBUTES 结构体的指针。此结构体拥有两个分开但是相关的数据成员:一个是可选的安全描述符,另一个是决定返回的句柄是否可以被子进程继承的 Boolean 值
- 此参数可以设置为 NULL
- 如果参数为 NULL,那么 CreateFile 函数返回的句柄无法被任意此进程的子进程所继承,且与返回的句柄所对应的文件或句柄拥有一个默认的安全描述符
- 该结构体的 lpSecurityDescriptor 成员为文件或设备指定一个安全描述符(SECURITY_DESCRIPTOR)。如果此成员取值为 NULL,那么与返回的句柄所对应的文件或句柄拥有一个默认的安全描述符
- 当打开一个已经存在的文件或设备时,CreateFile 函数忽略 lpSecurityDescriptor 成员,但是 bInheritHandle 成员仍然可以使用
- 结构体的 bInheritHandle 成员用于设置返回的句柄是否可以被继承
- 更多信息,参见本文备注
- 用于设置当文件存在或不存在时,要对文件或设备执行何种操作
- 对于设备来说,此参数通常设置为 OPEN_EXISTING
- 更多信息,参见本文备注
- 这个参数的值必须为以下值之一,且只能选择一个而不能组合多个:
值 | 含义 |
CREATE_ALWAYS | 1.总是创建一个新文件 |
CREATE_NEW | 1.仅当文件不存在时,才创建一个新文件 |
OPEN_ALWAYS | 1.总是打开一个文件 |
OPEN_EXISTING | 1.仅当文件或设备存在时,才打开它 |
TRUNCATE_EXISTING | 1.仅当文件存在时,才打开一个文件并且将其大小截取到 0 字节 |
- 文件或设备的属性值和标记位,对于文件来说,FILE_ATTRIBUTE_NORMAL 是最常用的默认值
- 此参数可以是任意文件属性值(FILE_ATTRIBUTE_*)的组合。所有其他的文件属性值会覆盖 FILE_ATTRIBUTE_NORMAL
- 此参数也可以包含任意标记位(FILE_FLAG_*)的组合以控制文件或设备的行为、权限设置和其他目的。此外,还可以与任意 FILE_ATTRIBUTE_* 进行组合
- 这个参数还可以通过指定 SECURITY_SQOS_PRESENT 标记来包含 Security Quality of Service (SQOS) 信息。与SQOS相关的标记位信息见属性与标记位表格下面的表格中
- 注意:当CreateFile 打开一个已存在的文件时,它通常将文件属性和文件标记位组合在一起,并且忽略在 dwFlagsAndAttributes 中定义的属性值。详细例子见 Creating and Opening Files
- 以下的属性和标记位可能只适用于文件的打开,而并非支持所有其他 CreateFile 函数可以打开的设备。想了解更多信息,参见本文备注部分和 Creating and Opening Files。想进一步了解文件属性相关信息,参见 SetFileAttributes 函数。你还可以在 File Attribute Constants 中看到完整的介绍所有文件属性的值和对应描述信息
属性 | 含义 |
FILE_ATTRIBUTE_ARCHIVE | 文件应该被存档。应用程序使用此属性来备份或移除标记文件 |
FILE_ATTRIBUTE_ENCRYPTED | 1.文件或目录被加密。对于文件来说,这意味着文件中所有数据都被加密了;对于目录来说,这意味着加密新创建的文件和子文件夹是默认选项。更多信息,参见 File Encryption |
FILE_ATTRIBUTE_HIDDEN | 文件被隐藏。即在普通文件夹中不被显示 |
FILE_ATTRIBUTE_NORMAL | 这个文件不包含其他属性。只有当它单独使用时,这个属性值才是合法的 |
FILE_ATTRIBUTE_OFFLINE | 文件的数据无法直接访问。这个属性表示文件数据在物理设备中被移动到一个离线存储器。这个属性被远程存储(Remote Storage)—— 即分级存储管理软件 —— 所使用。我们的程序不能随意改变这个属性值 |
FILE_ATTRIBUTE_READONLY | 文件只读。程序可以读取文件,但无法写入或删除文件 |
FILE_ATTRIBUTE_SYSTEM | 文件是操作系统的的一部分或者专门为操作系统所使用 |
FILE_ATTRIBUTE_TEMPORARY | 1.文件用于存储临时数据 |
FILE_FLAG_BACKUP_SEMANTICS | 1.此文件已经被打开或者创建以用于备份或者恢复操作。当调用进程拥有 SE_BACKUP_NAME 和 SE_RESTORE_NAME 权限时,系统保证调用进程覆盖文件安全检测。更多信息,参见 Changing Privileges in a Token |
FILE_FLAG_DELETE_ON_CLOSE | 1.在所有与此文件相关的句柄被关闭后,文件马上会被删除。与此文件相关的句柄包括当前句柄和任意其他打开或者被复制的句柄 |
FILE_FLAG_NO_BUFFERING | 1.文件或设备被以读写无缓冲区的方式打开。此标记位对于硬盘或者内存映射文件无效。 |
FILE_FLAG_OPEN_NO_RECALL | 文件数据被请求,但它不应该继续在远程存储器中被定位。文件数据不应该传输回本地存储器。这个标记位被远程存储系统所使用 |
FILE_FLAG_OPEN_REPARSE_POINT | 1.不会发生正常的 reparse point 过程。 CreateFile 函数将会尝试去打开一个 reparse point。当一个文件被打开,无论控制 reparse point 的过滤器是否可操作,都会返回一个文件句柄 |
FILE_FLAG_OVERLAPPED | 1.此文件或设备被打开或创建,用于异步 I/O |
FILE_FLAG_POSIX_SEMANTICS | 遵循 POSIX 规则,这包括允许多个文件名,只是在不同的情况下,为支持该命名规则的文件系统。当使用此选项时要小心,因为一个使用此标记位创建的文件可能无法被某些为 MS-DOS 或 16 位 Windows 操作系统所编写的程序所访问 |
FILE_FLAG_RANDOM_ACCESS | 1.文件访问为随机访问。此系统可以使用这个用于优化文件缓存 |
FILE_FLAG_SESSION_AWARE | 1.文件或设备带 session awareness 打开。如果此标记位没有被指定,则 per-session 设备(一个使用 RemoteFX USB 重定向的设备)无法被运行在 session 0 的进程所打开。如果调用进程没有运行在 session 0,则此标记位无效。此标记位只支持在 server 版本的 Windows |
FILE_FLAG_SEQUENTIAL_SCAN | 1.表示文件访问将从头顺序读到尾。系统利用此标记位用于优化文件缓存 |
FILE_FLAG_WRITE_THROUGH | 1.写操作不会通过任何中间缓冲区缓冲,而是直接写回磁盘 |
- 指向一个拥有 GENERIC_READ 访问权限的的模板文件的合法句柄
- 此模板文件为即将创建的文件提供属性和扩展属性
- 参数值可以为 NULL
- 如果打开一个已存在的文件,则 CreateFile 函数忽略这个参数
- 如果打开一个新的被加密文件,此函数从其父目录中继承自由存取控制列表。更多信息,见 File Encryption
- 返回值
- 如果函数调用成功,返回指向指定文件的句柄、设备、命名管道或者邮槽;
- 如果函数调用失败,返回值为 INVALID_HANDLE_VALUE。
- 想获取具体错误信息,调用 GetLastError 函数。
- CreateFile 函数最初被官方开发用于文件操作,但后来功能被扩展加强,Windows 开发者可用它来处理绝大部分其他类型的 I/O 设备和机器。当前备注试图讲解开发者在不同的环境下使用不同的 I/O 设备过程中,调用 CreateFile 函数可能遇到的不同的问题。
- 这篇文章中,仅当数据是存储在一个文件系统上的真正的文件,才会使用”文件”这个词。然而,在提及“文件”的情况下,有一些只得是更广义上的 I/O 对象,这些 I/O 对象支持类似文件操作的机制。由于前面所提及到的历史原因,“文件”这个名词使用过于自由,已经广泛被各种常量名称和参数名称所使用。
- 当 CreateFile 函数返回的句柄不再使用时,调用 CloseHandle 函数关闭这个句柄。这不仅只是释放系统资源,对于如文件或设备的共享和磁盘的数据提交等仍有很大的影响。具体细节在本文描述有介绍。
- Windows Server 2003 and Windows XP:当我们试图打开一个远程文件或目录以用于删除,此时如果指定了 dwDesiredAccess 参数包含了删除标记(0x00010000),且远程文件或目录并未以 FILE_SHARE_DELETE 属性被打开,那么就会引发共享错误。在这个例子中,为了避免共享错误发生,仅且仅以删除权限打开远程文件或目录,或者不用打开文件而是直接调用 DeleteFile 函数去删除文件。
- 一些文件系统,如 NTFS 文件系统,支持某些文件和文件夹的压缩和加密。如果一个分区装载了支持这些功能的文件系统,则一个新文件会从它所属文件夹中继承这些压缩和加密的属性。
- 你无法使用 CreateFile 函数去控制文件的压缩、解压或加密。更多信息,参见 Creating and Opening Files, File Compression and Decompression 和 File Encryption。
- Windows Server 2003 and Windows XP:为了实现向后兼容,当你在 lpSecurityAttributes 参数中指定一个安全描述符时,CreateFile 函数并不会应用继承规则。为了支持继承,后续询问文件安全描述符的函数可能会启发式地去决定并告知继承已经生效了。更多信息,参见 Automatic Propagation of Inheritable ACEs。
- 正如前面所阐述的,如果 lpSecurityAttributes 参数为 NULL,则 CreateFile 函数返回的句柄无法被你的程序可能创建的任何子进程所继承。以下是关于此参数被应用的信息:
- 如果 bInheritHandle 成员的取值不是 FALSE,而是其他任意非 0 值,那么这个句柄可以被继承。因此如果你不希望此句柄可以被继承,注意要把此参数初始化为 FALSE
- 文件或目录默认安全描述符中的访问控制列表(ACL)继承自其父目录
- 若想使 lpSecurityDescriptor 成员对文件和目录有效,文件系统必须支持文件和目录安全,这由使用 GetVolumeInformation 函数决定
- 在 Windows 8 和 Windows Server 2012 中,此函数由以下技术支持:
技术 | 是否支持 |
Server Message Block (SMB) 3.0 protocol | 支持 |
SMB 3.0 Transparent Failover (TFO) | 参见备注 |
SMB 3.0 with Scale-out File Shares (SO) | 参见备注 |
Cluster Shared Volume File System (CsvFS) | 支持 |
Resilient File System (ReFS) | 支持 |
- 符号链接相关
- 如果此函数的调用产生了一个新文件,FILE_FLAG_OPEN_REPARSE_POINT 标记位被忽视,否则,考虑以下说明:
- 如果指定了 FILE_FLAG_OPEN_REPARSE_POINT 标记,则:
- 如果一个已存在的文件被打开且它是一个符号链接,则返回的句柄为一个执行符号链接的句柄
- 如果指定了 TRUNCATE_EXISTING 或 FILE_FLAG_DELETE_ON_CLOSE 标记,则被影响的文件是一个符号链接
- 如果未指定了 FILE_FLAG_OPEN_REPARSE_POINT 标记,则:
- 如果一个已存在的文件被打开且它是一个符号链接,则返回的句柄为指向这个目标的句柄
- 如果指定了 CREATE_ALWAYS, TRUNCATE_EXISTING 或 FILE_FLAG_DELETE_ON_CLOSE 标记,被影响的文件为目标文件
- 文件缓存
- CreateFile 函数的 dwFlagsAndAttributes 参数的一些取值,用于告知系统如何指定其返回的文件句柄的缓存模式。这些取值是:
- FILE_FLAG_NO_BUFFERING
- FILE_FLAG_RANDOM_ACCESS
- FILE_FLAG_SEQUENTIAL_SCAN
- FILE_FLAG_WRITE_THROUGH
- FILE_ATTRIBUTE_TEMPORARY
- 如果以上标记位都没有被指定,则系统使用一个默认的适用于大多数情况的缓存模式。否则,系统就会按照我们指定的标记位的模式,进行数据缓存。
- 某些标记位无法被组合。如组合 FILE_FLAG_RANDOM_ACCESS 标记位和 FILE_FLAG_SEQUENTIAL_SCAN 标记位将发生自我矛盾。
- 当顺序读取大文件,指定 FILE_FLAG_SEQUENTIAL_SCAN 标记位可以提高性能;当程序读取大文件时,大部分都是顺序读取,偶尔跳过小范围的字节数,则性能提高更明显。如果程序操作文件指针以用于随机访问,则大部分情况下是无法达到最优缓存效果的。然而,仍然可以保证操作的正确执行。
- FILE_FLAG_WRITE_THROUGH 标记位和 FILE_FLAG_NO_BUFFERING 标记位是独立的,可以进行组合。
- 如果指定了 FILE_FLAG_WRITE_THROUGH 标记位,但是没有指定 FILE_FLAG_NO_BUFFERING 标记位,此时系统缓存机制仍在运行:数据被写到系统缓存区,但是马上被写回磁盘。
- 如果 FILE_FLAG_WRITE_THROUGH 标记位和 FILE_FLAG_NO_BUFFERING 标记位都被指定了,则系统缓冲区不会起作用,数据不经过 Windows 系统缓存区而是直接写入磁盘。系统会申请直接写入到本地磁盘的缓存中。
- 如果 FILE_FLAG_WRITE_THROUGH 标记位和 FILE_FLAG_NO_BUFFERING 标记位都被指定了,则系统缓存机制停止工作,数据不经过Windows系统缓存去,而是被直接写到磁盘上。系统会申请直接写入到本地磁盘的缓存中。
- 注意:并非所有磁盘硬件都支持直接写入的方法。
- 要想正确使用 FILE_FLAG_NO_BUFFERING 标记位,需要对程序一些更深的理解。更多信息,参见 File Buffering。
- 处理一个直接写入请求请求(通过 FILE_FLAG_WRITE_THROUGH 标志位来实现)时,会使得 NTFS 刷新任何被修改的元数据,如文件修改时间和重命名操作。为此,FILE_FLAG_WRITE_THROUGH 标记位常和 FILE_FLAG_NO_BUFFERING 标记位共同使用,以替代每次进行写操作后都调用 FlushFileBuffers 函数将数据写入文件,而后者会引起不必要的开销。使用这些标记位可以避免此类不必要的开销。更多关于文件和元数据缓存的信息,参见 File Caching。
- 当 FILE_FLAG_NO_BUFFERING 标记位和 FILE_FLAG_OVERLAPPED 标记位共同使用时,可以提供最大化异步 I/O 的效率,因为 I/O 操作并不依赖于内存管理器的同步操作。然而,一些 I/O 操作消耗更多时间,因为没有使用缓存区。此外,文件元数据可能仍然使用缓存(例如,当创建一个空文件)。为了保证元数据被写入磁盘,要使用 FlushFileBuffers 函数。
- 当指定了 FILE_ATTRIBUTE_TEMPORARY 属性值时,如果有可用的高效的缓冲内存,那么文件系统会避免将数据写回大容量存储器,因为程序在文件句柄被关闭后会删除临时文件。在这个情况下,系统可以完全避免将数据写回磁盘。虽然它不像前面提及到的标记位那样直接控制数据缓存行为,但是 FILE_ATTRIBUTE_TEMPORARY 属性值会告诉系统尽可能地不要将数据写到磁盘中,因此这可能会对某些程序很有意义。
- 文件
- 如果你重命名或删除一个文件,然后又马上恢复原样,则系统会搜索缓存区中的文件信息去执行恢复操作。被缓存的信息包括短、长文件名和文件创建时间。
- 操作系统会延迟删除操作、直到所有指向此文件的句柄被关闭。如果某个文件正在等待被执行 DeleteFile 函数,此时对此文件调用 CreateFile 函数,则 CreateFile 函数执行失败。GetLastError 函数会返回 ERROR_ACCESS_DENIED。
- dwDesiredAccess 参数可以为 0,此时如果程序的权限足够,则系统允许程序不访问文件而直接获取文件的属性值。 这在以下情况是很有用的:当我们不以读访问或写访问打开一个文件,却想测试一个文件是否存在或者获得其他关于此文件或目录的数据。参见 Obtaining and Setting File Information 和 GetFileInformationByHandle 函数。
- 如果指定了 CREATE_ALWAYS 和 FILE_ATTRIBUTE_NORMAL,则当文件存在且拥有 FILE_ATTRIBUTE_HIDDEN 属性值或 FILE_ATTRIBUTE_SYSTEM 属性值时,CreateFile 函数执行失败,且 last error 被设置为 ERROR_ACCESS_DENIED。为了避免这个错误,要为已存在文件设置与之相同的属性值。
- 当程序通过网络创建一个文件,dwDesiredAccess 参数设置为 GENERIC_READ | GENERIC_WRITE 会比单独设置 GENERIC_WRITE 要好。这样会使得代码执行的更快,因为重定向器可以使用缓存管理器,在发送更多数据的同时,附带更少的 SMB。二者组合用还可以防止通过网络写文件时,偶尔出现ERROR_ACCESS_DENIED错误。
- 更多信息,参见 Creating and Opening Files。
- 同步和异步 I/O 句柄
- CreateFile 函数可以创建的文件或设备句柄可以是同步也可以是异步的。一个同步句柄会导致当执行 I/O 调用函数时,调用者会被阻塞知道函数执行结束;而一个异步文件句柄在调用 I/O 函数时,无论 I/O 操作是否执行完成,调用者都会立刻返回。
- 正如前面所阐述的,使用同步 I/O 还是异步 I/O 由在 dwFlagsAndAttributes 参数中是否指定了 FILE_FLAG_OVERLAPPED 标记位决定。此外异步 I/O 会有一些复杂性和潜在的陷阱,更多信息,参见 Synchronous and Asynchronous I/O。
- 文件流
- 在 NTFS 文件系统中,你可以使用 CreateFile 函数在一个文件中创建分离的流。想了解更多信息,参见 File Streams。
- 目录
- 程序无法使用 CreateFile 函数创建一个目录,因此对目录操作时,在 dwCreationDisposition 参数里指定 OPEN_EXISTING 标记位才是合法的。为了创建一个目录,程序必须调用 CreateDirectory 函数或 CreateDirectoryEx 函数。
- 若要用 CreateFile 函数打开一个目录,需在 dwFlagsAndAttributes 参数里指定 FILE_FLAG_BACKUP_SEMANTICS 标记位。当程序没有 SE_BACKUP_NAME 和 SE_RESTORE_NAME 权限时,会触发适当的安全检查。
- 在 FAT 或 FAT32 文件系统中,如果我们在执行磁盘碎片整理过程中,使用 CreateFile 函数打开一个目录,那么不要指定 MAXIMUM_ALLOWED 访问权限;如果这样做,则对目录的访问将被拒绝;应使用 GENERIC_READ 访问权限替代之。
- 更多信息,参见 About Directory Management。
- 物理磁盘和分区
- 对磁盘或分区的直接访问是受限制的。更多信息,参见“在 Windows Vista 和 Windows Server 2008 中对文件系统和存储堆栈进行的更改可限制对磁盘和卷的直接访问”,网址为 http://support.microsoft.com/kb/942448。
- Windows Server 2003 和 Windows XP: 对磁盘或分区的直接访问是不受限制的。
- 你可以使用 CreateFile 函数打开一个物理磁盘或一个磁盘分卷,它会返回一个直接访问存储设备(DASD)句柄,这个句柄可以被 DeviceIoControl 函数所使用。这允许你直接访问磁盘或分区,例如磁盘的元数据分区表。然而,这会使得磁盘或分区的数据存在数据丢失的可能,因为使用这种方法对磁盘一次不正确的写操作可能使得操作系统无法识别修改后的数据。为了保证数据的完整性,要确保对 DeviceIoControl 函数足够熟悉,且了解其他API在使用文件系统句柄和直接访问句柄时,有什么区别。
- 若想对磁盘和分区直接访问成功执行,以下条件必须被满足:
- 调用者必须拥有 administrative 权限。更多信息,参见 Running with Special Privileges
- dwCreationDisposition 参数必须指定 OPEN_EXISTING 标记位。
- 当打开一个分区或者软盘时,dwShareMode 参数必须指定 FILE_SHARE_WRITE 标记位
- 注意:dwDesiredAccess 参数可以为 0,这允许程序不访问设备、就可以询问设备属性。例如:程序可以获得软盘驱动器的大小和其支持的格式却不用直接访问软盘。这还可以用于读取一些不需要获取高级别的数据读、写许可的就能读取的数据。
- 当打开一个物理驱动器 x:,则 lpFileName 字符串格式如下:”\.\PhysicalDriverX”。硬盘下标从 0 开始。下表显示了一个有关物理驱动器字符串格式的例子:
- 字符串 含义
- “\.\PhysicalDrive0” 打开第一个磁盘驱动器
- “\.\PhysicalDrive2” 打开第三个磁盘驱动器
- 为了得到一个物理磁盘驱动器的 ID 值,需要先获得执行其的句柄,然后带入 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 调用 DeviceIoControl 函数。
- 关于如何打开一个物理驱动器,参见 Calling DeviceIoControl 例子。
- 当打开一个分区或者可移动媒体驱动器(如一个磁盘驱动器或者闪盘驱动器),那么字符串 lpFileName 应该为以下格式 “\.\X:”。不要使用反斜线(\),这个符号表示驱动器的根目录。下表是一些驱动器目录字符串的例子:
- 字符串 含义
- “\.\A:” 打开软盘 A
- “\.\C:” 打开磁盘驱动器 C
- “\.\C:\” 打开 C 盘的文件系统
- 你也可以通过磁盘分区名字来打开一个磁盘分区。更多信息,参见 Naming a Volume。
- 一个分区可以有多个文件系统。一些特定的文件系统中,分区句柄被以禁止缓存方式打开,即使 CreateFile 函数并未指定禁止缓存选项。你可以假定所有微软文件系统的分区句柄都是禁止缓存的,且对无缓存文件的某些限制同样适用于分区句柄。
- 一个文件系统可能会也可能不会要求缓存对齐,即使数据是禁止缓存的。然而,如果在打开一个分区时指定了禁止缓存选项,无论加载在分区上的是何种文件系统,缓冲对齐都是强制要求的。当你打开一个分区时,推荐打开禁止缓存选项,且遵循禁止缓存 I/O 限制。
- 注意:读写分区的最后几个扇区时,你必须调用 DeviceIoControl 函数并且指定 FSCTL_ALLOW_EXTENDED_DASD_IO 标记位。这会告知文件系统,调用分区的读写函数时,不要进行任何的 I/O 边界检查,而是由设备驱动器来执行边界检查。
- Changer Device
DeviceIoControl 函数设置控制代码为 IOCTL_CHANGER_* 时,会接受一个指向 changer device 的句柄。要打开一个 changer device,使用如下格式的文件名:”\.\Changerx”,其中 x 表示要打开设备的下标,下标从 0 开始。如要在程序中使用 C 或者 C++ 打开 0 号 changer device,使用如下格式的文件名:”\\.\Changer0”。
- 磁带驱动器
你可以通过使用如下形式的名称来打开一个磁带驱动器:”\.\TAPEx”,x 表示要打开的驱动器的下标,下标从 0 开始。 如要使用 C 或 C++ 打开 0 号磁带驱动器,使用如下格式:”\\.\TAPE0”.
- 通信资源
- CreateFile 函数可以创建指向通信资源的句柄,如串行端口 COM1。 要打开一个通信资源,dwCreationDisposition 参数必须设置为 OPEN_EXISTING,dwShareMode 参数必须设置为 0(即只能由自己访问),且 hTemplateFile 参数必须为 NULL。读、写或者读/写权限可以指定任意一个,且此句柄可用于异步 I/O。
- 如果要指定一个端口号大于 9 的 COM 端口,使用以下的语法:”\.\COM10”。此语法适用于所有端口号和所有允许 COM 端口号被指定的硬件。
- 控制台
- 你可以用 CreateFile 函数创建一个指向控制台输入(COCNIN$)的句柄。如果进程通过继承或复制获得这个句柄,它还可以创建一个指向活动屏幕缓存的句柄。调用进程必须被附加到一个被继承的控制台或者一个通过 AllocConsole 函数分配到的控制台。有关控制台句柄的参数设置如下:
参数 | 值 |
lpFileName | 1.使用 CONIN值指向控制台输入 |
dwDesiredAccess | 更推荐使用 GENERIC_READ GENERIC_WRITE,但任选其一也可以用于设置权限 |
dwShareMode | 1.当打开 CONIN 时,要指定 FILE_SHARE_READ;当打开 CONOUT 时,要指定 FILE_SHARE_WRITE |
lpSecurityAttributes | 如果你想此控制台被继承,那么 SECURITY_ATTRIBUTES 结构体的 bInheritHandle 成员必须为 TRUE |
dwCreationDisposition | 当使用 CreateFile 函数打开一个控制台时,你必须指定 OPEN_EXISTING |
dwFlagsAndAttributes | 忽略 |
hTemplateFile | 忽略 |
- 下表是 dwDesiredAccess和 lpFileName参数取不同值时的结果:
lpFileName | dwDesiredAccess | 结果 |
“CON” | GENERIC_READ | 打开一个控制台程序用于读操作 |
“CON” | GENERIC_WRITE | 打开一个控制台程序用于写操作 |
“CON” | GENERIC_READ GENERIC_WRITE | 此时 CreateFile 函数执行失败;GetLastError 函数返回 ERROR_FILE_NOT_FOUND |
- 邮槽
如果 CreateFile函数打开邮槽,那么当客户端试图打开一个本地邮槽,而服务端并没有使用 CreateMailSlot函数创建这个邮槽时,则返回 INVALID_HANDLE_VALUE。
- 管道
- 如果 CreateFile函数打开一个客户端的命名管道,则此函数使用任意处于监听状态的命名管道句柄实例。进程可以无限复制命名管道句柄,但是当它被打开后,命名管道无法被另一个客户端再次打开。被指定的命名管道访问权限必须与使用函数 CreateNamedPipe创建命名管道时,在 dwOpenMode参数中设置的权限相兼容。
- 如果 CreateNamedPipe函数没有在服务端被成功调用,则命名管道不会被创建且 CreateFile函数执行失败,且返回 ERROR_FILE_NOT_FOUND。
- 如果在服务器上有至少一个活动的管道实例,但是不存在监听者管道,即所有的管道实现在此时都被连接了,CreateFile函数执行失败且返回错误信息ERROR_PIPE_BUSY。
这篇关于PoEdu-Windows班-004 文件操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!