Windows NT Systems Programming: Spring 1999

[ Home | Syllabus | Course Notes | Assignments | Search]


Drives and Directories


Drives: One big difference from UNIX

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


WNET Functions

Network/Domain/Machine/Drive tree

FOR THE TIME BEING (MAYBE FOREVER) WE DON"T NEED TO KNOW ABOUT THIS VERY MUCH.


Directories

 


Enumerating a Directory

FindFirstFile

FindNextFile

FindClose


Teaching Project: Version 1

(click to access version 1 directory)

  1. Message Object
  2. Message Folder Object
  3. List Object

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
// 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

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

 


// 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)
{
	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;
}
}

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";
}

Assignment 2

See Assignment 2.


Copyright chris wild 1999.
For problems or questions regarding this web contact [Dr. Wild].
Last updated: January 18, 1999.