文件的读写操作

打开文件之后,最重要的操作是对文件的读写。读与写的方法是对称的。只是参数输入与输出的方向不同。读取文件内容一般用ZwReadFile,写文件一般使用ZwWriteFile。

NTSTATUS 
ZwReadFile(
    IN HANDLE  FileHandle,
    IN HANDLE  Event  OPTIONAL,
    IN PIO_APC_ROUTINE  ApcRoutine  OPTIONAL,
    IN PVOID  ApcContext  OPTIONAL,
    OUT PIO_STATUS_BLOCK  IoStatusBlock,
    OUT PVOID  Buffer,
    IN ULONG  Length,
    IN PLARGE_INTEGER  ByteOffset  OPTIONAL,
    IN PULONG  Key  OPTIONAL);
  • FileHandle:是前面ZwCreateFile成功后所得到的FileHandle。如果是内核句柄,ZwReadFile和ZwCreateFile并不需要在同一个进程中。句柄是各进程通用的。
  • Event :一个事件。用于异步完成读时。下面的举例始终用同步读,所以忽略这个参数。请始终填写NULL。
  • ApcRoutine Apc:回调例程。用于异步完成读时。下面的举例始终用同步读,所以忽略这个参数。请始终填写NULL。
  • IoStatusBlock:返回结果状态。同ZwCreateFile中的同名参数。
  • Buffer:缓冲区。如果读文件的内容成功,则内容被被读到这个缓冲里。
  • Length:描述缓冲区的长度。这个长度也就是试图读取文件的长度。
  • ByteOffset:要读取的文件的偏移量。也就是要读取的内容在文件中的位置。一般的说,不要设置为NULL。文件句柄不一定支持直接读取当前偏移。
  • Key:读取文件时用的一种附加信息,一般不使用。设置NULL。
  • 返回值:成功的返回值是STATUS_SUCCESS。只要读取到任意多个字节(不管是否符合输入的Length的要求),返回值都是STATUS_SUCCESS。即使试图读取的长度范围超出了文件本来的大小。但是,如果仅读取文件长度之外的部分,则返回STATUS_END_OF_FILE。

ZwWriteFile的参数与ZwReadFile完全相同。当然,除了读写文件外,有的读者可能会问是否提供一个ZwCopyFile用来拷贝一个文件。这个要求未能被满足。如果有这个需求,这个函数必须自己来编写。下面是一个例子,用来拷贝一个文件。利用到了ZwCreateFile,ZwReadFile和ZwWrite这三个函数。不过作为本节的例子,只举出ZwReadFile和ZwWriteFile的部分:

NTSTATUS MyCopyFile(
    PUNICODE_STRING target_path,
    PUNICODE_STRING source_path)
{
    // 源和目标的文件句柄
    HANDLE target = NULL,source = NULL;
    // 用来拷贝的缓冲区
    PVOID buffer = NULL;
    LARGE_INTEGER offset = { 0 };
    IO_STATUS_BLOCK io_status = { 0 };

    do {
        // 这里请用前一小节说到的例子打开target_path和source_path所对应的
        // 句柄target和source,并为buffer分配一个页面也就是4k的内存。
        … …
        // 然后用一个循环来读取文件。每次从源文件中读取4k内容,然后往
        // 目标文件中写入4k,直到拷贝结束为止。 
         while(1) {
            length = 4*1024;        // 每次读取4k。
            // 读取旧文件。注意status。
            status = ZwReadFile (
                    source,NULL,NULL,NULL,
                    &my_io_status,buffer, length,&offset,
                    NULL);
            if(!NT_SUCCESS(status))
            {
                // 如果状态为STATUS_END_OF_FILE,则说明文件
                // 的拷贝已经成功的结束了。
                if(status == STATUS_END_OF_FILE)
                    status = STATUS_SUCCESS;
                break;
            }
            // 获得实际读取到的长度。
            length = IoStatus.Information;

            // 现在读取了内容。读出的长度为length.那么我写入
            // 的长度也应该是length。写入必须成功。如果失败,
            // 则返回错误。
            status = ZwWriteFile(
                target,NULL,NULL,NULL,
                &my_io_status,
                buffer,length,&offset,
                NULL);
            if(!NT_SUCCESS(status))
                break;

            // offset移动,然后继续。直到出现STATUS_END_OF_FILE
            // 的时候才结束。
            offset.QuadPart += length;
        }
    } while(0);

    // 在退出之前,释放资源,关闭所有的句柄。
    if(target != NULL)
        ZwClose(target);
    if(source != NULL)
        ZwClose(source);
    if(buffer != NULL)
        ExFreePool(buffer);
    return STATUS_SUCCESS;
}

除了读写之外,文件还有很多的操作。比如删除、重新命名、枚举。这些操作将在后面实例中用到时,再详细讲解。

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

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

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