[ Home | Syllabus | Course Notes | Assignments | Search]
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.
APIs to
Network/Domain/Machine/Drive tree
FOR THE TIME BEING (MAYBE FOREVER) WE DON"T NEED TO KNOW ABOUT THIS VERY MUCH.
FindFirstFile
FindNextFile
FindClose
(click to access version 1 directory)
- Message Object
- Message Folder Object
- List Object
//****************************************************************************** // 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
//*******************************************************
// MessageFolder Object: Keeps a folder of CMessage objects
// AUTHOR: Chris Wild
// DATE: July 25, 1997
// Revised: January 17, 1999
//*******************************************************
#ifndef MESSAGEFOLDER_H
#define MESSAGEFOLDER_H
#include "Message.h"
#include "ListMsgs.h"
#include <windows.h>
typedef int msgID;
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);
// Writes out open messages and folder information
~CMsgFolder();
// Puts a message in the folder
bool PostMsg(const CMessage& msg);
// PRE: message with "messageID" exists
// POST: gets message with ID "messageId"
// and returns it in "thisMsg"
// returns true if successful else false
bool GetMsg(msgID messageId, CMessage & thisMsg) 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)
// Deletes message with msgID "messageID"
// removes from MsgList and deletes Message file
// returns false if 1) no folder open
// 2) message does not exists in list
// 3) file cannot be deleted
// returns true is successfull
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"
// Will close previously open folder first
// 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
//*************************************** // 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
// 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;
}
// . . .
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)
{
char currentDir[MAX_PATH];// from stdio.h int currentLen = GetCurrentDirectory(MAX_PATH, currentDir);
messages = NULL; nextMsgNum = 0; 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); } // 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 false message bool CMsgFolder::GetMsg(msgID messageId, CMessage & thisMsg) 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); } if(!thisMsg.ReadMsg(msgFile)) { CloseHandle(msgFile); return false; } CloseHandle(msgFile); return true; } else return false; } // Deletes message with msgID "messageID" // removes from MsgList and deletes Message file // returns false if 1) no folder open // 2) message does not exists in list // 3) file cannot be deleted // returns true is successfull bool CMsgFolder::DeleteMsg(msgID messageID) { if(messages == NULL) return false; Item msgHeader; if(msgList.GetItem(messageID, msgHeader)) { if(!DeleteFile(msgHeader.fileName)) { cerr << "Error: " << GetLastError() << " when deleting file: " << msgHeader.fileName << endl; ExitProcess(1); } if(msgList.DeleteItem(messageID)) return true; else return false; } 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; } nextMsgNum = 0; 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]; if( messages != NULL) CloseMsgFolder(); 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
SetEndOfFile(messages); // truncate file 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); }
// delete list
while(msgList.DeleteItem(1)) ; // delete first item continuously until done
// now return to parent directory
if(!SetCurrentDirectory("..")) {
cerr << "Could not reset to parent directory" << GetLastError();
return false;
}
if (CloseHandle(messages)){
messages = NULL;
return TRUE;
}
else
return FALSE;
}
}
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";
}
Assignment 2
See Assignment 2.