Windows NT Systems
Programming
[ Home
| Syllabus |Course Notes]
Drives and Directories
Drives: One big difference from UNIX
- There are only 26 letters.
Bummer (vestiges of CPM)
- Unix just mounts drives as a
root in the big file tree. Elegant
- NT networks use
Domain/Machine/Drive naming
- UNIX uses the big Tree (i.e.
it allows drives in other machines to be mounted just
like drives on the local machine - using the NFS file
system).
All said, NT is less location
transparent than UNIX. If files migrate from one machine to
another, or one drive to another, you have to use a different
name to find them.
Handling Drives
APIs to
- get size and free space
- get type
- get file system
- other information
- are file names case
sensitive
- UNICODE supported
WNET Functions
Network/Domain/Machine/Drive tree
- Network, Domain and Machines
are called Containers
- Drives and Printers are called
Objects
- Enumerate things in a
container
- Connect to a drive
FOR THE TIME BEING
(MAYBE FOREVER) WE DON"T NEED TO KNOW ABOUT THIS VERY
MUCH.
Directories
- No Symbolic Links
- Asynchronously Detect changes
to files and directories
- CreateDirectory/RemoveDirectory
- Get/SetCurrentDirectory
- SearchPath
Enumerating a Directory
FindFirstFile
- Specific/Wil Card
- Path or Current Directory
FindNextFile
- Finds next given handle to
last one found
FindClose
Teaching Project: Version 1
(click
to access version 1 directory)
- Message Object
- Message Folder Object
- List Object
- Responsibility for persistent
storage of object split
- Message Folder is a directory
- Messages are files in the
message folder directory
- Special file
("messages") keeps the topics and file
names of all messages in the folder
Message.h
//******************************************************************************
// Message Object: messages consist of a topic and message text
// both of which are bounded character strings.
// This is a simple, but limited and possibly inefficient solution
// ****** CLASS INVARIANTS
// Thus all messages are subject to the size constraints (SIZE_TOPIC and SIZE_TEXT)
// and thus will not be listed as a PRECONDITION.
// The current implementation will only store up to this maximum size.
// ******* PERSISTANCE OF OBJECTS:
// Interesting feature is the ability of the message to read/write itself
// to an open file - this is reminiscent of the document archiving done
// in MFC's CDocument class.
// This feature supports persistence of objects where the place for storage is controlled
// by the application by the details of storage is arranged by the object itself
// Thus the application can decide to store all objects in one file or in many
// The non-default constructor will initialize a message from an open file.
// AUTHOR: Chris Wild
// DATE: July 25, 1997
//***************************************************************
#ifndef MESSAGE_H // to keep from doubly defining this header file
#define MESSAGE_H
#include <windows.h>
const int SIZE_TOPIC = 50; // maximum size for topic field
const int SIZE_TEXT = 300; // maximum size for message text field
class CMessage {
public:
//************** constructors and desctructors
// Default constructor builds an empty message
CMessage();
// This constructor initializes messages from file
// fileHandle: is handle to already open message file
CMessage(HANDLE fileHandle);
// Copy constructor
CMessage(const CMessage& Msg);
~CMessage();
//************** Operations
// Sets the message topic
//POST: Topic of this message is set to string
void SetTopic(const char * );
// Gets the message topic
// PRE: thisTopic is big enough (maximum SIZE_TOPIC+1 characters)
// POST: returns topic of this message
void GetTopic(char * thisTopic) const;
// Sets text of message
void SetText(const char *);
// returns text of message
// PRE: thisMessage is big enough (maximum SIZE_TEXT+1 characters)
void GetText(char * thisMessage) const;
// Read/Write message to already open file
bool ReadMsg(HANDLE fileHandle);
bool WriteMsg(HANDLE fileHandle);
private:
char topic[SIZE_TOPIC+1]; // fixed string buffer for convenience
char text[SIZE_TEXT+1];
};
#endif
MsgFolder.h
//*******************************************************
// MessageFolder Object: Keeps a folder of CMessage objects
// Using the three constructors, folders can be stored either
// 1) default folder: "c:\MsgFldr
// 2) user chosen folder name in current directory
// 3) User chosen folder name in given path
// AUTHOR: Chris Wild
// DATE: July 25, 1997
//*******************************************************
#ifndef MESSAGEFOLDER_H
#define MESSAGEFOLDER_H
#include "Message.h"
#include "ListMsgs.h"
#include <windows.h>
typedef int msgID;
//typedef int bool;
const int NULL_ID = 0;
class CMsgFolder
{
public:
//************* constructors and destructors
CMsgFolder(); // default
// opens (or creates a folder called "folderName" in current directory
CMsgFolder(const char* folderName);
// opens (or creates) a folder called "folderName in "path"
CMsgFolder(const char* path, const char* folderName);
// Writes out open messages and folder information
~CMsgFolder();
// Puts a message in the folder
bool PostMsg(const CMessage& msg);
// PRE: message with "messageID" exists
// gets message with ID "messageId"
CMessage GetMsg(msgID messageId) const;
// PRE: message with "messageID" exists
// topic points to a string big enough to hold topic string
// POST: if exists, returns true and topic of this message
// else returns false
bool GetMsgTopic(msgID messageId, char* topic) const;
// PRE: message with "messageID" exists
// POST: deletes this message (renumbering messages)
bool DeleteMsg(msgID messageID);
// POST: returns the number of messages in this folder
int NumMsgs() const;
// POST: Closes this folder making it undefined
// returns false if problem closing folder else true
bool CloseMsgFolder();
// POST: Open folder with "folderName"
// returns true is opened successfully else false
bool OpenMsgFolder(const char* folderName);
private:
char* folderName;
HANDLE messages; // handle to open message folder
CListMsgs msgList; // list of available messages
int nextMsgNum; // unique number for next message
bool verifyFolder(const char*); // opens(creates) message folder
};
#endif
ListMsgs.h
//***************************************
// Implements a list of message header objects which can manipulated by position
// Items can be added to a position (moving items to make space - if necessary)
// Items can be deleted from a specified position (moving items to fill the emptied
// space.
// Also you can retrieve any item by its position.
// Based on object described in Carrano Walls and Mirrors book.
// AUTHOR: Chris Wild
// DATE: July 25, 1997
//****************************************
#ifndef LIST_MESSAGE_H
#define LIST_MESSAGE_H
#include "message.h"
typedef struct {
char topic[SIZE_TOPIC+1]; // topic of this message
char fileName[10]; // file name where message is kept
} Item ;
struct ListNode;
typedef ListNode* ListPtr;
struct ListNode {
Item item;
ListPtr next;
};
class CListMsgs {
public:
// Default constructor builds an empty message
CListMsgs();
// This constructor reads message list from file
CListMsgs(HANDLE listHandle);
~CListMsgs();
// Adds topic to list in "position"
// moves other messages down to make room
bool AddItem(int position, Item thisItem);
bool DeleteItem(int position);
bool GetItem(int position, Item& thisItem) const;
bool ReadListMsgs(HANDLE listHandle);
bool WriteListMsgs(HANDLE listHandle) const;
int length() const;
private:
ListPtr PtrTo(int position) const;
int size;
ListPtr head;
};
#endif
Messages.cpp
//...
CMessage::CMessage()
{
ZeroMemory(topic,sizeof(topic)); // I like clean memoery
ZeroMemory(text, sizeof(text));
}
// Initializes message from already open file
CMessage::CMessage(HANDLE fileHandle)
{
DWORD nBytes;
ReadFile(fileHandle,topic,SIZE_TOPIC+1,&nBytes,0);
ReadFile(fileHandle,text,SIZE_TEXT+1,&nBytes,0);
}
CMessage::CMessage(const CMessage& msg)
{
strncpy(topic,msg.topic,SIZE_TOPIC+1);
strncpy(text,msg.text,SIZE_TEXT+1);
}
CMessage::~CMessage()
{ // nothing to do
}
// . . .
bool CMessage::ReadMsg(HANDLE fileHandle)
{
DWORD nBytes;
if(!ReadFile(fileHandle,topic,SIZE_TOPIC+1,&nBytes,0))
return FALSE;
if(!ReadFile(fileHandle,text,SIZE_TEXT+1,&nBytes,0))
return FALSE;
return TRUE;
}
bool CMessage::WriteMsg(HANDLE fileHandle) const
{
DWORD nBytes;
if(!WriteFile(fileHandle, topic, SIZE_TOPIC+1, &nBytes,NULL)) {
cerr << "Error: " << GetLastError() <<
" when writing file:" << endl;
return FALSE;
}
if(!WriteFile(fileHandle, text, SIZE_TEXT+1, &nBytes,NULL)) {
cerr << "Error: " << GetLastError() <<
" when writing file:" << endl;
return FALSE;
}
return TRUE;
}
MsgFolder.cpp
// . . .
const int MAGIC_COOKIE=1234567;
CMsgFolder::CMsgFolder():messages(NULL), nextMsgNum(0)
{
}
// opens (or creates a folder called "folderName" in current directory
CMsgFolder::CMsgFolder(const char* folder):messages(NULL), nextMsgNum(0)
{
char currentDir[MAX_PATH]; // from stdio.h
int currentLen = GetCurrentDirectory(MAX_PATH, currentDir);
folderName = new char[currentLen + strlen(folder)+2];
strncpy(folderName,currentDir,strlen(currentDir));
strncpy(folderName+strlen(currentDir) ,"\\" ,1);
strncpy(folderName+strlen(currentDir)+1, folder, strlen(folder));
strncpy(folderName+strlen(currentDir)+1+strlen(folder), "\0",1);
verifyFolder(folderName);
}
// opens (or creates) a folder called "folderName in "path"
CMsgFolder::CMsgFolder(const char* path, const char* folderName):messages(NULL), nextMsgNum(0)
{
}
// Writes out open messages and folder information
CMsgFolder::~CMsgFolder()
{
if( messages != NULL)
CloseMsgFolder();
}
// Puts a message in the folder
// Will save message in its own file
// Names are of the form "MSGxxx" where xxx is a number from
// 1 to max number of messages ever created.
// new messages are put at the end of the list of current messages
// Save maximum buffer sizes for topic and text fields.
bool CMsgFolder::PostMsg(const CMessage& thisMsg)
{
// 1 to the watershed of number of messages??
HANDLE fileHandle;
Item thisItem;
if (messages == NULL) // no message folder opened
return FALSE;
ZeroMemory(thisItem.fileName, sizeof(thisItem.fileName));
ZeroMemory(thisItem.topic, sizeof(thisItem.topic));
sprintf(thisItem.fileName, "MSG%d", ++nextMsgNum);
fileHandle = CreateFile(thisItem.fileName,GENERIC_WRITE,0,0,CREATE_NEW,0,0);
if(fileHandle == INVALID_HANDLE_VALUE) {
cerr << "Error: " << GetLastError() <<
" when creating file:" << thisItem.fileName << endl;
return FALSE;
}
if(!thisMsg.WriteMsg(fileHandle)) {
return FALSE;
}
CloseHandle(fileHandle);
thisMsg.GetTopic(thisItem.topic);
msgList.AddItem(msgList.length()+1,thisItem);
return TRUE;
}
// gets message with ID "messageId
// if message ID does not exist returns EMPTY message
CMessage CMsgFolder::GetMsg(msgID messageId) const
{
if (messages == NULL)
return NULL;
Item msgHeader;
HANDLE msgFile;
if (msgList.GetItem(messageId, msgHeader)) {
msgFile = CreateFile(msgHeader.fileName,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(msgFile == INVALID_HANDLE_VALUE) {
cerr << "Error: " << GetLastError() <<
" when opening file: " << msgHeader.fileName << endl;
ExitProcess(1);
}
CMessage msg(msgFile); // create a new message and initialize from msgFile
CloseHandle(msgFile);
return msg;
}
else
return NULL;
}
bool CMsgFolder::DeleteMsg(msgID messageID)
{
if(messages == NULL)
return false;
if(msgList.DeleteItem(messageID))
return TRUE;
else
return FALSE;
}
// PRE: message with "messageID" exists
// topic points to a string big enough to hold topic string
// POST: if exists, returns true and topic of this message
// else returns false
bool CMsgFolder::GetMsgTopic(msgID messageId, char* topic) const
{
if(messages == NULL)
return false;
Item thisItem;
if(msgList.GetItem(messageId, thisItem)) {
strncpy(topic, thisItem.topic, SIZE_TOPIC+1);
return TRUE;
}
else
return FALSE;
}
int CMsgFolder::NumMsgs() const
{
return msgList.length();
}
// ****
// verifyFolder - verifies then creates/opens message folder
bool CMsgFolder::verifyFolder(const char* name)
{
DWORD nBytes;
if(CreateDirectory(name,0)) {
cout << "Created Directory:" << name << endl;
if(!SetCurrentDirectory(name)) {
cerr << "Could not set message folder" << name << GetLastError();
return FALSE;
}
// create "messages" file with magic cookie
messages = CreateFile("messages",GENERIC_WRITE,0,0,CREATE_NEW,0,0);
if(messages == INVALID_HANDLE_VALUE) {
cerr << "Could not create messages file" << GetLastError();
return FALSE;
}
return TRUE;
}
else {
if(!SetCurrentDirectory(name)) {
cerr << "Could not set message folder" << name << GetLastError();
return FALSE;
}
// Now check if this folder contains a "messages" file with the magic cookie
messages = CreateFile(
"messages",
GENERIC_READ|GENERIC_WRITE, // read then write this file
0,0,
OPEN_EXISTING,
0,0);
if(messages == INVALID_HANDLE_VALUE) {
cerr << "Could not open messages file" << GetLastError();
return FALSE;
}
int cookie;
if(!ReadFile(messages, (LPBYTE) &cookie, sizeof(cookie), &nBytes, NULL)) {
cerr << "Could not read magic cookie!" << GetLastError();
return FALSE;
}
if(cookie != MAGIC_COOKIE) {
cerr << "no magic cookie!\n";
return FALSE;
}
if(!ReadFile(messages, (LPBYTE) &nextMsgNum, sizeof(nextMsgNum), &nBytes, NULL)) {
cerr << "Could not nMessages!" << GetLastError();
return FALSE;
}
// now read messages list
if(!msgList.ReadListMsgs(messages)) {
cerr << "Could not read message list\n";
return FALSE;
}
// now set file pointer to beginning for update
return TRUE;
}
}
bool CMsgFolder::OpenMsgFolder(const char* folder)
{
char currentDir[MAX_PATH];
int currentLen = GetCurrentDirectory(MAX_PATH, currentDir);
folderName = new char[currentLen + strlen(folder)+2];
strncpy(folderName,currentDir,strlen(currentDir));
strncpy(folderName+strlen(currentDir),"\\",1);
strncpy(folderName+strlen(currentDir)+1, folder, strlen(folder));
strncpy(folderName+strlen(currentDir)+1+strlen(folder), "\0",1);
return verifyFolder(folderName);
}
bool CMsgFolder::CloseMsgFolder()
{
DWORD nBytes;
if (messages == NULL) return TRUE; // nothing to do
delete folderName;
folderName = NULL;
SetFilePointer(messages,0,0,FILE_BEGIN); // rest pointer
if(!WriteFile(messages, (LPBYTE) &MAGIC_COOKIE, sizeof(int), &nBytes, NULL)) {
cerr << "Could not write magic cookie!" << GetLastError();
ExitProcess(1);
}
if(!WriteFile(messages, (LPBYTE) &nextMsgNum, sizeof(nextMsgNum), &nBytes, NULL)) {
cerr << "Could not write nMessages!" << GetLastError();
ExitProcess(1);
}
if(!msgList.WriteListMsgs(messages)) {
cerr << "Could not write messages!\n" ;
ExitProcess(1);
}
if (CloseHandle(messages)){
messages = NULL;
return TRUE;
}
else
return FALSE;
}
}
Testing and Experimenting
In adding new functions to each
version, I use a test driver for the CMessage class. Here is
the one for version 1.
//***************************************************
// TestMessage: Test Message Object
// alos demonstrates some WIN32 file handling calls
// This is a console application program
//
// Store Msg1 in file1 and Msg2 in file2
// Initialize Msg3 from file1
// store both msg1 and msg2 in file3 and read into
// msg 4 and msg5
//
// AUTHOR: Chris Wild
// DATE: July 25, 1997
//***************************************************
#include "message.h"
#include <iostream.h>
void main() {
HANDLE file1, file2, file3;
cout << "Testing the Message Object!\n\n";
CMessage msg1, msg2;
// set topics in first two messages
msg1.SetTopic("This is topic 1!");
msg2.SetTopic("This is topic 2!");
// now display them
char topic1[SIZE_TOPIC+1], topic2[SIZE_TOPIC+1];
msg1.GetTopic(topic1);
cout << "Topic 1: " << topic1 << endl;
msg2.GetTopic(topic2);
cout << "Topic 2: " << topic2 << endl;
// Set message text in message1 and display
msg1.SetText("This is first line of message 1\nThis is second line of message 1\n");
char message1[SIZE_TEXT+1];
msg1.GetText(message1);
cout << "\n\nMessage 1:\n" << message1;
// save msg1 to file
file1 = CreateFile("TestMsg1",GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
if(file1 == INVALID_HANDLE_VALUE) {
cerr << "Error: " << GetLastError() <<
" when creating file: TestMsg1" << endl;
ExitProcess(1);
}
if(!msg1.WriteMsg(file1)) {
cerr << " Error writing message msg1\n";
ExitProcess(1);
}
CloseHandle(file1);
// read this file into msg3
file1 = CreateFile("TestMsg1",GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(file1 == INVALID_HANDLE_VALUE) {
cerr << "Error: " << GetLastError() <<
" when opening file: TestMsg1" << endl;
ExitProcess(1);
}
CMessage msg3(file1); // create msg3 - initializing from file2
char topic3[SIZE_TOPIC+1];
msg3.GetTopic(topic3);
cout << "\n\nTopic 3 (should be the same as 1): " << topic3 << endl;
char message3[SIZE_TEXT+1];
msg3.GetText(message3);
cout << " \nMessage 3 (Should be the same as 1):\n" << message3;
// save msg1 and msg2 to same file
file2 = CreateFile("TestCombinedMsg",GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
if(file2 == INVALID_HANDLE_VALUE) {
cerr << "Error: " << GetLastError() <<
" when creating file: TestCombinedMsg" << endl;
ExitProcess(1);
}
if(!msg1.WriteMessage(file2)) {
cerr << " Error writing message msg1\n";
ExitProcess(1);
}
if(!msg2.WriteMessage(file2)) {
cerr << " Error writing message msg2\n";
ExitProcess(1);
}
CloseHandle(file2);
// Now read back messages from combined file into msg4 and msg5
CMessage msg4, msg5;
file3 = CreateFile("TestCombinedMsg",GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(file3 == INVALID_HANDLE_VALUE) {
cerr << "Error: " << GetLastError() <<
" when opening file: TestCombinedMsg" << endl;
ExitProcess(1);
}
msg4.ReadMessage(file3);
msg5.ReadMessage(file3);
char topic4[SIZE_TOPIC+1], topic5[SIZE_TOPIC+1];
msg4.GetTopic(topic4);
cout << "\nTopic 4 (should be same as topic1): " << topic4 << endl;
msg5.GetTopic(topic5);
cout << "\nTopic 5 (should be same as topic2): " << topic5 << endl;
char message4[SIZE_TEXT+1];
msg4.GetText(message4);
cout << "\nMessage 4 (should be same as 1):\n" << message4;
cout << "\n\nThis terminates the test of the Message Object\n";
}
Lab Assignment 1
Assume that the
"messages" file in the message folder directory has
become corrupted.
Rebuild that file from the other
files in the directory.
Copyright chris wild 1997.
For problems or questions regarding this web contact [Dr. Wild].
Last updated: September 01, 1997.