0% found this document useful (0 votes)
24 views22 pages

02

Chapter 2 discusses the centrality of files and console I/O in Windows operating systems, highlighting core functions like CreateFile, ReadFile, WriteFile, and CloseHandle. It explores various Windows file systems, naming conventions, and the importance of Unicode for internationalization. The chapter emphasizes the need for robust error reporting and modern coding practices while interacting with files and directories.

Uploaded by

Hoang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views22 pages

02

Chapter 2 discusses the centrality of files and console I/O in Windows operating systems, highlighting core functions like CreateFile, ReadFile, WriteFile, and CloseHandle. It explores various Windows file systems, naming conventions, and the importance of Unicode for internationalization. The chapter emphasizes the need for robust error reporting and modern coding practices while interacting with files and directories.

Uploaded by

Hoang
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

Chap 2

1. Centrality of Files and Console I/O

 Core OS features: Files and terminal (console) I/O were among the first services
offered by early PC operating systems (e.g., MS-DOS).
 Why files matter:
o Long-term storage of data and programs.
o Inter-program communication via simple file reads and writes.
o Analogy: Many principles of file systems extend to interprocess and network
communication (e.g., pipes, sockets).
 Fundamental Win32 functions: The four “building blocks” for file I/O introduced in
Chapter 1 are:
1. CreateFile
2. WriteFile
3. ReadFile
4. CloseHandle

2. Windows File Systems (Section 2.1)

Windows exposes a single, uniform file-system API that works across multiple on-disk
formats—though advanced features vary by format.
File
Characteristics Typical Usage
System
• Long file names
• Security (ACLs) support
Default for Windows
• Fault tolerance (journaling)
NTFS system and data
• Compression & encryption
volumes
• Extended attributes
• Supports files > 4 GB via 64-bit offsets
• Legacy from MS-DOS
• No built-in security or journaling
FAT / • Limited to ≤ 4 GB files on FAT32 (though FAT32 itself can Removable media,
FAT32 support much larger volumes) legacy support
• Only format supported on floppy disks; common on
smaller flash-storage devices
• ISO 9660 compliant
CDFS CD-ROMs
• Read-only

1
File
Characteristics Typical Usage
System
• Industry standard
• Used on DVDs, Blu-ray Optical media,
UDF • Windows calls its packet-writing variant “Live File large-media
System” (LFS)—lets you add files and mark deletions interchange
without physically removing data)

The author notes that NTFS is assumed unless otherwise specified, since it’s the only
Windows format supporting security descriptors and most advanced capabilities.

Clarification: On NTFS and FAT32, truly “huge” files (≫ 4 GB) require using the 64-bit file APIs
(CreateFile, GetFileSizeEx, SetFilePointerEx, etc.) because the old 32-bit offsets overflow at
4 GB.

3. File Naming Conventions (Section 2.2)

Windows supports a hierarchical, drive-letter–based namespace plus UNC paths for network
shares:

1. Drive-letter paths:
o Format: C:\path\to\file.txt
o Drives A: and B: typically reserved for floppy disks; C:, D:… for hard disks, optical
drives, USB, etc.
2. UNC (network) paths:
o Format: \\ServerName\ShareName\path\to\file.txt
o Bypasses drive letters to refer directly to remote shares.
3. Separators:
o Primary: Backslash (\).
o Alternative: Forward slash (/) is accepted by low-level APIs (e.g., CreateFileA), but
backslashes are recommended to avoid subtle issues.
4. Character restrictions:
o Cannot include ASCII 0x01–0x1F control codes or any of:

CopyEdit

" < > : | ? * / \

2
o Spaces are allowed—but on a command line, paths containing spaces must be
quoted ("My Docs\file.txt").
5. Case handling:
o Insensitive but case-retaining: You can open MyFile.TXT, myfile.txt, or MYFILE.txt
interchangeably; the name is stored/displayed as originally created.
6. Length limits:
o Component (each directory or file name): up to 255 characters.
o Full path: traditionally limited to MAX_PATH (260 characters).
o Long paths: Prepend the “extended-length” prefix \\?\ (e.g., \\?\C:\very\long\…)
to bypass MAX_PATH and permit up to ~32 000 characters.
7. Extensions:
o Separated by a period (.). The convention is for 2–4 character extensions to
indicate file type (e.g., .exe, .c, .txt), but Windows imposes no hard limit on
number or length of extensions.
8. Special directories:
o . refers to the current directory.
o .. refers to the parent directory.

4. Unicode and Internationalization

 Why Unicode? Windows originally used ANSI code pages, which vary by locale and
cannot represent global character sets reliably.
 Wide-char APIs: Always prefer the W-suffixed functions (e.g., CreateFileW) with
wchar_t* (UTF-16) strings to handle filenames in any language.
 Example correction & modernization: Below is a C11-style snippet for opening (or
creating) a UTF-8–encoded filename in a Unicode-aware manner. It shows how to
convert a narrow (UTF-8) char* path to a wide (wchar_t*) string before calling
CreateFileW.
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>

// Converts a UTF-8 string to UTF-16 and opens the file.


HANDLE OpenFileUtf8(const char *utf8Path)
{
// Determine required buffer size (in wide chars), including null terminator.
int size_needed = MultiByteToWideChar(
CP_UTF8, 0,
3
utf8Path, -1,
NULL, 0);
if (size_needed == 0) {
fprintf(stderr, "Error converting path to UTF-16.\n");
return INVALID_HANDLE_VALUE;
}

// Allocate and convert.


wchar_t *widePath = malloc(size_needed * sizeof(wchar_t));
if (!widePath) {
perror("malloc");
return INVALID_HANDLE_VALUE;
}

MultiByteToWideChar(
CP_UTF8, 0,
utf8Path, -1,
widePath, size_needed);

// Optionally prepend \\?\ to support long paths.


wchar_t fullPath[MAX_PATH + 5] = L"\\\\?\\";
wcsncat(fullPath, widePath, MAX_PATH);

free(widePath);

// Create or open the file for read/write.


HANDLE hFile = CreateFileW(
fullPath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);

if (hFile == INVALID_HANDLE_VALUE) {
fprintf(stderr, "CreateFileW failed: %lu\n", GetLastError());
}
return hFile;
}

4
The above snippet corrects for OCR-induced typos, uses C11 conventions (nullptr isn’t in C, so
we use NULL), handles errors, and meets modern Windows and C standards.

In essence, this chapter lays the groundwork for interacting with files and the console on
Windows by:

1. Introducing the unified file I/O API (CreateFile, ReadFile, etc.).


2. Surveying the variety of underlying file-system formats (NTFS, FAT, CDFS, UDF).
3. Explaining Windows-specific path and naming rules, including legacy limits and
extensions for long or Unicode paths.

With these fundamentals clear—and by favoring Unicode-aware APIs—you’re equipped to


build robust, internationalized Windows applications that deal with files and directories in a
predictable, cross-locale way.

--------------------------------

in §2.3, with corrections and modern clarifications added where appropriate.

1. CreateFile — Opening and Creating Files

Prototype (Unicode‐aware):

CopyEdit

HANDLE CreateFileW(

LPCWSTR lpFileName,

DWORD dwDesiredAccess,

DWORD dwShareMode,

LPSECURITY_ATTRIBUTES lpSecurityAttributes,

DWORD dwCreationDisposition,

5
DWORD dwFlagsAndAttributes,

HANDLE hTemplateFile

);
Parameter Meaning & Tips
Path to file (or pipe, device, etc.). Use \\?\ prefix for very long paths (up to
lpFileName
~32 000 chars) on NTFS.
Bit-mask of access rights: GENERIC_READ, GENERIC_WRITE (or both). No
dwDesiredAccess
simple READ/WRITE exists.
dwShareMode Sharing flags:

 0: exclusive (no sharing)


 FILE_SHARE_READ
 FILE_SHARE_WRITE
Combine with | for read/write sharing. |
| lpSecurityAttributes | Pointer to SECURITY_ATTRIBUTES. Pass NULL (default) until you
need custom ACLs. |
| dwCreationDisposition | What to do if file exists or not:
 CREATE_NEW (fail if exists)
 CREATE_ALWAYS (overwrite)
 OPEN_EXISTING (fail if missing)
 OPEN_ALWAYS (create if missing)
 TRUNCATE_EXISTING (zero-length; must have write access) |
| dwFlagsAndAttributes | File attributes and hints, OR’d together:
 Attributes:
• FILE_ATTRIBUTE_NORMAL (no other attrs)
• FILE_ATTRIBUTE_READONLY
 Flags (hints):
• FILE_FLAG_SEQUENTIAL_SCAN / FILE_FLAG_RANDOM_ACCESS
• FILE_FLAG_OVERLAPPED (async I/O)
• FILE_FLAG_WRITE_THROUGH / FILE_FLAG_NO_BUFFERING
• FILE_FLAG_DELETE_ON_CLOSE |
| hTemplateFile | Handle to an existing file whose attributes to copy to a new file.
Otherwise NULL. |

The author walks through each field carefully to demystify what can feel like an
overwhelming list of options. In practice, you’ll almost always pass NULL for
lpSecurityAttributes and hTemplateFile, combine GENERIC_READ|GENERIC_WRITE, choose

6
the right creation disposition, and OR in at most one or two flags such as
FILE_FLAG_SEQUENTIAL_SCAN for performance hints.

Example (modern C11):

CopyEdit
// Open (or create) example.txt for read/write, shared read, with a sequential-scan hint
HANDLE hFile = CreateFileW(
L"example.txt", // Unicode literal
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL, // default security
OPEN_ALWAYS, // create if missing
FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_SEQUENTIAL_SCAN, // caching hint
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
// error handling...
}

2. CloseHandle — Releasing Resources

CopyEdit

BOOL CloseHandle(HANDLE hObject);

 Closes almost all kernel handles (files, events, threads, etc.).


 Decrements the object’s handle count; some objects auto-delete when count reaches
zero (e.g., FILE_FLAG_DELETE_ON_CLOSE).
 Closing a bad or already-closed handle under a debugger raises an exception—so
track your handles carefully.
 Never close the standard I/O handles (GetStdHandle) yourself; Windows manages
those.

Clarification: On program exit Windows will close remaining handles, but it’s best practice to
close them explicitly to avoid resource leaks in long-running processes or libraries.
7
3. ReadFile — Synchronous Reads

CopyEdit
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped // pass NULL for blocking I/O
);

 Starts at the handle’s current file pointer and advances it by the number of bytes
actually transferred.
 Returns TRUE even if zero bytes are read (e.g., end-of-file).
 On failure, returns FALSE; call GetLastError() for details.
 When not using FILE_FLAG_OVERLAPPED, always pass lpOverlapped = NULL.

4. WriteFile — Synchronous Writes

CopyEdit
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped // NULL for blocking I/O
);

 Behaves like ReadFile, advancing the file pointer.


 Note: Unless you specified FILE_FLAG_WRITE_THROUGH, data may be cached and not
yet durable on disk when WriteFile returns. Use the flush functions (FlushFileBuffers)
or flags for stricter guarantees.

8
5. Comparison with UNIX & C Runtime
UNIX / C Runtime (<unistd.h>,
Aspect Win32 API
<stdio.h>)
CreateFile w/ separate access, share, open(path, oflag, mode) packs flags
Open/create
create, attribute args into one oflag
Explicit dwShareMode; you can forbid Implicitly shareable; no share-mode
Sharing
sharing control
File handle
HANDLE (opaque pointer) int file descriptor
type
Close CloseHandle(handle) close(fd)
Optional—Win32 I/O is unbuffered by <stdio> streams provide buffering
Buffered I/O
default (fopen, fread)
Rich Windows ACL model via UNIX permissions via mode_t
Security/ACLs
SECURITY_ATTRIBUTES (owner/group/other)

The author points out these contrasts to highlight the flexibility (and complexity) of Win32’s
sharing model and attribute flags versus the more streamlined but less granular UNIX
interface.

Bottom line:

 Master CreateFile (and its generic-text mapping to CreateFileA/CreateFileW) to open or


create any file or device on Windows.
 Always close your handles.
 Use separate calls to ReadFile and WriteFile for blocking I/O, or set up overlapped
(async) I/O later.
 Remember that Windows makes a clear distinction between file attributes (on‐disk
metadata) and flags (hints for caching and behavior at handle‐open time).
 Compared to UNIX, Windows gives you much finer control over sharing, buffering, and
security—at the cost of a more verbose API.

----------------------------

9
1. Character Encodings on Windows (Section 2.4)

 Two “native” character sets


o ANSI/ASCII: 8-bit char (alias CHAR), historically miscalled “ANSI” by Microsoft.
o Unicode/UTF-16: 16-bit wchar_t (alias WCHAR), capable of representing virtually
every script.
 Generic-text mapping
o The author shows how to write one code-base that can compile for either
“ANSI” or Unicode by using:

CopyEdit

#define UNICODE /* select UTF-16 Windows APIs */

#define _UNICODE /* select wide-char C runtime */

#include <windows.h>

#include <tchar.h>

o Then use types/macros in <tchar.h>:


| Generic | ANSI expands to | Unicode expands to |
|:-------:|:--------------:|:------------------:|
| TCHAR | char | wchar_t |
| LPSTR | char* | — |
| LPTSTR| char* | wchar_t* |
| _T("…") / TEXT("…") | "…” | L"…”" |
 Why generic at all?
o At the time, many projects still emitted 8-bit output, but needed an easy
migration path.
o Today’s best practice is to target Unicode only—no need for the extra clutter of
_T() macros. Use the wide-char APIs (CreateFileW, std::wstring, wchar_t, or even
char8_t/char16_t in standard C++20).

2. Unicode Strategy Choices (Section 2.5)

The author lists four approaches; here’s how they look in 2025:

10
1. 8-bit only
o Legacy code; OK only for pure-ASCII domains.
2. Generic (“TCHAR”)
o Adds build-time flexibility, but leads to hard-to-read code.
3. Unicode-only
o Modern recommended strategy. Drop all ANSI APIs.
4. Dual at run-time
o Rarely needed now; better to offer a single, well-tested path.

3. Robust Error Reporting (Section 2.6)

What the author proposes

 A ReportError function that:


1. Calls GetLastError() to fetch the Windows error code.
2. Uses FormatMessage() to turn that code into a localized message.
3. Prints both your own message and the system message via _ftprintf.
4. Frees the buffer with LocalFree().
5. Optionally calls ExitProcess() or returns.

Corrections & modern C11-style rewrite

 Fix typos in the original (duplicated #define UNICODE, missing commas).


 Prefer explicit wide-char APIs instead of generic macros.
 Use standard headers and consistent naming.

CopyEdit
#include <windows.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>

/**
* ReportError:
* - userMsg: Your descriptive text.
* - exitCode: If > 0, calls ExitProcess(exitCode); otherwise returns.
* - showSys: If true, prints the system error message.

11
*/
void ReportError(const wchar_t *userMsg, UINT exitCode, BOOL showSys)
{
DWORD err = GetLastError();
fputws(userMsg, stderr);
fputwc(L'\n', stderr);

if (showSys) {
wchar_t *sysMsg = NULL;
DWORD len = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(wchar_t*)&sysMsg,
0,
NULL
);
if (len > 0 && sysMsg) {
fputws(sysMsg, stderr);
} else {
fwprintf(stderr, L"Unknown error code: %lu\n", err);
}
if (sysMsg) LocalFree(sysMsg);
}

if (exitCode > 0) {
ExitProcess(exitCode);
}
}

 Notes:
o We use FormatMessageW and wide-char I/O (fputws, fputwc), matching a
Unicode-only policy.
o C11’s <wchar.h> and <stdio.h> suffice—no need for <tchar.h> or generic macros.

4. Standard I/O Devices (Section 2.7)


12
 Windows vs. UNIX
o UNIX: file descriptors 0/1/2 for stdin/stdout/stderr.
o Windows: opaque HANDLEs obtained by

CopyEdit

HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE);

o You can also redirect by calling SetStdHandle() or opening “CONIN$” /


“CONOUT$” if you need an unredirected console.
 Key takeaway
o Never CloseHandle() a standard handle—Windows will stop sending data to it.
o Use these in your console utilities whenever you need raw handle-level I/O (e.g.,
overlapped reads, pipes, or console-mode manipulations).

In summary

 The author stresses the importance of writing flexible code via generic-text mapping,
but in modern practice you can simplify by targeting Unicode-only.
 A robust ReportError helper—wrapping GetLastError, FormatMessage, and cleanup—
is indispensable for clear diagnostics.
 Standard device handles (GetStdHandle / SetStdHandle) give you low-level control of
stdin/stdout/stderr, analogous to UNIX’s file descriptors, but using Windows-style
HANDLEs.

By adopting Unicode-only APIs, cleaning up the old macros, and following the corrected
ReportError pattern above, you’ll have more readable, maintainable, and future-proof
Windows code.

13
Sections 2.8–2.10, blending the author’s intent with modern corrections and
recommendations.

1. Copying Multiple Files to Standard Output (Section 2.8)

What the author shows

 A minimal cat clone (Program 2-2) that:


1. Parses an optional “-s” flag (suppress missing-file errors) via a helper Options()
(akin to UNIX getopt).
2. If no filenames are given, reads from standard input
(GetStdHandle(STD_INPUT_HANDLE)).
3. Otherwise, for each file:
 Opens with CreateFile(…, OPEN_EXISTING).
 On error (file not found), either reports via ReportError or skips if “-s” was
set.
 Calls a helper CatFile(hIn, hStdOut) that loops ReadFile→WriteFile.
4. Cleans up handles.

Corrections & Modern Tips

 Unicode-only: Drop TCHAR indirection—use wchar_t/CreateFileW and std::wstring (or


UTF-8 + CreateFileA with @-prefixed paths).
 Error handling: Instead of a home-grown Options, consider CommandLineToArgvW or
a small argument parser.
 Buffer size: 0x200 (512 bytes) works, but larger (e.g. 64 KB) usually gives much higher
throughput.
 Simplified rewrite (Unicode-only, C11 style):

CopyEdit
#include <windows.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>

#define BUF_SIZE (64*1024)

14
static void CatFile(HANDLE in, HANDLE out) {
DWORD read, written;
BYTE *buf = malloc(BUF_SIZE);
while (ReadFile(in, buf, BUF_SIZE, &read, NULL) && read > 0) {
WriteFile(out, buf, read, &written, NULL);
}
free(buf);
}

int wmain(int argc, wchar_t *argv[]) {


BOOL suppressError = FALSE;
int firstFile = 1;
if (argc > 1 && wcscmp(argv[1], L"-s") == 0) {
suppressError = TRUE;
firstFile = 2;
}

HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);


HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

if (firstFile >= argc) {


CatFile(hStdIn, hStdOut);
} else {
for (int i = firstFile; i < argc; i++) {
HANDLE h = CreateFileW(argv[i], GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
if (!suppressError)
fwprintf(stderr, L"Error opening \"%s\"\n", argv[i]);
} else {
CatFile(h, hStdOut);
CloseHandle(h);
}
}
}
return 0;
}

2. Simple File Encryption (Section 2.9)


15
Author’s approach

 A modified Caesar cipher that:


1. Takes three arguments: a numeric shift and two filenames.
2. Opens input/output files with CreateFile.
3. Reads blocks into a buffer, adds shift modulo 256 to each byte, writes out.
4. Reports errors via ReportError.

Corrections & Improvements

 Typo fixes: ensure buffer indexing and modulo logic are correct.
 Performance: add FILE_FLAG_SEQUENTIAL_SCAN for large files.
 Modern C: use UINT8_T or BYTE; check ReadFile/WriteFile return values explicitly.

#include <windows.h>
#include <stdio.h>
#include <stdint.h>

#define BUF_SIZE (64*1024)

BOOL CaesarCipherFile(const wchar_t *inPath,


const wchar_t *outPath,
uint8_t shift)
{
HANDLE hIn = CreateFileW(inPath, GENERIC_READ, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hIn == INVALID_HANDLE_VALUE) return FALSE;

HANDLE hOut = CreateFileW(outPath, GENERIC_WRITE, 0, NULL,


CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hOut == INVALID_HANDLE_VALUE) {
CloseHandle(hIn);
return FALSE;
}

BYTE *buf = malloc(BUF_SIZE);


16
DWORD read, written;
BOOL ok = TRUE;

while (ok && ReadFile(hIn, buf, BUF_SIZE, &read, NULL) && read > 0) {
for (DWORD i = 0; i < read; i++)
buf[i] = (uint8_t)(buf[i] + shift);
ok = WriteFile(hOut, buf, read, &written, NULL) && (written == read);
}

free(buf);
CloseHandle(hIn);
CloseHandle(hOut);
return ok;
}

int wmain(int argc, wchar_t *argv[]) {


if (argc != 4) {
fwprintf(stderr, L"Usage: %s <shift> <infile> <outfile>\n", argv[0]);
return 1;
}
uint8_t shift = (uint8_t)_wtoi(argv[1]);
if (!CaesarCipherFile(argv[2], argv[3], shift)) {
fwprintf(stderr, L"Encryption failed.\n");
return 2;
}
return 0;
}

3. File and Directory Management (Section 2.10)

Core APIs

 Delete: DeleteFileW(lpFileName)
 Copy: CopyFileW(src, dst, failIfExists) (also preserves timestamps/attributes)
 Hard link: CreateHardLinkW(newName, existingName, NULL)
 Symbolic link (Vista+): CreateSymbolicLinkW(symlinkName, targetName, flags)
 Rename/Move:
o MoveFileW(old, new) (fails if new exists)

17
oMoveFileExW(old, new, flags) (e.g. MOVEFILE_REPLACE_EXISTING,
MOVEFILE_WRITE_THROUGH, or MOVEFILE_DELAY_UNTIL_REBOOT)
 Directories:
o Create: CreateDirectoryW(path, NULL)
o Remove: RemoveDirectoryW(path)
o Current directory:
 SetCurrentDirectoryW(path)
 GetCurrentDirectoryW(bufSize, buf) → if return > bufSize, realloc and
retry.

Modern notes

 When writing portable C++, consider std::filesystem (C++17) rather than raw Win32
calls.
 Always use the wide-char versions (ending in “W”) for true Unicode support.
 Check return values carefully and, if needed, get extended error info with
GetLastError().

Bottom line: these examples teach you how to

1. Stream data via ReadFile/WriteFile,


2. Wrap errors in a reusable helper,
3. Manipulate files/directories with the full suite of Win32 APIs,
all while offering an easy “generic text” layer that—today—can be simplified by
targeting Unicode directly.

--------------------------------------

18
1. Console I/O Fundamentals (Section 2.11)

Author’s approach

 Low-level I/O: you can use ReadFile/WriteFile on CONIN$/CONOUT$, but


 Specialized APIs: ReadConsole/WriteConsole know about character encoding (generic
TCHAR) and respect console modes set by:

CopyEdit

BOOL SetConsoleMode(HANDLE hConsole, DWORD dwMode);

Common flags (all on by default):

o ENABLE_LINE_INPUT (return on Enter)


o ENABLE_ECHO_INPUT (echo keystrokes)
o ENABLE_PROCESSED_INPUT (handle backspace, CR, LF)
o ENABLE_PROCESSED_OUTPUT (translate control chars)
o ENABLE_WRAP_AT_EOL_OUTPUT (auto-wrap)
 Attaching/detaching consoles:
o AllocConsole() creates a new console if none exists (e.g. in a GUI app).
o FreeConsole() detaches the process from its console.

Modern recommendations

 Unicode-only: call ReadConsoleW, WriteConsoleW, and work in wchar_t.


 Avoid TCHAR/generic: simplifies code by removing _T() macros.
 Example snippet:

CopyEdit

HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);

DWORD mode;

GetConsoleMode(hIn, &mode);

SetConsoleMode(hIn, mode & ~(ENABLE_ECHO_INPUT)); // turn off echo

19
wchar_t buf[128];

DWORD read;

ReadConsoleW(hIn, buf, _countof(buf)-1, &read, NULL);

buf[read] = L'\0'; // trim trailing CR/LF

2. Printing & Prompting Utilities (Section 2.12)

What the author provides

 PrintStrings: variadic function that writes one or more LPCTSTR messages to any
HANDLE.
 PrintMsg: convenience wrapper for a single string.
 ConsolePrompt:
1. Opens "CONIN$" and "CONOUT$" handles.
2. Sets input/output modes (e.g. echo on/off).
3. Displays a prompt, reads up to maxChar characters.
4. Replaces the trailing CR/LF with '\0'.
5. Cleans up and returns TRUE/FALSE.

Cleaned-up Unicode-only version

#include <windows.h>
#include <wchar.h>
#include <stdarg.h>
#include <stdio.h>

BOOL PrintMsg(HANDLE hOut, const wchar_t *fmt, ...) {


va_list args;
va_start(args, fmt);
vwprintf(fmt, args);
va_end(args);
return TRUE;
}

20
BOOL ConsolePrompt(const wchar_t *prompt, wchar_t *response, DWORD maxChars, BOOL
echo) {
HANDLE hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
HANDLE hOut = CreateFileW(L"CONOUT$", GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
if (hIn == INVALID_HANDLE_VALUE || hOut == INVALID_HANDLE_VALUE) return FALSE;

DWORD inMode, outMode;


GetConsoleMode(hIn, &inMode);
GetConsoleMode(hOut, &outMode);
SetConsoleMode(hIn, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | (echo ?
ENABLE_ECHO_INPUT : 0));
SetConsoleMode(hOut, ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_PROCESSED_OUTPUT);

PrintMsg(hOut, L"%s", prompt);

DWORD read;
ReadConsoleW(hIn, response, maxChars, &read, NULL);
// Trim CR/LF
if (read >= 2) response[read - 2] = L'\0';
else response[read] = L'\0';

CloseHandle(hIn);
CloseHandle(hOut);
return TRUE;
}

3. Printing the Current Directory (Section 2.13)

Author’s example

 Uses GetCurrentDirectory to retrieve the process’s working directory into a TCHAR


buffer, checks for errors (zero return or insufficient buffer), then prints via PrintMsg.

Modern Unicode-only version

21
CopyEdit
#include <windows.h>
#include <wchar.h>
#include <stdio.h>

int wmain(void) {
DWORD needed = GetCurrentDirectoryW(0, NULL);
if (needed == 0) {
fwprintf(stderr, L"Error retrieving directory: %lu\n", GetLastError());
return 1;
}
wchar_t *buf = malloc(needed * sizeof(wchar_t));
if (!GetCurrentDirectoryW(needed, buf) || wcslen(buf) + 1 > needed) {
fwprintf(stderr, L"Directory path too long.\n");
free(buf);
return 2;
}
wprintf(L"%s\n", buf);
free(buf);
return 0;
}

4. Key Takeaways (Section 2.14)

1. Console I/O: prefer ReadConsoleW/WriteConsoleW for character-level I/O and


console-mode features.
2. Prompting & Printing: wrap common patterns into utilities (ConsolePrompt, formatted
print).
3. Directory queries: use Windows’s length-returning pattern to allocate buffers safely.
4. Generic vs. Unicode-only: while the author showed how to write either ANSI or
Unicode builds, today it’s simpler and more robust to target Unicode directly and drop
the TCHAR indirection altogether.

By modernizing these examples to Unicode-only and cleaning up buffer logic, you get clearer,
safer, and more maintainable console-based Windows utilities.

22

You might also like