Chapter 14 on the Web site, although not in the text) also illustrates direct file access.
/* Chapter 3. RecordAccess. */ /* Usage: RecordAccess FileName [nrec] If nrec is omitted, FileName must already exist. If nrec is supplied, create FileName (destroying an existing file). If the number of records is large, a sparse file is recommended. */ /* This program illustrates: 1. Random file access. 2. LARGE_INTEGER arithmetic and using the 64-bit file positions. 3. Record update in place. 4. File initialization to 0 (requires an NTFS file system). */ #include "EvryThng.h" #define STRING_SIZE 256 typedef struct _RECORD { /* File record structure */ DWORD ReferenceCount; /* 0 means an empty record. */ SYSTEMTIME RecordCreationTime; SYSTEMTIME RecordLastReferenceTime; SYSTEMTIME RecordUpdateTime; TCHAR DataString[STRING_SIZE]; } RECORD; typedef struct _HEADER { /* File header descriptor */ DWORD NumRecords; DWORD NumNonEmptyRecords; } HEADER; int _tmain (int argc, LPTSTR argv []) { HANDLE hFile; LARGE_INTEGER CurPtr; DWORD FPos, OpenOption, nXfer, RecNo; RECORD Record; TCHAR String[STRING_SIZE], Command, Extra; OVERLAPPED ov = {0, 0, 0, 0, NULL}, ovZero = {0, 0, 0, 0, NULL}; HEADER Header = {0, 0}; SYSTEMTIME CurrentTime; BOOLEAN HeaderChange, RecordChange; OpenOption = (argc == 2) ? OPEN_EXISTING : CREATE_ALWAYS; hFile = CreateFile (argv [1], GENERIC_READ | GENERIC_WRITE, 0, NULL, OpenOption, FILE_ATTRIBUTE_NORMAL, NULL); if (argc >= 3) { /* Write the header and presize the new file) */ Header.NumRecords = atoi(argv[2]); WriteFile(hFile, &Header, sizeof (Header), &nXfer, &ovZero); CurPtr.QuadPart = sizeof(RECORD)*atoi(argv[2])+sizeof(HEADER); FPos = SetFilePointer (hFile, CurPtr.LowPart, &CurPtr.HighPart, FILE_BEGIN); if (FPos == 0xFFFFFFFF && GetLastError () != NO_ERROR) ReportError (_T ("Set Pointer error."), 4, TRUE); SetEndOfFile(hFile); } /* Read file header: find number of records & nonempty records. */ ReadFile(hFile, &Header, sizeof (HEADER), &nXfer, &ovZero); /* Prompt the user to read or write a numbered record. */ while (TRUE) { HeaderChange = FALSE; RecordChange = FALSE; _tprintf (_T("Enter r(ead)/w(rite)/d(elete)/q Record#\n")); _tscanf (_T ("%c" "%d" "%c"), &Command, &RecNo, &Extra ); if (Command == 'q') break; CurPtr.QuadPart = RecNo * sizeof(RECORD) + sizeof(HEADER); ov.Offset = CurPtr.LowPart; ov.OffsetHigh = CurPtr.HighPart; ReadFile (hFile, &Record, sizeof (RECORD), &nXfer, &ov); GetSystemTime (&CurrentTime); /* To update record time fields */ Record.RecordLastRefernceTime = CurrentTime; if (Command == 'r' || Command == 'd') { /* Report contents. */ if (Record.ReferenceCount == 0) { _tprintf (_T("Record Number %d is empty.\n"), RecNo); continue; } else { _tprintf (_T("Record Number %d. Reference Count: %d \n"), RecNo, Record.ReferenceCount); _tprintf (_T("Data: %s\n"), Record.DataString); /* Exercise: Display times. See next example. */ RecordChange = TRUE; } if (Command == 'd') { /* Delete the record. */ Record.ReferenceCount = 0; Header.NumNonEmptyRecords--; HeaderChange = TRUE; RecordChange = TRUE; } } else if (Command == 'w') { /* Write the record. First time? */ _tprintf (_T("Enter new data string for the record.\n")); _getts (String); if (Record.ReferenceCount == 0) { Record.RecordCreationTime = CurrentTime; Header.NumNonEmptyRecords++; HeaderChange = TRUE; } Record.RecordUpdateTime = CurrentTime; Record.ReferenceCount++; _tcsncpy (Record.DataString, String, STRING_SIZE-1); RecordChange = TRUE; } else { _tprintf (_T("Command must be r, w, or d. Try again.\n")); } /* Update record in place if any contents have changed. */ if (RecordChange) WriteFile (hFile, &Record, sizeof (RECORD), &nXfer, &ov); /* Update the number of nonempty records if required. */ if (HeaderChange) WriteFile(hFile, &Header, sizeof (Header), &nXfer, &ovZero); } _tprintf (_T("Computed number of nonempty records is: %d\n"), Header.NumNonEmptyRecords); CloseHandle (hFile); return 0; }