399 lines
10 KiB
C++
399 lines
10 KiB
C++
// 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);
|
|
}
|