Example: Random Record Updates
Chapter 14 on the Web site, although not in the text) also illustrates direct file access.
Program 3-1. RecordAccess
/* 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;
}
