In my last blog post I wrote about mapping stdin into memory. This time we're going to be mapping stdout.
Firstly, we need to get a handle to stdout. That's easy enough with Win32 API GetStdHandle:
HANDLE Stdout = GetStdHandle(STD_OUTPUT_HANDLE);
Alternatively we could directly use NT API NtCurrentTeb, which GetStdHandle uses under the hood, but it's a little more lengthy:
HANDLE Stdout = NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->StdOutputHandle;
Next we need to map our file into memory. Windows (or any other platform) doesn't support write-only pages, so we have to map into read-write pages. Let's try to follow what we did for stdin (CreateFileMapping):
HANDLE FileMapping = CreateFileMapping(
Stdout, // [in] File
NULL, // [in] FileMappingAttributes [optional]
PAGE_READWRITE, // [in] Protect
0, /* one */ // [in] MaximumSizeHigh
4096, /* page */ // [in] MaximumSizeLow
NULL // [in] Name [optional]
);
Unfortunately, we get Access is denied, because stdout is not readable. What can we do about it?.. We can reopen the file with read access!
First, let's get the name of the output file. We can do it with Win32 API GetFinalPathNameByHandleW. We can't get VOLUME_NAME_DOS or VOLUME_NAME_GUID, so let's use VOLUME_NAME_NT:
WCHAR FileName[MAX_PATH];
DWORD ret = GetFinalPathNameByHandleW(Stdout, FileName, MAX_PATH, VOLUME_NAME_NT);
Again we could instead directly call NT API that GetFinalPathNameByHandleW uses under the hood — NtQueryObject:
struct { UNICODE_STRING Name; WCHAR Buffer[MAX_PATH]; } FileNameBuffer;
NtQueryObject(Stdout, ObjectNameInformation, &FileNameBuffer, sizeof(FileNameBuffer), NULL);
As a result we get something like this: \Device\ImDisk1\54345f194c63c344b90cbfcd5944cfd3\run-a6c9a90c30e45e273a5ca250be6b2efb\run\output.fd0138e687.txt
This means that CodeForces uses ImDisk and creates with it ramdisk to store output files to speed up writing. Nice!
Now we need to open this file. Win32 API function for this is CreateFile, which, despite the name, also opens files:
HANDLE File = CreateFileW(
FileName, // [in] lpFileName
GENERIC_READ | GENERIC_WRITE, // [in] dwDesiredAccess
FILE_SHARE_READ | FILE_SHARE_WRITE, // [in] dwShareMode
NULL, // [in] lpSecurityAttribute [optional]
OPEN_EXISTING, // [in] dwCreationDisposition
0, // [in] dwFlagsAndAttributes
NULL // [in] hTemplateFile [optional]
);
But if we do it just like that, we get: The system cannot find the path specified. What we have to do instead is replace "\Device" in the beginning of FileName with "\\?" (in C string you'd use escapes: "\\\\?"). Alternatively we can use the underlying NT API NtOpenFile, which accepts NT object names directly:
HANDLE File;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
InitializeObjectAttributes(&ObjectAttributes, &FileNameBuffer.Name, 0, NULL, NULL);
NtOpenFile(
&File, // [out] FileHandle
FILE_READ_DATA | FILE_WRITE_DATA, // [in] DesiredAccess
&ObjectAttributes, // [in] ObjectAttributes
&IoStatusBlock, // [out] IoStatusBlock
FILE_SHARE_READ | FILE_SHARE_WRITE, // [in] ShareAccess
0 // [in] OpenOptions
);
If we open file without FILE_SHARE_READ | FILE_SHARE_WRITE we get STATUS_SHARING_VIOLATION.
Now that we've opened the file for both reading and writing we can map it and write to it without any issues.
const char* msg = "Hello from memory-mapped stdout\n";
size_t len = strlen(msg);
HANDLE map = CreateFileMapping(File, NULL, PAGE_READWRITE, 0, len, NULL);
void *view = MapViewOfFile(map, FILE_MAP_WRITE, 0, 0, 0);
memcpy(view, msg, len);
Except we get a funny bug from CodeForces runner.
After some digging around, we find that CodeForces runner really doesn't like string "PAGE_READWRITE" present anywhere in the code. So we just substitute this macro by hand with value 4.
A-a-and we finally get...
Hello from memory-mapped stdout
That's it!
Next time we'll use some other NT APIs to not just map statically-sized output file, but also extend it while it's mapped — for this we'll need MEM_RESERVE, which is only present in Windows. We'll also pack it all into a neat class, like we did last time with input. See you there!








