SSN: __________________________________________ (put on all submitted work)

Systems Programming/NT

Mid Term Test

1) The current version of the message handler only deletes messages from the message list (CListMsgs msgList),
it does not delete the message file itself. Fix this (write the code which will also delete the message file).
(see Drives and Directories Notes for code - version1 is still relevant). (15 points)

bool CMsgFolder::DeleteMsg(msgID messageID)
{
	Item thisMsg; // 2 points

	if(messages == NULL)
		return false;
	if( msgList.GetItem(messageID, thisMsg)) // 5 points
		if(msgList.DeleteItem(messageID))
			// delete message file
			return DeleteFile(thisMsg.fileName)) // 8 points
	return false;
}
rainbow.gif (2243 bytes)
// A correct but less acceptable solution is to delete the file in CListMsgs::DeleteItem
// Why less acceptable? because the responsibility is misplaced. This object maintains lists
// not message archives. It should be a template to handle lists of anything.
bool CListMsgs::DeleteItem(int position)
{
	if((position < 1)
		|| (position > size))
		return FALSE;	// bad position
	ListPtr cur;
	size--;
	if(position == 1) {
		cur = head;
		head = head->next;
	}
	else {
		ListPtr prev = PtrTo(position-1);
		cur = prev->next;
		prev->next = cur->next;
	}
	DeleteFile(cur->item.fileName);
	delete cur; // avoid memory leak -5
	return TRUE;
}

2) Consider an implementation of the message folder in which all messages are stored in the same file (like most e-mail systems). In an early example (the drives and directories notes) we showed how all messages could be saved to a single file.
Rewrite the GetMsg function of CMsgFolder to directly read the fixed length message from the folder file. Assume that the OpenMsgFolder function has already opened this file and stored a handle to it in the data member "m_msgFolder". Assume further that this file contains only messages (and thus can be considered an "array" of CMessage Objects. Your algorithm should be O(1).  (HINT use SetFilePointer) (15 points)

// gets message with ID "messageId
// if message ID does not exist returns NULL
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);
	//	}
		SetFilePointer(m_msgFolder,(messageId-1)*sizeof(msg), NULL, FILE_BEGIN); // assume less than 32 bits
// SIZE_TOPIC+SIZE_TEXT assumes certain packing of strings -- OK but risky.
		CMessage msg(m_msgFolder);	// create a new message and initialize from msgFile
// reading the file here - not dividing responsibility -5
		return msg;
	}
	else
		return NULL;
}

3) In implementing a Remote Procedure Call for the client/server version 2 and 3. we neglected several important aspects of the WIN32 environment. First describe why the win32 function GetLastError (used in several places in the non client/server version) no longer works. Suggest a way to fix this problem. (15 points)

Because getLastError is only valid in the environment in which the error occurred, GetLastError will not work with our version of RPC calls. (7 poins - solution below is 8 points))

4) In Version 3, multiple clients are handled with mutliple threads. Using the code fragments from the notes on version 3, modify it so that only one client is handled at a time, eliminating the need for threads. Thus your solution should: (15 points)

  1. Have no threads
  2. Accept a new client if not already busy
  3. Handle a client until the client breaks the connection
  4. Run forever.

 

// Prototype to thread call
VOID talkToClient(VOID * cs);

int main()
{
// . . .
	DWORD threadID;
	HANDLE clientThread; 
	
	serverSocket = getServerSocket();
	
	while (true) {
		// accept a connection from a client
		clientSocket=accept(serverSocket,
			(LPSOCKADDR) &clientSockAddr,
			&addrLen);
//. . .
		talkToClient((VOID *) clientSocket); // Pass in one parameter
// 10 points
// if modify version2 instead - must have accept in the loop else -8 and must have loop -7. . .
	}
	
}


VOID talkToClient(VOID *cs)
{
	SOCKET socket2client=(SOCKET)cs; 
	NET_CONTROL request;

	while(TRUE) // loop until killed
	{
		request = getControl(socket2client);
		if (request == FAILURE) {
			shutdown(socket2client, 2);  // 2 shutdown send and receive
			closesocket(socket2client);
			return; // 5 points
		}
		switch (request)
// . . .

5) One of the values in a windows message is the message ID. Is it possible to have two message handlers which respond to messages with the same ID? explain. Is it possible to have two controls (any arbitrary function actually) which send a message with the same ID to the same message handler? explain (15 points)

Yes, because messages are routed to a window and if the window has its own message handler attach for that message ID, two different windows can have two different handlers attached to the same ID. In MFC, two different classes can attach message handlers to the same messageID. (7 points)

rainbow.gif (2243 bytes)

Actually you can easily test this, by changing the IDs in pre Dialog version of MsgFolder

Of you can check out the SendMessage Test Program.

Also check out help files on ON_BN_CLICKED

rainbow.gif (2243 bytes)

Yes it used often - for instance to map keyboard accelerators and menu items to the same handler. (8 points)

6) You are unhappy about the user interface developed for version 4 for the following reasons:

  1. The program starts with an initially blank screen (BLANK_SCREEN1) - boring
  2. After selecting a message folder, you again get the blank screen (BLANK_SCREEN2) and have to select the edit message menu item
  3. After editing a message, you again get that blank screen (BLANK_SCREEN3).

You hate that blank screen and want to get rid of it. Here are some problems to ponder (using the code below from "main.cpp" which implement the OnOpenFolder and OnEditMsg menu message handlers: (5 points each)

  1. What would be the effect of changing the first "if" statement in OnEditMsg to a "while" statement? (does it eliminate any of the three dreaded blank screens)?
    Eliminates BLANK_SCREEN3 - after editing a message, returns to message selection dialog
  2. How would you get out of this loop and where would you be (what state of the objects and what appears on the screen)?
    Hit the cancel button (new won't do since it calls OnOK - look at the program logic).
    You will be at BLANK_SCREEN2 (with message folder open)
  3. How can you change this code segment so that after selecting a folder to open, the program immediately goes to the message selection dialog (CMsgDlg) to select a message for editing?
    Yes - call OnEditMsg after "SetWindowText"
  4. Can  you eliminate the initial blank screen (BLANK_SCREEN1) by calling dlg.DoModal in the constructor for CMainWindow? explain
    NO - this is the first error discussed in dialog notes. (the ShowWindow/UPdateWindow will not be called until after DoModal - so nothing will show)
  5. Can you eliminate a blank screen in the case you want to change to another message folder after already opening one? explain or show the code.
    Change "if" in OnOpenFolder to "while" and call OnEdit Msg as in part3. So if user selects cancel on message selection it returns to folder selection dialog and stays there until you cancel that dialog.
    // calling DoModal in OnOpenFolder is not exactly what I wanted -2
void CMainWindow::OnOpenFolder()
{
	CFolderDlg dlg;
	CString message;

	if(dlg.DoModal() == IDOK) {
		SetWindowText("Message Folder Demo [" + dlg.m_folderName + "]");
	}
}

void CMainWindow::OnEditMsg()
{
	CString title;
	CMsgDlg msgDlg;
	CMessage msg;

	if(msgDlg.DoModal() == IDOK)
		if (msgDlg.m_MsgID != NONE_SELECTED) {
			if(msgDlg.m_MsgID != NEW_MSG)
				msg = msgFolderApp.msgFolder.GetMsg(msgDlg.m_MsgID);
			
			CMsgEdit msgEdit;
			msgEdit.m_pMsg = &msg;
			if(msgEdit.DoModal() == IDOK)
				if(msgDlg.m_MsgID == NEW_MSG)
					msgFolderApp.msgFolder.NewMsg(msg);
				else
					msgFolderApp.msgFolder.UpdateMsg(msg, msgDlg.m_MsgID);
		}

}
rainbow.gif (2243 bytes)
void CMainWindow::OnOpenFolder()
{
	CFolderDlg dlg;
	CString message;

	while(dlg.DoModal() == IDOK) { // part 5
		SetWindowText("Message Folder Demo [" + dlg.m_folderName + "]");
		OnEditMsg(); // part 3
	}
}

void CMainWindow::OnEditMsg()
{
	CString title;
	CMsgDlg msgDlg;
	CMessage msg; // define it outside the loop
	CMsgEdit msgEdit;
			

	while(msgDlg.DoModal() == IDOK) // part 1
		if (msgDlg.m_MsgID != NONE_SELECTED) {
			if(msgDlg.m_MsgID != NEW_MSG)
				msg = msgFolderApp.msgFolder.GetMsg(msgDlg.m_MsgID);
			else {
				msg.SetTopic(""); // clear any previous values
				msg.SetText("");
			}
			msgEdit.m_pMsg = &msg;
			if(msgEdit.DoModal() == IDOK)
				if(msgDlg.m_MsgID == NEW_MSG)
					msgFolderApp.msgFolder.NewMsg(msg);
				else
					msgFolderApp.msgFolder.UpdateMsg(msg, msgDlg.m_MsgID);
		}

}