// TransferDlg.cpp : implementation file // #include "stdafx.h" #include "serial.h" #include "TransferDlg.h" #include "OXSCFILE.H" #include "OXCRCCHK.H" #include "Globals.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define IDM_START_SENDING (WM_USER + 10) #define IDM_START_RECEIVING (WM_USER + 11) //#define __USE_MESSAGES // // Uncomment the above directive if you wish MESSAGE-ing between the two // sides enabled. Obviosly a "Real" protocol would implement such a device but this // application is only intended as a sample and hence does not cover advanced // topics like synchronization. Popular protocols are Z-modem, Y-modem, and X-modem. // // #define MESSAGE_TYPE UINT #define MESSAGE_SIZE sizeof(UINT) #define MESSAGE_NONE (0x0000) #define MESSAGE_CANCEL (0x0001) #define MESSAGE_STOPPED (0x0002) #define MESSAGE_WANTSEND (0x0004) #define MESSAGE_OKAYSEND (0x0008) ///////////////////////////////////////////////////////////////////////////// // CTransferDlg dialog CTransferDlg::CTransferDlg(CWnd* pParent /*=NULL*/) : CDialog(CTransferDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTransferDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_pFile = NULL; m_pCommFile = NULL; m_bSending = TRUE; m_bCancelPressed = FALSE; } void CTransferDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTransferDlg) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CTransferDlg, CDialog) //{{AFX_MSG_MAP(CTransferDlg) //}}AFX_MSG_MAP ON_MESSAGE(IDM_START_SENDING, OnStartSending) ON_MESSAGE(IDM_START_RECEIVING, OnStartReceiving) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTransferDlg message handlers BOOL CTransferDlg::OnInitDialog() { CDialog::OnInitDialog(); SetDlgItemText(IDC_TRANSFER_TEXT, m_sMessage); // // Start either Receivng or Sending data // if (m_bSending) PostMessage(IDM_START_SENDING); else PostMessage(IDM_START_RECEIVING); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } LONG CTransferDlg::OnStartReceiving(WPARAM, LPARAM) { // // // This function (message handler) will begin receiving data on the // COMM port. This function is not multi-threaded, instead it uses // the global PumpMessages function to maintain the Applications message // pump. This function should be implemented as another thread, but, as // mentioned before, this is left up to you to implement. // // The function will wait for incoming data to appear in the Receive queue. // Once data is found it begins receiving it by first, reading the filesize, // next the filename, and then the file checksum. After all this // the file is read in 1024 chunks until complete. Again, this is a very simple // protocol. You should investigate Z-modem, Y-modem and other popular // protocols. // // The file checksum is only checked once at the end of the transmission. This is // done for simplicity. Idealy, you would check the checksum of each transmitted // packet. Thus, if the checksum of a packed failed you could simply ask to have // it transmitted instead of having the whole file re-transmitted (as the case // would be in this scenario). // // On completion, CDialog::OnOK or CDialog::OnCancel is called to close the dialog // window. // ASSERT_VALID(m_pCommFile); if (m_pCommFile == NULL) { CDialog::OnCancel(); return -1; } ShowWindow(SW_SHOW); CString sNewFilename = GetAppDir(); CFile f; BYTE bBuffer[1024]; UINT nBytesReceived = 0; UINT nTotalBytesReceived = 0; DWORD dwFileSize; TCHAR szFilename[MAX_PATH]; DWORD dwChecksum; TRY { // Wait m_sMessage.Format(_T("Waiting for incoming files...")); SetDlgItemText(IDC_TRANSFER_TEXT, m_sMessage); while (m_pCommFile->IsRxQueueEmpty() && !m_bCancelPressed) { PumpMessages(); } if (m_bCancelPressed) { CDialog::OnCancel(); return 0; } // first: read the filesize m_pCommFile->Read(&dwFileSize, sizeof(dwFileSize)); // second: read the filename m_pCommFile->Read(szFilename, sizeof(szFilename)); // display message about incoming file m_sMessage.Format(_T("file %s (%d bytes long) detected.\r\n"), szFilename, dwFileSize); SetDlgItemText(IDC_TRANSFER_TEXT, m_sMessage); // third: read the checksum m_pCommFile->Read(&dwChecksum, sizeof(dwChecksum)); CProgressCtrl* pProg = (CProgressCtrl*) GetDlgItem(IDC_TRANSFER_PROGRESS); ASSERT_VALID(pProg); pProg->SetRange(0, 100); pProg->SetPos(0); // fourth: read the incoming data (streamed into a file object) sNewFilename += _T('\\'); sNewFilename += szFilename; if (!f.Open(sNewFilename, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite | CFile::shareDenyRead)) { m_sMessage.Format(_T("Error: Unable to create the new file.")); } else { TRY { // // Keep reading the file until either the user cancels or // the file has finsihed. // while ((nTotalBytesReceived < dwFileSize) && !m_bCancelPressed) { nBytesReceived = m_pCommFile->Read(bBuffer, sizeof(bBuffer)); nTotalBytesReceived += nBytesReceived; m_sMessage.Format(_T("%s\n%d of %d bytes received."), sNewFilename, nTotalBytesReceived, dwFileSize); SetDlgItemText(IDC_TRANSFER_TEXT, m_sMessage); f.Write(bBuffer, nBytesReceived); pProg->SetPos(((nTotalBytesReceived * 100 / (UINT) dwFileSize))); PumpMessages(); } if (m_bCancelPressed) { #ifdef __USE_MESSAGES // the transfer has been cancelled; wait for the other side to // acknowledge receipt of this message MESSAGE_TYPE message = MESSAGE_CANCEL; m_pCommFile->Write(&message, MESSAGE_SIZE); do { m_pCommFile->Read(&message, MESSAGE_SIZE); } while (message != MESSAGE_STOPPED); #endif m_sMessage.Format(_T("Transfer was cancelled\r\n")); CDialog::OnCancel(); return 0; } f.Close(); } CATCH(CFileException, e) { TCHAR szError[256]; e->GetErrorMessage(szError, sizeof(szError)); m_sMessage.Format(_T("Error: %s\r\n"), szError); } END_CATCH } } CATCH(COXSerialCommException, e) { m_sMessage.Format(_T("An Exception occurred (%d)\r\n"), e->m_cause); } END_CATCH // perform simple integrity check on the file if (nTotalBytesReceived == dwFileSize) { COXCheckSum32 checksum; DWORD dwNewChecksum; TRY { dwNewChecksum = checksum.CalculateFile(sNewFilename); TRACE(_T("%s has checksum of %d\n"), sNewFilename, dwNewChecksum); } CATCH(CFileException, e) { m_sMessage.Format(_T("%s successfully received\r\nChecksum test failed.\r\n"), sNewFilename); CDialog::OnOK(); return 0; } END_CATCH if (dwChecksum == dwNewChecksum) { m_sMessage.Format(_T("%s successfully received\r\nFile passed integrity check (%d)\r\n"), sNewFilename, dwNewChecksum); } else { m_sMessage.Format(_T("%s successfully received\r\nFile failed integrity check (remote:%d; local:%d)\r\n"), sNewFilename, dwChecksum, dwNewChecksum); } } else { m_sMessage.Format(_T("%s was not properly received (%d bytes not received)\r\n"), sNewFilename, dwFileSize - nTotalBytesReceived); } CDialog::OnOK(); return 0; } LONG CTransferDlg::OnStartSending(WPARAM, LPARAM) { // // // The function will begin sending the file over the COMM port. It assumes that // the awaiting client is ready to receive. Really however, A // "Are you ready to receive" message should be send to the client to ensure // the transfer works properly. In this application if the user isn't waiting to // receive a file, the data will appear in his/her "Terminal" window. // // This function should run in a thread. Instead, for simplicity's sake it uses the // simple PumpMessage function to maintain the application's message pump. // // See CTransferDlg::OnStartReceiving for more information about the protocol used // to transfer files. // ASSERT_VALID(m_pFile); ASSERT_VALID(m_pCommFile); if (m_pFile == NULL || m_pCommFile == NULL || m_sFilename.IsEmpty()) { CDialog::OnCancel(); return -1; } ShowWindow(SW_SHOW); CProgressCtrl* pProg = (CProgressCtrl*) GetDlgItem(IDC_TRANSFER_PROGRESS); ASSERT_VALID(pProg); DWORD dwTotal = 0; DWORD dwCount = 0; // CFile object m_pFile already open COXCheckSum32 checksum; DWORD dwChecksum; TRY { // calculate the file checksum dwChecksum = checksum.CalculateFile(m_pFile); m_pFile->SeekToBegin(); TRACE(_T("%s has checksum of %d\n"), m_sFilename, dwChecksum); } CATCH(CFileException, e) { m_sMessage.Format(_T("Fatal Error: Unable to calculate file checksum\r\n")); TRACE(_T("Unable to calculate file checksum\r\n")); return -1; } END_CATCH BYTE bBuffer[1024]; TCHAR szFilename[MAX_PATH]; lstrcpy(szFilename, (LPCTSTR) m_sFilename); // first: send number of bytes this file is DWORD dwFileSize = m_pFile->GetLength(); m_pCommFile->Write(&dwFileSize, sizeof(DWORD)); // second: send filename m_pCommFile->Write(&szFilename, sizeof(szFilename)); // third: send checksum m_pCommFile->Write(&dwChecksum, sizeof(dwChecksum)); pProg->SetRange(0, 100); pProg->SetPos(0); // fourth: write the file while ((dwCount = m_pFile->Read(bBuffer, sizeof(bBuffer))) != 0 && !m_bCancelPressed) { dwTotal += dwCount; m_pCommFile->Write(bBuffer, dwCount); m_sMessage.Format(_T("%s\n%d of %d bytes sent"), m_sFilename, dwTotal, dwFileSize); SetDlgItemText(IDC_TRANSFER_TEXT, m_sMessage); pProg->SetPos(((dwTotal * 100 / dwFileSize))); #ifdef __USE_MESSAGES m_pCommFile->Read(&message, MESSAGE_SIZE); if (message == MESSAGE_CANCEL) { m_bCancelPressed = TRUE; message = MESSAGE_STOPPED; m_pCommFile->Write(&message, MESSAGE_SIZE); break; } #endif PumpMessages(); } #ifdef __USE_MESSAGES if (message == MESSAGE_CANCEL) m_sMessage.Format(_T("Other side cancelled transfer; %d of %d bytes sent\r\n"), dwTotal, dwFileSize); else m_sMessage.Format(_T("Finished. Successfully sent %d byte(s).\r\n"), dwTotal); #else m_sMessage.Format(_T("Finished. Successfully sent %d byte(s).\r\n"), dwTotal); #endif if (m_bCancelPressed) CDialog::OnCancel(); else CDialog::OnOK(); return 0; } void CTransferDlg::OnCancel() { // User has pressed the cancel button; Disable the button and set // flag // m_bCancelPressed = TRUE; GetDlgItem(IDCANCEL)->EnableWindow(FALSE); }