PoEdu培训 Windows班 第五课 WindowsAPI之CreateFile
文章类别: 培训笔记 0 评论

PoEdu培训 Windows班 第五课 WindowsAPI之CreateFile

文章类别: 培训笔记 0 评论

CreateFile

CreateFile

// 原型
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
    );
注意:
_In_ 表示这是一个输入参数
_In_opt_ 表示这是一个 输入指针参数
_Out_ 表示这是一个输出参数
_Out_opt_ 表示这是一个 输出指针参数

它其实包含了两个版本


WINBASEAPI
HANDLE
WINAPI
CreateFileA(
    _In_ LPCSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    );

WINBASEAPI
HANDLE
WINAPI
CreateFileW(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
    );

#ifdef UNICODE
#define CreateFile  CreateFileW
#else
#define CreateFile  CreateFileA
#endif // !UNICODE

所以, 有很多API都包括了宽字节和窄字节两个版本.

使用CreateFile

注意, 不推荐直接使用CreateFile, 推荐使用CreateFileW

// 使用CreateFile的正确姿势
#include <Windows.h>

INT main()
{
    TCHAR szFileName[] = TEXT("Test.txt");
    HANDLE hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile)
    {
        // 文件打开成功
        // ... 进行操作
        CloseHandle(hFile);
    }
    else
        // 文件打开失败

    return 0;
}

以上代码就可以在ASNI和UNICODE中自由的切换了

CreateFile的参数

更加具体请看 MSDN.aspx)

lpFileName

  1. 指定要打开、创建的文件或设备的名字。你可以在名字中使用斜杠(/)或者反斜杠()
  2. 在此函数的 ANSI 版本中,名字长度被限制为 MAX_PATH 个字符。为了将此限制扩展到 32767 个宽字符,需要调用此函数的 Unicode 版本,并且在添加在路径名中添加 “?” 前缀。获取更多的信息,参见 Naming Files, Paths, and Namespaces
  3. 想获取关于特殊设备的名字,参见 Defining an MS-DOS Device Name
  4. 为了创建一个文件流,需要指定文件名,一个冒号加上流文件的名字。获取更多信息,参见 File Streams

dwDesiredAccess

  1. 指定以何种权限打开文件或设备,可以归纳为:读访问权、写访问权、读写访问权、非读非写访问权
  2. 最常使用的值为 GENERIC_READ, GENERIC_WRITE, 或者二者都用(GENERIC_READ | GENERIC_WRITE)。获取更多信息,参见 Generic Access Rights, File Security and Access Rights, File Access Rights Constants 和 ACCESS_MASK
  3. 如果参数值为 0,那么程序可以在不访问文件或设备情况下,询问某些元数据,如文件、目录或者设备属性。此外,即使 GENERIC_READ 请求,也会被拒绝
  4. 你无法请求一个与共享模式冲突的访问权限。共享模式是在参数 dwShareMode 中设置的
  5. 获取更多信息,参见本文备注以及 Creating and Opening Files

dwShareMode

  1. 设置文件或者设备的共享模式,包括读、写、读写、删除、全部权限或者以上什么权限都没有(参考下面的表格)。此参数不影响对属性和扩展属性的访问请求
  2. 如果此参数为 0 且 CreateFile 函数执行成功,那么此文件或设备无法被共享,且在其句柄被关闭前,无法被再次打开。更多信息,参见本文备注。
  3. 你无法设置一个与访问模式相冲突的共享模式。此时如果 CreateFile 函数执行失败,那么 GetLastError 函数会返回 ERROR_SHARING_VIOLATION
  4. 为了允许进程去共享一个已经在另一个进程中打开的文件或句柄,那么可对以下一个或多个取值进行组合,且各取值间需要可互相兼容。更多有关此参数与 dwDesiredAccess 参数的合法取值组合的信息,参见 Creating and Opening Files
  5. 注意:在句柄被关闭之前,每个句柄的共享选项都会一直生效,且与进程的运行上下文(process context)无关。
含义
0 防止其他进程以删除、读取或写入权限打开一个文件或者设备
FILE_SHARE_DELETE(0x00000004) 1.允许随后的对文件或设备的打开操作获取删除权限 <br/>2.否则,其他进程无法以删除权限打开一个文件或设备 <br/>3.如果此标记位没有指定,但文件或设备已经打开并且获取了删除权限,则此函数执行失败<br/>4.注意:删除权限运行删除操作和重命名操作
FILE_SHARE_READ(0x00000001) 1.允许后续对文件或设备的读权限打开请求<br/>2.否则,其他进程无法以读权限打开此文件<br/>3.如果此处标记位未被指定,但是文件或设备被以读权限打开,则函数执行失败
FILE_SHARE_WRITE(0x00000002) 1.允许后续对文件或设备的写权限打开请求

lpSecurityAttributes

  1. 指向 SECURITY_ATTRIBUTES 结构体的指针。此结构体拥有两个分开但是相关的数据成员:一个是可选的安全描述符,另一个是决定返回的句柄是否可以被子进程继承的 Boolean 值
  2. 此参数可以设置为 NULL
  3. 如果参数为 NULL,那么 CreateFile 函数返回的句柄无法被任意此进程的子进程所继承,且与返回的句柄所对应的文件或句柄拥有一个默认的安全描述符
  4. 该结构体的 lpSecurityDescriptor 成员为文件或设备指定一个安全描述符(SECURITY_DESCRIPTOR)。如果此成员取值为 NULL,那么与返回的句柄所对应的文件或句柄拥有一个默认的安全描述符
  5. 当打开一个已经存在的文件或设备时,CreateFile 函数忽略 lpSecurityDescriptor 成员,但是 bInheritHandle 成员仍然可以使用
  6. 结构体的 bInheritHandle 成员用于设置返回的句柄是否可以被继承
  7. 更多信息,参见本文备注

dwCreationDisposition

  1. 用于设置当文件存在或不存在时,要对文件或设备执行何种操作
  2. 对于设备来说,此参数通常设置为 OPEN_EXISTING
  3. 更多信息,参见本文备注
  4. 这个参数的值必须为以下值之一,且只能选择一个而不能组合多个:
含义
CREATE_ALWAYS 1.总是创建一个新文件<br/>2.如果指定的文件已经存在而且是可写的,此时函数重写(overwrite)这个文件,函数为执行成功、且将 last-error code 设置为 ERROR_ALREADY_EXISTS(183)<br/>3.如果指定的文件不存在且其路径名合法,那么会创建一个新文件,函数为执行成功、且将 last-error code 设为 0<br/>4. 更多信息,参见本文备注
CREATE_NEW 1.仅当文件不存在时,才创建一个新文件<br/>2.如果指定文件已存在,函数执行失败且将 last-error code 设为 ERROR_FILE_EXISTS(80)<br/>3.如果指定文件不存在,当路径名合法而且可写时,则创建一个新文件
OPEN_ALWAYS 1.总是打开一个文件<br/>2.如果指定的文件存在,函数执行成功,且 last-error code 设置为 ERROR_ALREADY_EXISTS(183)<br/>3.如果指定文件不存在,当路径名合法而且可写时,则创建一个新文件,last-error code 设置为 0
OPEN_EXISTING 1.仅当文件或设备存在时,才打开它<br/>2.如果指定的文件或设备不存在,函数执行失败且 last-error code 设置为 ERROR_FILE_NOT_FOUND(2)3.更多信息,参见本文备注
TRUNCATE_EXISTING 1.仅当文件存在时,才打开一个文件并且将其大小截取到 0 字节<br/>2.如果指定的文件不存在,函数执行失败且 last-error code 设置为 ERROR_FILE_NOT_FOUND(2)<br/>3.调用方需要设置 dwDesiredAccess 参数的 GENERIC_WRITE 位

dwFlagsAndAttributes

  1. 文件或设备的属性值和标记位,对于文件来说,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 文件应该被存档。应用程序使用此属性来备份或移除标记文件
FILE_ATTRIBUTE_ENCRYPTED 1.文件或目录被加密。对于文件来说,这意味着文件中所有数据都被加密了;对于目录来说,这意味着加密新创建的文件和子文件夹是默认选项。更多信息,参见 File Encryption<br/>2.如果指定了FILE_ATTRIBUTE_SYSTEM标记位,则这个标记位不起作用<br/>3.这个标记位在以下 Windows 版本中不被支持:家庭版、家庭高级版、简易版或 ARM 版本
FILE_ATTRIBUTE_HIDDEN 文件被隐藏。即在普通文件夹中不被显示
FILE_ATTRIBUTE_NORMAL 这个文件不包含其他属性。只有当它单独使用时,这个属性值才是合法的
FILE_ATTRIBUTE_OFFLINE 文件的数据无法直接访问。这个属性表示文件数据在物理设备中被移动到一个离线存储器。这个属性被远程存储(Remote Storage)—— 即分级存储管理软件 —— 所使用。我们的程序不能随意改变这个属性值
FILE_ATTRIBUTE_READONLY 文件只读。程序可以读取文件,但无法写入或删除文件
FILE_ATTRIBUTE_SYSTEM 文件是操作系统的的一部分或者专门为操作系统所使用
FILE_ATTRIBUTE_TEMPORARY 1.文件用于存储临时数据<br/>2.更多信息,参见 Caching Behavior
FILE_FLAG_BACKUP_SEMANTICS 1.此文件已经被打开或者创建以用于备份或者恢复操作。当调用进程拥有 SE_BACKUP_NAME 和 SE_RESTORE_NAME 权限时,系统保证调用进程覆盖文件安全检测。更多信息,参见 Changing Privileges in a Token<br/>2.为了获取一个文件夹句柄,你必须设置这个标记位。某些函数只能接受文件夹句柄而非文件句柄。更多信息,参见本文备注
FILE_FLAG_DELETE_ON_CLOSE 1.在所有与此文件相关的句柄被关闭后,文件马上会被删除。与此文件相关的句柄包括当前句柄和任意其他打开或者被复制的句柄<br/>2.如果存在指向文件的句柄,且所有这些句柄是以 FILE_SHARE_DELETE 共享模式创建,则函数执行失败。如果没有指定 FILE_SHARE_DELETE 共享模式,文件后续的打开请求都会失败
FILE_FLAG_NO_BUFFERING 1.文件或设备被以读写无缓冲区的方式打开。此标记位对于硬盘或者内存映射文件无效。<br/>2.此外,若想让 FILE_FLAG_NO_BUFFERING 标记位正常工作,还需要满足一些严格的要求,具体参见 File Buffering
FILE_FLAG_OPEN_NO_RECALL 文件数据被请求,但它不应该继续在远程存储器中被定位。文件数据不应该传输回本地存储器。这个标记位被远程存储系统所使用
FILE_FLAG_OPEN_REPARSE_POINT 1.不会发生正常的 reparse point 过程。 CreateFile 函数将会尝试去打开一个 reparse point。当一个文件被打开,无论控制 reparse point 的过滤器是否可操作,都会返回一个文件句柄<br/>2.此标记无法与 CREATE_ALWAYS 标记位共同使用<br/>3.如果文件不是一个 reparse point,那么此标记位无效<br/>4.更多信息,参见本文备注
FILE_FLAG_OVERLAPPED 1.此文件或设备被打开或创建,用于异步 I/O<br/>2.当后续的 I/O 操作通过此句柄执行完成,则 OVERLAPPED 结构中指定的事件会被设置为受信状态<br/>3.如果此标记位被指定,此文件可以被用于同步读写操作<br/>4.如果此标记为未被指定,则 I/O 操作被序列化,即使对读写函数的调用指定了一个 OVERLAPPED 结构<br/>5.更多关于使用一个指定了此标记位的文件句柄的信息,参见本页 Synchronous and Asynchronous I/O Handles 章节
FILE_FLAG_POSIX_SEMANTICS 遵循 POSIX 规则,这包括允许多个文件名,只是在不同的情况下,为支持该命名规则的文件系统。当使用此选项时要小心,因为一个使用此标记位创建的文件可能无法被某些为 MS-DOS 或 16 位 Windows 操作系统所编写的程序所访问
FILE_FLAG_RANDOM_ACCESS 1.文件访问为随机访问。此系统可以使用这个用于优化文件缓存<br/>2.如果文件系统不支持 I/O 缓冲和 FILE_FLAG_NO_BUFFERING 标记,则此标记位无效<br/>3.更多信息,参见 Caching Behavior
FILE_FLAG_SESSION_AWARE 1.文件或设备带 session awareness 打开。如果此标记位没有被指定,则 per-session 设备(一个使用 RemoteFX USB 重定向的设备)无法被运行在 session 0 的进程所打开。如果调用进程没有运行在 session 0,则此标记位无效。此标记位只支持在 server 版本的 Windows<br/>2.indows Server 2008 R2 和 Windows Server 2008:此标记位不支持 Windows Server 2012 之前的版本
FILE_FLAG_SEQUENTIAL_SCAN 1.表示文件访问将从头顺序读到尾。系统利用此标记位用于优化文件缓存<br/>2.如果你打算从文件尾读到文件头(即反向扫描文件),则不该使用此标记位<br/>3.如果文件系统不支持 I/O 缓存和 FILE_FLAG_NO_BUFFERING 标记位,则此标记位无效<br/>4.更多信息,参见 Caching Behavior
FILE_FLAG_WRITE_THROUGH 1.写操作不会通过任何中间缓冲区缓冲,而是直接写回磁盘<br/>2.更多信息,参见 Caching Behavior<br/>3. wFlagsAndAttributes 参数还可以指定 SQOS 信息。更多信息,参见 Impersonation Levels。当程序在 dwFlagsAndAttribbutes 指定了 SECURITY_SQOS_PRESENT 标记位,它还可以包含下表一个或多个值:<br/>SECURITY_ANONYMOUS 将客户端模拟在匿名级别<br/>SECURITY_CONTEXT_TRACKING 指定安全跟踪模式是动态的。 如果此标记位未被指定,则安全跟踪模式为静态的<br/>SECURITY_DELEGATION 将客户端模拟在授权级别<br/>SECURITY_EFFECTIVE_ONLY 服务器只能访问到客户端安全配置中被允许的数据。如果此标记位未被指定,则客户端安全配置中所有数据都能访问;当模拟一个客户端时,此标记位允许客户端去限制服务端可以使用的组和权限<br/>SECURITY_IDENTIFICATION 将客户端模拟在身份认证级别<br/>SECURITY_IMPERSONATION 将客户端模拟在伪装级别;如果没有其他标记位和<br/>SECURITY_SQOS_PRESENT 标记位共同被指定,则当前标记位是默认标记

hTemplateFile

  1. 指向一个拥有 GENERIC_READ 访问权限的的模板文件的合法句柄
  2. 此模板文件为即将创建的文件提供属性和扩展属性
  3. 参数值可以为 NULL
  4. 如果打开一个已存在的文件,则 CreateFile 函数忽略这个参数
  5. 如果打开一个新的被加密文件,此函数从其父目录中继承自由存取控制列表。更多信息,见 File Encryption

返回值

  1. 如果函数调用成功,返回指向指定文件的句柄、设备、命名管道或者邮槽;
  2. 如果函数调用失败,返回值为 INVALID_HANDLE_VALUE。
  3. 想获取具体错误信息,调用 GetLastError 函数。

事务型操作

未完待续...

如有错误,请提出指正!谢谢.

回复