Ch11.15: NT File APIs
Overview
Modern Windows (NT, 2000, XP, 7, 10, 11) is built on the NT kernel. The lowest-level user-mode entry points for file I/O are the native system calls:
NtCreateFileNtReadFileNtWriteFileNtClose
These live in ntdll.dll and are the same functions the Win32 subsystem
itself calls. A parallel set of entry points with the Zw prefix
(ZwCreateFile, …) exist as well; they differ only in whether the
previous-mode parameter is set to KernelMode or UserMode. For
user-mode code they behave identically.
fast_io exposes the NT layer through:
-
::fast_io::nt_file— an RAII owning wrapper. The constructor invokesNtCreateFile; the destructor callsNtClose. -
::fast_io::nt_io_observer— a non-owning view over an existing NTHANDLE. Trivially copyable, no destructor.
The programming model is exactly the same pattern as posix_file and
win32_file.
Opening a File with nt_file
#include <fast_io.h>
#include <fast_io_device.h>
int main()
{
using namespace ::fast_io::iomnp;
// The constructor translates open_mode into the corresponding
// NtCreateFile parameters (DesiredAccess, CreateDisposition, ...).
::fast_io::nt_file nf(u8"hello_nt.txt", ::fast_io::open_mode::out);
println(nf, u8"Hello from nt_file!");
::std::size_t const n{42zu};
println(nf, u8"The answer is: ", n);
// NtClose is invoked automatically when `nf` goes out of scope.
}
Wrapping an Existing HANDLE
#include <fast_io.h>
#include <fast_io_device.h>
void inspect(void* nt_handle)
{
using namespace ::fast_io::iomnp;
// Non-owning view. No NtClose on destruction.
::fast_io::nt_io_observer observer{
static_cast<::fast_io::nt_io_observer::native_handle_type>(nt_handle)};
println(observer, u8"Written through an nt_io_observer\n");
println(::fast_io::mnp::handlevw(observer.native_handle()));
}
int main()
{
::fast_io::nt_file nf(u8"inspect.txt", ::fast_io::open_mode::out);
inspect(nf.native_handle());
}
Relative Opens
Because NtCreateFile natively accepts a
RootDirectory handle and a relative ObjectName, the NT layer is
the most natural home for at(dir) on Windows:
#include <fast_io.h>
#include <fast_io_device.h>
int main(int argc, char** argv)
{
using namespace ::fast_io::iomnp;
::fast_io::nt_file dir{::fast_io::mnp::os_c_str(argv[1]),
::fast_io::open_mode::directory};
::fast_io::nt_file nf(at(dir), u8"relative.txt",
::fast_io::open_mode::out);
println(nf, u8"Opened through NtCreateFile with a root directory\n");
}
Why Use nt_file Directly?
Most programs should just use ::fast_io::native_file (which aliases to
nt_file on NT) or ::fast_io::iobuf_file. You reach for
nt_file directly when you need:
- Direct kernel access without going through
kernel32.dll. - Fine-grained control over
NtCreateFileparameters. - Relative opens, which map cleanly onto the native API.
Summary
-
::fast_io::nt_filewraps the NT kernel'sNtCreateFile/NtClosewith RAII. -
::fast_io::nt_io_observeris a non-owning view over an existing NTHANDLE. -
The API surface mirrors
posix_fileandwin32_file: once you know one layer, the others are trivial. -
On modern Windows,
::fast_io::native_fileis an alias fornt_file.