文件的打开和关闭

文件的打开

下面的函数用于打开一个文件:

    NTSTATUS ZwCreateFile(
        OUT PHANDLE FileHandle,                            
        IN ACCESS_MASK DesiredAccess,                    
        IN POBJECT_ATTRIBUTES ObjectAttribute,    
        OUT PIO_STATUS_BLOCK IoStatusBlock,
        IN PLARGE_INTEGER AllocationSize OPTIONAL,
        IN ULONG FileAttributes,
        IN ULONG ShareAccess,
        IN ULONG CreateDisposition,
        IN ULONG createOptions,
        IN PVOID EaBuffer OPTIONAL,
        IN ULONG EaLength);

这个函数的参数异常复杂。下面逐个的说明如下:

  • FileHandle:是一个句柄的指针。如果这个函数调用返回成成功(STATUS_SUCCESS),那就么打开的文件句柄就返回在这个地址内。
  • DesiredAccess:申请的权限。如果打开写文件内容,请使用FILE_WRITE_DATA。如果需要读文件内容,请使用FILE_READ_DATA。如果需要删除文件或者把文件改名,请使用DELETE。如果想设置文件属性,请使用FILE_WRITE_ATTRIBUTES。反之,读文件属性则使用FILE_READ_ATTRIBUTES。这些条件可以用|(位或)来组合。有两个宏分别组合了常用的读权限和常用的写权限。分别为GENERIC_READ和GENERIC_WRITE。此外还有一个宏代表全部权限,是GENERIC_ALL。此外,如果想同步的打开文件,请加上SYNCHRONIZE。同步打开文件详见后面对CreateOptions的说明。
  • ObjectAttribute:对象描述。见前一小节。
  • IoStatusBlock也是一个结构。这个结构在内核开发中经常使用。它往往用于表示一个操作的结果。这个结构在文档中是公开的,如下:
    typedef struct _IO_STATUS_BLOCK {
      union {
          NTSTATUS Status;
          PVOID Pointer;
      };
      ULONG_PTR Information;
    } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
    
    实际编程中很少用到Pointer。一般的说,返回的结果在Status中。成功则为STATUS_SUCCESS。否则则是一个错误码。进一步的信息在Information中。不同的情况下返回的Information的信息意义不同。针对ZwCreateFile调用的情况,Information的返回值有以下几种可能:
  • FILE_CREATED:文件被成功的新建了。
  • FILE_OPENED: 文件被打开了。
  • FILE_OVERWRITTEN:文件被覆盖了。
  • FILE_SUPERSEDED: 文件被替代了。
  • FILE_EXISTS:文件已存在。(因而打开失败了)。
  • FILE_DOES_NOT_EXIST:文件不存在。(因而打开失败了)。

这些返回值和打开文件的意图有关(有时希望打开已存在的文件,有时则希望建立新的文件等等。这些意图在本小节稍后的内容中详细说明。

ZwCreateFile的下一个参数是AllocationSize。这个参数很少使用,请设置为NULL。 再接下来的一个参数为FileAttributes。这个参数控制新建立的文件的属性。一般的说,设置为FILE_ATTRIBUTE_NORMAL即可。在实际编程中,笔者没有尝试过其他的值。

ShareAccess是一个非常容易被人误解的参数。实际上,这是在本代码打开这个文件的时候,允许别的代码同时打开这个文件所持有的权限。所以称为共享访问。一共有三种共享标记可以设置:FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_DELETE。这三个标记可以用|(位或)来组合。举例如下:如果本次打开只使用了FILE_SHARE_READ,那么这个文件在本次打开之后,关闭之前,别次打开试图以读权限打开,则被允许,可以成功打开。如果别次打开试图以写权限打开,则一定失败。返回共享冲突。

同时,如果本次打开只只用了FILE_SHARE_READ,而之前这个文件已经被另一次打开用写权限打开着。那么本次打开一定失败,返回共享冲突。其中的逻辑关系貌似比较复杂,读者应耐心理解。

CreateDisposition参数说明了这次打开的意图。可能的选择如下(请注意这些选择不能组合):

  • FILE_CREATE:新建文件。如果文件已经存在,则这个请求失败。
  • FILE_OPEN:打开文件。如果文件不存在,则请求失败。
  • FILE_OPEN_IF:打开或新建。如果文件存在,则打开。如果不存在,则失败。
  • FILE_OVERWRITE:覆盖。如果文件存在,则打开并覆盖其内容。如果文件不存在,这个请求返回失败。
  • FILE_OVERWRITE_IF:新建或覆盖。如果要打开的文件已存在,则打开它,并覆盖其内存。如果不存在,则简单的新建新文件。
  • FILE_SUPERSEDE:新建或取代。如果要打开的文件已存在。则生成一个新文件替代之。如果不存在,则简单的生成新文件。

请联系上面的IoStatusBlock参数中的Information的说明。

最后一个重要的参数是CreateOptions。在惯常的编程中,笔者使用FILE_NON_DIRECTORY_FILE| FILE_SYNCHRONOUS_IO_NONALERT。此时文件被同步的打开。而且打开的是文件(而不是目录。创建目录请用FILE_ DIRECTORY_FILE)。所谓同步的打开的意义在于,以后每次操作文件的时候,比如写入文件,调用ZwWriteFile,在ZwWriteFile返回时,文件写操作已经得到了完成。而不会有返回STATUS_PENDING(未决)的情况。在非同步文件的情况下,返回未决是常见的。此时文件请求没有完成,使用者需要等待事件来等待请求的完成。当然,好处是使用者可以先去做别的事情。

要同步打开,前面的DesiredAccess必须含有SYNCHRONIZE。

此外还有一些其他的情况。比如不通过缓冲操作文件。希望每次读写文件都是直接往磁盘上操作的。此时CreateOptions中应该带标记FILE_NO_INTERMEDIATE_BUFFERING。带了这个标记后,请注意操作文件每次读写都必须以磁盘扇区大小(最常见的是512字节)对齐。否则会返回错误。

这个函数是如此的繁琐,以至于再多的文档也不如一个可以利用的例子。早期笔者调用这个函数往往因为参数设置不对而导致打开失败。非常渴望找到一个实际可以使用的参数的范例。下面举例如下:

// 要返回的文件句柄
HANDLE file_handle = NULL;
// 返回值
NTSTATUS status;
// 首先初始化含有文件路径的OBJECT_ATTRIBUTES
OBJECT_ATTRIBUTES object_attributes;
UNICODE_STRING ufile_name = RTL_CONST_STRING(L”\\??\\C:\\a.dat”);
InitializeObjectAttributes(
    &object_attributes,
    &ufile_name,
    OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
    NULL,
       NULL);
// 以OPEN_IF方式打开文件。
status = ZwCreateFile(
       &file_handle,
       GENERIC_READ | GENERIC_WRITE,
       &object_attributes,
       &io_status,
       NULL,
       FILE_ATTRIBUTE_NORMAL,
       FILE_SHARE_READ,
       FILE_OPEN_IF,
       FILE_NON_DIRECTORY_FILE |
       FILE_RANDOM_ACCESS |
       FILE_SYNCHRONOUS_IO_NONALERT,
       NULL,
       0);

值得注意的是路径的写法。并不是像应用层一样直接写“C:\a.dat”。而是写成了“\??\C:\a.dat”。这是因为ZwCreateFile使用的是对象路径。“C:”是一个符号链接对象。符号链接对象一般都在“\??\”路径下。

文件关闭

这种文件句柄的关闭非常简单。调用ZwClose即可。内核句柄的关闭不需要和打开在同一进程中。示例如下:

ZwClose(file_handle);
  • USB基础
  • USB摄像头UVC
  • USB人机交互HID
  • USB音频UAC
  • Windows基础
  • 磁盘与文件系统
  • Windows编程
  • Windows驱动
  • 开发模块
  • Windows运维
  • Linux相关
  • C语言学习
  • 高级语言
  • 前端开发
  • 服务器开发
  • 数据库
  • 字节流笔记
  • 字节流
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

    打开支付宝扫一扫,即可进行扫码打赏哦

    Powered by bytekits.com,汇天下文字,成非凡梦想!!!