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