CreateFile()用来创建或打开一个文件,或打开一个I/O设备。CreateFile()可以操作的I/O设备有很多,比如文件流(File Stream)、物理磁盘、卷、控制台、磁带机、通信端口(比如COM口)、邮件槽、管道等。该函数最初是用来操作文件的,但随着系统功能的丰富,CreateFile()的功能也被扩充到可以操作大多数的设备。由于涉及的领域很广,CreateFile()的参数众多,逻辑复杂,本文尝试对其逻辑进行一下大致梳理,以便于读者更方便地调用该函数。
HANDLE CreateFile(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in,optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndSttributes,
[in,optional] HANDLE hTemplateFile
);
CraeteFile()返回一个可用于访问文件或I/O设备地句柄,具体可以进行哪些操作取决于输入参数中指定的数值。如果该函数运行失败,会返回INVALIDE_HANDLE_VALUE,具体错误信息可调用GetLastError()获取。下面解释一下各参数。
lpFileName用来指定期望操作的文件或设备的名称。对于文件,名称的长度受限于MAX_PATH常量,但在名称前面加上“\\?\”前缀可将长度扩大到32767个宽字符的长度。对于物理磁盘,需采用“\\.\PhysicalDriveX”的形式,X是从0开始的编号,比如“\\.\PhysicalDrive0”表示第一个磁盘。对于更换设备(Changer Device),其名称采用“\\.\ChangerX”的形式,X也是起始于0的编号。如果要打开卷或可移动存储(比如软盘或U盘),应采用”\\.\X:“的形式,X是以字母表示的盘符,比如”\\.\A:“表示A盘;如果最后面再跟一个反斜线(\)表示该盘的根目录。对于磁带设备,用”\\.\TAPEX“,比如”\\.\TAPE0“表示第一个磁带机。对于通信端口,采用"COMX",X表示从0到9的序号,如果X大于9,就要用”\\.\COMX“这种形式,比如”\\.\COM10“。这种形式也可用于标号为0到9的端口。对于主控台,用”CONIN$“和”CONOUT$“分别表示输入和输出设备。对于管道,采用”\\.\pipe\pipename“的形式。
dwDesiredAccess用来指定对文件或设备所期望的操作,可以是读(GENERIC_READ)、写(GENERIC_WRITE)、既读又写,或者为0。如果该参数设置为0,CreateFile()可以查询到文件或设备的属性信息而不用访问其中的数据。
dwShareMode用来指定期望与其他调用者对文件或设备采用的共享模式。可以是共享读(FILE_SHARE_READ)、共享写(FILE_SHARE_WRITE)、删除(FILE_SHARE_DELETE)、前者都指定、或为0。比如调用者以共享读模式打开文件,说明在此之前可能已经有其他调用者以共享读模式打开了文件,同时后续调用者也可以用共享读模式打开该文件。如果调用者指定了FILE_SHARE_DELETE,表示也允许后续调用者采用该共享模式打开文件,并对文件执行删除或重命名操作。
如果dwShareMode被设置为0,表示不进行任何形式的共享,只要CreateFile()调用成功,那么在返回的句柄关闭之前,将不允许后续调用者以任何形式打开该文件或设备。
dwCreationDispositon指对所指定文件或设备是否已经存在所做的处理规定,囊括了以下几种场景:
创建文件/设备 | |
CREATE_NEW | 如果指定文件不存在,且对指定的文件路径有写权限,则创建该文件。 如果指定文件已存在,函数失败,GetLastError()会返回ERROR_FILE_EXISTS(80)。 |
CREATE_ALWAYS | 指定文件已存在,但将其清空为一个新文件。这时函数成功返回但会设置last-error为ERROR_ALREADY_EXISTS(183)。 如果指定文件不存在,且对指定的文件路径有写权限,则会创建一个新文件,函数成功返回。 |
打开文件/设备 | |
OPEN_EXISTING | 如果指定的文件或设备不存在,函数失败且last-error被设置为ERROR_FILE_NOT_FOUND(2)。 对于打开设备的调用,通常都是指定该值。 |
TRUNCATE_EXISTING | 打开现有文件但将其截为空文件。调用者必须在dwDesiredAccess中设置GENERIC_WRITE。 如果指定的文件或设备不存在,函数失败且last-error被设置为ERROR_FILE_NOT_FOUND(2)。 |
OPEN_ALWAYS | 即使指定文件不存在也要创建该文件。 如果指定文件已存在,函数成功返回但会设置last-error为ERROR_ALREADY_EXISTS(183)。 如果指定文件不存在,函数会创建该文件且成功返回,不设置last-error。 |
经过以上对lpFileName、dwDesiredAccess、dwShareMode、dwCreationDisposition参数的介绍,基本上说明了CreateFile()的主体语义,即通过lpFileName、dwDesiredAccess用来说明希望对指定文件所进行的操作,同时再考虑文件或设备当前是否存在以及是否已被别人打开,将其体现到dwCreationDispositon和dwShareMode中。做好这些基本决定后,剩下的dwFlagsAndAttributes、lpSecurityAttributes和hTemplateFile就是与细节相关的设置了。
需要特别注意的是,当打开一个已经被打开的文件或设备时,要确保dwShareMode与前面调用者指定的值兼容,同时dwDesiredAccess也不能与其冲突。