鱼C论坛

 找回密码
 立即注册
查看: 11416|回复: 1

[API档案] CreateFile

[复制链接]
发表于 2016-6-18 22:41:03 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
注:本文档由 vcvycy 翻译,小甲鱼校对。

原文链接 -> 传送门

函数功能:

CreateFile 函数用于创建或打开一个文件或者 I/O 设备。最常使用的 I/O 设备如下:文件、文件流、文件夹、物理磁盘、磁盘驱动器、控制台程序缓冲区、磁带、通信资源、邮槽和管道。此函数返回一个指向用于访问来自不同类型I/O的文件或设备的句柄,其访问权限取决于它所访问的文件、设备和我们所指定的标记位、属性。

为了使之可以处理事务型 I/O,请使用 CreateFileTransacted 函数。


API 函数原型:

注释:_In_ 说明该参数是输入的,_opt_ 说明该参数是可选的
HANDLE WINAPI CreateFile(
  _In_     LPCTSTR               lpFileName,
  _In_     DWORD                 dwDesiredAccess,
  _In_     DWORD                 dwShareMode,
  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  _In_     DWORD                 dwCreationDisposition,
  _In_     DWORD                 dwFlagsAndAttributes,
  _In_opt_ HANDLE                hTemplateFile
);

参数解析:

参数 含义
lpFileName1. 指定要打开、创建的文件或设备的名字。你可以在名字中使用斜杠(/)或者反斜杠(\)

2. 在此函数的 ANSI 版本中,名字长度被限制为 MAX_PATH 个字符。为了将此限制扩展到 32767 个宽字符,需要调用此函数的 Unicode 版本,并且在添加在路径名中添加 "\\?\" 前缀。获取更多的信息,参见 Naming Files, Paths, and Namespaces

3. 想获取关于特殊设备的名字,参见 Defining an MS-DOS Device Name

4. 为了创建一个文件流,需要指定文件名,一个冒号加上流文件的名字。获取更多信息,参见 File Streams
dwDesiredAccess1. 指定以何种权限打开文件或设备,可以归纳为:读访问权、写访问权、读写访问权、非读非写访问权

2. 最常使用的值为 GENERIC_READ, GENERIC_WRITE, 或者二者都用(GENERIC_READ | GENERIC_WRITE)。获取更多信息,参见 Generic Access Rights, File Security and Access Rights, File Access Rights ConstantsACCESS_MASK

3. 如果参数值为 0,那么程序可以在不访问文件或设备情况下,询问某些元数据,如文件、目录或者设备属性。此外,即使 GENERIC_READ 请求,也会被拒绝

4. 你无法请求一个与共享模式冲突的访问权限。共享模式是在参数 dwShareMode 中设置的

5. 获取更多信息,参见本文备注以及 Creating and Opening Files
dwShareMode1. 设置文件或者设备的共享模式,包括读、写、读写、删除、全部权限或者以上什么权限都没有(参考下面的表格)。此参数不影响对属性和扩展属性的访问请求

2. 如果此参数为 0 且 CreateFile 函数执行成功,那么此文件或设备无法被共享,且在其句柄被关闭前,无法被再次打开。更多信息,参见本文备注。

3. 你无法设置一个与访问模式相冲突的共享模式。此时如果 CreateFile 函数执行失败,那么 GetLastError 函数会返回 ERROR_SHARING_VIOLATION

4. 为了允许进程去共享一个已经在另一个进程中打开的文件或句柄,那么可对以下一个或多个取值进行组合,且各取值间需要可互相兼容。更多有关此参数与 dwDesiredAccess 参数的合法取值组合的信息,参见 Creating and Opening Files

5. 注意:在句柄被关闭之前,每个句柄的共享选项都会一直生效,且与进程的运行上下文(process context)无关。

含义
0
(0x00000000)
防止其他进程以删除、读取或写入权限打开一个文件或者设备
FILE_SHARE_DELETE
(0x00000004)
1. 允许随后的对文件或设备的打开操作获取删除权限

2. 否则,其他进程无法以删除权限打开一个文件或设备

3. 如果此标记位没有指定,但文件或设备已经打开并且获取了删除权限,则此函数执行失败

4. 注意:删除权限运行删除操作和重命名操作
FILE_SHARE_READ
(0x00000001)
1. 允许后续对文件或设备的读权限打开请求

2. 否则,其他进程无法以读权限打开此文件

3. 如果此处标记位未被指定,但是文件或设备被以读权限打开,则函数执行失败
FILE_SHARE_WRITE
(0x00000002)
1. 允许后续对文件或设备的写权限打开请求

2. 否则,其他进程无法以写权限打开此文件

3. 如果此标记位未被设置,但是文件或设备被以写权限被打开或者有一个拥有写权限的文件映射,则函数执行失败
lpSecurityAttributes1. 指向 SECURITY_ATTRIBUTES 结构体的指针。此结构体拥有两个分开但是相关的数据成员:一个是可选的安全描述符,另一个是决定返回的句柄是否可以被子进程继承的 Boolean 值

2. 此参数可以设置为 NULL

3. 如果参数为 NULL,那么 CreateFile 函数返回的句柄无法被任意此进程的子进程所继承,且与返回的句柄所对应的文件或句柄拥有一个默认的安全描述符

4. 该结构体的 lpSecurityDescriptor 成员为文件或设备指定一个安全描述符(SECURITY_DESCRIPTOR)。如果此成员取值为 NULL,那么与返回的句柄所对应的文件或句柄拥有一个默认的安全描述符

5. 当打开一个已经存在的文件或设备时,CreateFile 函数忽略 lpSecurityDescriptor 成员,但是 bInheritHandle 成员仍然可以使用

6. 结构体的 bInheritHandle 成员用于设置返回的句柄是否可以被继承

7. 更多信息,参见本文备注
dwCreationDisposition1. 用于设置当文件存在或不存在时,要对文件或设备执行何种操作

2. 对于设备来说,此参数通常设置为 OPEN_EXISTING

3. 更多信息,参见本文备注

4. 这个参数的值必须为以下值之一,且只能选择一个而不能组合多个:

含义
CREATE_ALWAYS(2)1. 总是创建一个新文件

2. 如果指定的文件已经存在而且是可写的,此时函数重写(overwrite)这个文件,函数为执行成功、且将 last-error code 设置为 ERROR_ALREADY_EXISTS(183)

3. 如果指定的文件不存在且其路径名合法,那么会创建一个新文件,函数为执行成功、且将 last-error code 设为 0

4. 更多信息,参见本文备注
CREATE_NEW(1)1. 仅当文件不存在时,才创建一个新文件

2. 如果指定文件已存在,函数执行失败且将 last-error code 设为 ERROR_FILE_EXISTS(80)

3. 如果指定文件不存在,当路径名合法而且可写时,则创建一个新文件
OPEN_ALWAYS(4)1. 总是打开一个文件

2. 如果指定的文件存在,函数执行成功,且 last-error code 设置为 ERROR_ALREADY_EXISTS(183)

3. 如果指定文件不存在,当路径名合法而且可写时,则创建一个新文件,last-error code 设置为 0
OPEN_EXISTING(3)1. 仅当文件或设备存在时,才打开它

2. 如果指定的文件或设备不存在,函数执行失败且 last-error code 设置为 ERROR_FILE_NOT_FOUND(2)

3. 更多信息,参见本文备注
TRUNCATE_EXISTING(5)1. 仅当文件存在时,才打开一个文件并且将其大小截取到 0 字节

2. 如果指定的文件不存在,函数执行失败且 last-error code 设置为 ERROR_FILE_NOT_FOUND(2)

3. 调用方需要设置 dwDesiredAccess 参数的 GENERIC_WRITE 位
dwFlagsAndAttributes1. 文件或设备的属性值和标记位,对于文件来说,FILE_ATTRIBUTE_NORMAL 是最常用的默认值

2. 此参数可以是任意文件属性值(FILE_ATTRIBUTE_*)的组合。所有其他的文件属性值会覆盖 FILE_ATTRIBUTE_NORMAL

3. 此参数也可以包含任意标记位(FILE_FLAG_*)的组合以控制文件或设备的行为、权限设置和其他目的。此外,还可以与任意 FILE_ATTRIBUTE_* 进行组合

4. 这个参数还可以通过指定 SECURITY_SQOS_PRESENT 标记来包含 Security Quality of Service (SQOS) 信息。与SQOS相关的标记位信息见属性与标记位表格下面的表格中

5. 注意:当CreateFile 打开一个已存在的文件时,它通常将文件属性和文件标记位组合在一起,并且忽略在 dwFlagsAndAttributes 中定义的属性值。详细例子见 Creating and Opening Files

6. 以下的属性和标记位可能只适用于文件的打开,而并非支持所有其他 CreateFile 函数可以打开的设备。想了解更多信息,参见本文备注部分和 Creating and Opening Files。想进一步了解文件属性相关信息,参见 SetFileAttributes 函数。你还可以在 File Attribute Constants 中看到完整的介绍所有文件属性的值和对应描述信息

属性 含义
FILE_ATTRIBUTE_ARCHIVE
32 (0x20)
文件应该被存档。应用程序使用此属性来备份或移除标记文件
FILE_ATTRIBUTE_ENCRYPTED
16384 (0x4000)
1. 文件或目录被加密。对于文件来说,这意味着文件中所有数据都被加密了;对于目录来说,这意味着加密新创建的文件和子文件夹是默认选项。更多信息,参见 File Encryption

2. 如果指定了FILE_ATTRIBUTE_SYSTEM标记位,则这个标记位不起作用

3. 这个标记位在以下 Windows 版本中不被支持:家庭版、家庭高级版、简易版或 ARM 版本
FILE_ATTRIBUTE_HIDDEN
2 (0x2)
文件被隐藏。即在普通文件夹中不被显示
FILE_ATTRIBUTE_NORMAL
128 (0x80)
这个文件不包含其他属性。只有当它单独使用时,这个属性值才是合法的
FILE_ATTRIBUTE_OFFLINE
4096 (0x1000)
文件的数据无法直接访问。这个属性表示文件数据在物理设备中被移动到一个离线存储器。这个属性被远程存储(Remote Storage)—— 即分级存储管理软件 —— 所使用。我们的程序不能随意改变这个属性值
FILE_ATTRIBUTE_READONLY
1 (0x1)
文件只读。程序可以读取文件,但无法写入或删除文件
FILE_ATTRIBUTE_SYSTEM
4 (0x4)
文件是操作系统的的一部分或者专门为操作系统所使用
FILE_ATTRIBUTE_TEMPORARY
256 (0x100)
1. 文件用于存储临时数据

2. 更多信息,参见 Caching Behavior

标志位 含义
FILE_FLAG_BACKUP_SEMANTICS
(0x02000000)
1. 此文件已经被打开或者创建以用于备份或者恢复操作。当调用进程拥有 SE_BACKUP_NAME 和 SE_RESTORE_NAME 权限时,系统保证调用进程覆盖文件安全检测。更多信息,参见 Changing Privileges in a Token

2. 为了获取一个文件夹句柄,你必须设置这个标记位。某些函数只能接受文件夹句柄而非文件句柄。更多信息,参见本文备注
FILE_FLAG_DELETE_ON_CLOSE
(0x04000000)
1. 在所有与此文件相关的句柄被关闭后,文件马上会被删除。与此文件相关的句柄包括当前句柄和任意其他打开或者被复制的句柄

2. 如果存在指向文件的句柄,且所有这些句柄是以 FILE_SHARE_DELETE 共享模式创建,则函数执行失败。如果没有指定 FILE_SHARE_DELETE 共享模式,文件后续的打开请求都会失败
FILE_FLAG_NO_BUFFERING
(0x20000000)
1. 文件或设备被以读写无缓冲区的方式打开。此标记位对于硬盘或者内存映射文件无效。

2. 此外,若想让 FILE_FLAG_NO_BUFFERING 标记位正常工作,还需要满足一些严格的要求,具体参见 File Buffering
FILE_FLAG_OPEN_NO_RECALL
(0x00100000)
文件数据被请求,但它不应该继续在远程存储器中被定位。文件数据不应该传输回本地存储器。这个标记位被远程存储系统所使用
FILE_FLAG_OPEN_REPARSE_POINT
(0x00200000)
1. 不会发生正常的 reparse point 过程。 CreateFile 函数将会尝试去打开一个 reparse point。当一个文件被打开,无论控制 reparse point 的过滤器是否可操作,都会返回一个文件句柄

2. 此标记无法与 CREATE_ALWAYS 标记位共同使用

3. 如果文件不是一个 reparse point,那么此标记位无效

4. 更多信息,参见本文备注
FILE_FLAG_OVERLAPPED
(0x40000000)
1. 此文件或设备被打开或创建,用于异步 I/O

2. 当后续的 I/O 操作通过此句柄执行完成,则 OVERLAPPED 结构中指定的事件会被设置为受信状态

3. 如果此标记位被指定,此文件可以被用于同步读写操作

4. 如果此标记为未被指定,则 I/O 操作被序列化,即使对读写函数的调用指定了一个 OVERLAPPED 结构

5. 更多关于使用一个指定了此标记位的文件句柄的信息,参见本页 Synchronous and Asynchronous I/O Handles 章节
FILE_FLAG_POSIX_SEMANTICS
(0x0100000)
遵循 POSIX 规则,这包括允许多个文件名,只是在不同的情况下,为支持该命名规则的文件系统。当使用此选项时要小心,因为一个使用此标记位创建的文件可能无法被某些为 MS-DOS 或 16 位 Windows 操作系统所编写的程序所访问
FILE_FLAG_RANDOM_ACCESS
(0x10000000)
1. 文件访问为随机访问。此系统可以使用这个用于优化文件缓存

2. 如果文件系统不支持 I/O 缓冲和 FILE_FLAG_NO_BUFFERING 标记,则此标记位无效

3. 更多信息,参见 Caching Behavior
FILE_FLAG_SESSION_AWARE
(0x00800000)
1. 文件或设备带 session awareness 打开。如果此标记位没有被指定,则 per-session 设备(一个使用 RemoteFX USB 重定向的设备)无法被运行在 session 0 的进程所打开。如果调用进程没有运行在 session 0,则此标记位无效。此标记位只支持在 server 版本的 Windows

2. Windows Server 2008 R2 和 Windows Server 2008:此标记位不支持 Windows Server 2012 之前的版本
FILE_FLAG_SEQUENTIAL_SCAN
(0x08000000)
1. 表示文件访问将从头顺序读到尾。系统利用此标记位用于优化文件缓存

2. 如果你打算从文件尾读到文件头(即反向扫描文件),则不该使用此标记位

3. 如果文件系统不支持 I/O 缓存和 FILE_FLAG_NO_BUFFERING 标记位,则此标记位无效

4. 更多信息,参见 Caching Behavior
FILE_FLAG_WRITE_THROUGH
(0x80000000)
1. 写操作不会通过任何中间缓冲区缓冲,而是直接写回磁盘

2. 更多信息,参见 Caching Behavior

7. dwFlagsAndAttributes 参数还可以指定 SQOS 信息。更多信息,参见 Impersonation Levels。当程序在 dwFlagsAndAttribbutes 指定了 SECURITY_SQOS_PRESENT 标记位,它还可以包含下表一个或多个值:

安全标志 含义
SECURITY_ANONYMOUS将客户端模拟在匿名级别
SECURITY_CONTEXT_TRACKING指定安全跟踪模式是动态的。 如果此标记位未被指定,则安全跟踪模式为静态的
SECURITY_DELEGATION将客户端模拟在授权级别
SECURITY_EFFECTIVE_ONLY1. 服务器只能访问到客户端安全配置中被允许的数据。如果此标记位未被指定,则客户端安全配置中所有数据都能访问

2. 当模拟一个客户端时,此标记位允许客户端去限制服务端可以使用的组和权限
SECURITY_IDENTIFICATION将客户端模拟在身份认证级别
SECURITY_IMPERSONATION1. 将客户端模拟在伪装级别

2. 如果没有其他标记位和 SECURITY_SQOS_PRESENT 标记位共同被指定,则当前标记位是默认标记
hTemplateFile1. 指向一个拥有 GENERIC_READ 访问权限的的模板文件的合法句柄

2. 此模板文件为即将创建的文件提供属性和扩展属性

3. 参数值可以为 NULL

4. 如果打开一个已存在的文件,则 CreateFile 函数忽略这个参数

5. 如果打开一个新的被加密文件,此函数从其父目录中继承自由存取控制列表。更多信息,见 File Encryption


返回值:

1. 如果函数调用成功,返回指向指定文件的句柄、设备、命名管道或者邮槽;

2. 如果函数调用失败,返回值为 INVALID_HANDLE_VALUE。

想获取具体错误信息,调用 GetLastError 函数。


备注:

1. CreateFile 函数最初被官方开发用于文件操作,但后来功能被扩展加强,Windows 开发者可用它来处理绝大部分其他类型的 I/O 设备和机器。当前备注试图讲解开发者在不同的环境下使用不同的 I/O 设备过程中,调用 CreateFile 函数可能遇到的不同的问题。
这篇文章中,仅当数据是存储在一个文件系统上的真正的文件,才会使用”文件”这个词。然而,在提及“文件”的情况下,有一些只得是更广义上的 I/O 对象,这些 I/O 对象支持类似文件操作的机制。由于前面所提及到的历史原因,“文件”这个名词使用过于自由,已经广泛被各种常量名称和参数名称所使用。

2. 当 CreateFile 函数返回的句柄不再使用时,调用 CloseHandle 函数关闭这个句柄。这不仅只是释放系统资源,对于如文件或设备的共享和磁盘的数据提交等仍有很大的影响。具体细节在本文描述有介绍。

3. Windows Server 2003 and Windows XP:当我们试图打开一个远程文件或目录以用于删除,此时如果指定了 dwDesiredAccess 参数包含了删除标记(0x00010000),且远程文件或目录并未以 FILE_SHARE_DELETE 属性被打开,那么就会引发共享错误。在这个例子中,为了避免共享错误发生,仅且仅以删除权限打开远程文件或目录,或者不用打开文件而是直接调用 DeleteFile 函数去删除文件。

4. 一些文件系统,如 NTFS 文件系统,支持某些文件和文件夹的压缩和加密。如果一个分区装载了支持这些功能的文件系统,则一个新文件会从它所属文件夹中继承这些压缩和加密的属性。

5. 你无法使用 CreateFile 函数去控制文件的压缩、解压或加密。更多信息,参见 Creating and Opening Files, File Compression and DecompressionFile Encryption

6. Windows Server 2003 and Windows XP:为了实现向后兼容,当你在 lpSecurityAttributes 参数中指定一个安全描述符时,CreateFile 函数并不会应用继承规则。为了支持继承,后续询问文件安全描述符的函数可能会启发式地去决定并告知继承已经生效了。更多信息,参见 Automatic Propagation of Inheritable ACEs

7. 正如前面所阐述的,如果 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) 支持

注意:当 CreateFile 函数设置将会替代旧文件,如果所处理的文件已经打开了一个数据流,则函数会执行失败。

8. 符号链接相关

如果此函数的调用产生了一个新文件,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 标记,被影响的文件为目标文件

9. 文件缓存

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 属性值会告诉系统尽可能地不要将数据写到磁盘中,因此这可能会对某些程序很有意义。

10. 文件

如果你重命名或删除一个文件,然后又马上恢复原样,则系统会搜索缓存区中的文件信息去执行恢复操作。被缓存的信息包括短、长文件名和文件创建时间。

操作系统会延迟删除操作、直到所有指向此文件的句柄被关闭。如果某个文件正在等待被执行 DeleteFile 函数,此时对此文件调用 CreateFile 函数,则 CreateFile 函数执行失败。GetLastError 函数会返回 ERROR_ACCESS_DENIED。

dwDesiredAccess 参数可以为 0,此时如果程序的权限足够,则系统允许程序不访问文件而直接获取文件的属性值。 这在以下情况是很有用的:当我们不以读访问或写访问打开一个文件,却想测试一个文件是否存在或者获得其他关于此文件或目录的数据。参见 Obtaining and Setting File InformationGetFileInformationByHandle 函数。

如果指定了 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

11. 同步和异步 I/O 句柄

CreateFile 函数可以创建的文件或设备句柄可以是同步也可以是异步的。一个同步句柄会导致当执行 I/O 调用函数时,调用者会被阻塞知道函数执行结束;而一个异步文件句柄在调用 I/O 函数时,无论 I/O 操作是否执行完成,调用者都会立刻返回。

正如前面所阐述的,使用同步 I/O 还是异步 I/O 由在 dwFlagsAndAttributes 参数中是否指定了 FILE_FLAG_OVERLAPPED 标记位决定。此外异步 I/O 会有一些复杂性和潜在的陷阱,更多信息,参见 Synchronous and Asynchronous I/O

12. 文件流

在 NTFS 文件系统中,你可以使用 CreateFile 函数在一个文件中创建分离的流。想了解更多信息,参见 File Streams

13. 目录

程序无法使用 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

14. 物理磁盘和分区

对磁盘或分区的直接访问是受限制的。更多信息,参见“在 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 边界检查,而是由设备驱动器来执行边界检查。

15. Changer Device

DeviceIoControl 函数设置控制代码为 IOCTL_CHANGER_* 时,会接受一个指向 changer device 的句柄。要打开一个 changer device,使用如下格式的文件名:"\\.\Changerx",其中 x 表示要打开设备的下标,下标从 0 开始。如要在程序中使用 C 或者 C++ 打开 0 号 changer device,使用如下格式的文件名:"\\\\.\\Changer0"。

16. 磁带驱动器

你可以通过使用如下形式的名称来打开一个磁带驱动器:"\\.\TAPEx",x 表示要打开的驱动器的下标,下标从 0 开始。 如要使用 C 或 C++ 打开 0 号磁带驱动器,使用如下格式:"\\\\.\\TAPE0".

更多信息,参见 Backup

17. 通信资源

CreateFile 函数可以创建指向通信资源的句柄,如串行端口 COM1。 要打开一个通信资源,dwCreationDisposition 参数必须设置为 OPEN_EXISTING,dwShareMode 参数必须设置为 0(即只能由自己访问),且 hTemplateFile 参数必须为 NULL。读、写或者读/写权限可以指定任意一个,且此句柄可用于异步 I/O。

如果要指定一个端口号大于 9 的 COM 端口,使用以下的语法:"\\.\COM10"。此语法适用于所有端口号和所有允许 COM 端口号被指定的硬件。

更多关于通信的信息,参见 Communications

18. 控制台

你可以用 CreateFile 函数创建一个指向控制台输入(COCNIN$)的句柄。如果进程通过继承或复制获得这个句柄,它还可以创建一个指向活动屏幕缓存的句柄。调用进程必须被附加到一个被继承的控制台或者一个通过 AllocConsole 函数分配到的控制台。有关控制台句柄的参数设置如下:

参数
lpFileName1. 使用 CONIN$ 值指向控制台输入

2. 使用 CONOUT$ 值指向控制台输出

3. CONIN$ 获得一个指向控制台输入缓存的句柄,即使控制台标准输入句柄被 SetStdHandle 函数重定向了。使用 GetStdHandle 函数可以获取标准输入句柄

4. 使用 CONOUT$ 获取一个执行活动屏幕缓存的句柄,即使标准输出句柄被 SetStdHandle 函数重定向了。使用 GetStdHandle 函数可以获取标准输出句柄
dwDesiredAccess更推荐使用 GENERIC_READ | GENERIC_WRITE,但任选其一也可以用于设置权限
dwShareMode1. 当打开 CONIN$ 时,要指定 FILE_SHARE_READ;当打开 CONOUT$ 时,要指定 FILE_SHARE_WRITE

2. 如果这个控制台是调用进程通过继承获得的,或如果一个子进程要想访问这个控制台,此参数必须设置为 FILE_SHARE_READ | 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

19. 邮槽

如果 CreateFile 函数打开邮槽,那么当客户端试图打开一个本地邮槽,而服务端并没有使用 CreateMailSlot 函数创建这个邮槽时,则返回 INVALID_HANDLE_VALUE。

更多信息,参见 Mailslots

20. 管道

如果 CreateFile 函数打开一个客户端的命名管道,则此函数使用任意处于监听状态的命名管道句柄实例。进程可以无限复制命名管道句柄,但是当它被打开后,命名管道无法被另一个客户端再次打开。被指定的命名管道访问权限必须与使用函数 CreateNamedPipe 创建命名管道时,在 dwOpenMode 参数中设置的权限相兼容。

如果 CreateNamedPipe 函数没有在服务端被成功调用,则命名管道不会被创建且 CreateFile 函数执行失败,且返回 ERROR_FILE_NOT_FOUND。
如果在服务器上有至少一个活动的管道实例,但是不存在监听者管道,即所有的管道实现在此时都被连接了,CreateFile 函数执行失败且返回错误信息ERROR_PIPE_BUSY。

更多信息,参见 Pipes


例子

1. 关于文件操作的例子如下:


2. 物理设备 I/O 在以下单元显示:

3. 一个使用命名管道的例子放在 Named Pipe Client

4. 使用邮件槽的例子在 Writing to a Mailslot

5. 一份磁盘备份的代码片段可以在这里找到 Creating a Backup Application


需求:

Minimum supported client Windows XP [仅桌面应用程序]
Minimum supported server Windows 2003 服务器版 [仅桌面应用程序]
Header FileAPI.h (包含于 Windows.h)
在 Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 和 Windows XP 系统上的 WinBase.h (包含于 Windows.h)
LibraryKernel32.lib
DLL Kernel32.dll
Unicode and ANSI namesCreateFileW (Unicode) 和 CreateFileA (ANSI)


【API档案】版权归鱼C工作室(www.fishc.com)所有,转载请注明来源。



想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2016-6-19 12:50:03 | 显示全部楼层
讲CreateFile函数吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-22 14:48

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表