📄 simplechatdlg.cpp
字号:
// SimpleChatDlg.cpp : implementation file
// Written by Marat Bedretdinov (maratb@hotmail.com)
// Copyright (c) 2000.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// If the source code in this file is used in any commercial application
// then acknowledgement must be made to the author of this file
// and permissions to use this file are requested from the author
//
// (in whatever form you wish).// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage whatsoever.
// It's free - so you get what you pay for.//
#include "stdafx.h"
#include "SimpleChat.h"
#include "SimpleChatDlg.h"
#include "NetIPAddress.h"
#include "NetFileAddress.h"
#include "ChatMessages.h"
#include "NetSockConnector.h"
#include "NetFileConnector.h"
#include "NetFileInfo.h"
#include "NetFileHdr.h"
#include "NetNotif.h"
#include "SenderThread.h"
#include "ReceiverThread.h"
#include "ThreadStorage.h"
#include "NetSockAcceptor.h"
#include "MDC.h"
#include "SystemTray.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
const char* lpszTip = "SimpleChat is waiting for a peer to connect...";
HHOOK g_hook = 0;
CSimpleChatDlg* g_pDlg = 0;
/////////////////////////////////////////////////////////////////////////////
// CSimpleChatDlg dialog
CSimpleChatDlg::CSimpleChatDlg(CWnd* pParent /*=NULL*/)
: CDialog(CSimpleChatDlg::IDD, pParent),
m_pTrayIcon(0),
m_bMinimized(false),
m_nmsgTray(RegisterWindowMessage(lpszTip)),
m_nCheckForData(0),
m_nThreadId(0)
{
//{{AFX_DATA_INIT(CSimpleChatDlg)
m_strText = _T("");
m_strPort = _T("");
m_strWelMsg = _T("");
m_strByeMsg = _T("");
m_strYou = _T("");
m_bUseMsg = FALSE;
m_strStatus = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_bHandleNotify = true;
}
void CSimpleChatDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSimpleChatDlg)
DDX_Control(pDX, IDC_PROGRESS_SENT, m_ctrlProgressSent);
DDX_Control(pDX, IDC_PROGRESS_RECEIVED, m_ctrlProgressReceived);
DDX_Control(pDX, IDRE_MESSAGES, m_reChat);
DDX_Control(pDX, IDC_IPADDRESS, m_ctrlAddress);
DDX_Text(pDX, IDED_TEXT, m_strText);
DDV_MaxChars(pDX, m_strText, 255);
DDX_Text(pDX, IDED_PORT, m_strPort);
DDV_MaxChars(pDX, m_strPort, 4);
DDX_CBString(pDX, IDCB_WELCOME_MESSAGE, m_strWelMsg);
DDV_MaxChars(pDX, m_strWelMsg, 100);
DDX_CBString(pDX, IDCB_GOODBYE_MESSAGE, m_strByeMsg);
DDV_MaxChars(pDX, m_strByeMsg, 100);
DDX_CBString(pDX, IDCB_NICK_NAME, m_strYou);
DDV_MaxChars(pDX, m_strYou, 100);
DDX_Check(pDX, IDCH_USE_THIS, m_bUseMsg);
DDX_Text(pDX, IDST_CONNECT_STATUS, m_strStatus);
DDV_MaxChars(pDX, m_strStatus, 100);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CSimpleChatDlg, CDialog)
//{{AFX_MSG_MAP(CSimpleChatDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDBT_GO, OnGo)
ON_BN_CLICKED(IDBT_DISCONNECT, OnDisconnect)
ON_WM_DESTROY()
ON_BN_CLICKED(IDBT_SEND, OnSend)
ON_BN_CLICKED(IDBT_CLEAR, OnClear)
ON_WM_TIMER()
ON_BN_CLICKED(IDCH_USE_THIS, OnUseThis)
ON_MESSAGE(UWM_ON_ACCEPT, OnAccept)
ON_MESSAGE(UWM_ON_ACCEPT_FOR_FILE, OnAcceptForFile)
ON_MESSAGE(UWM_ON_LISTENING, OnListening)
ON_MESSAGE(UWM_ON_ACCEPT_ERROR, OnAcceptError)
ON_MESSAGE(UWM_ON_FILE, OnFile)
ON_MESSAGE(UWM_ON_FILE_SAVE_AS, OnFileSaveAs)
ON_MESSAGE(UWM_ON_FILE_RECEIVED_HEADER_FILE, OnFileHeaderReceived)
ON_MESSAGE(UWM_ON_FILE_PROGRESS_SEND, OnFileProgressSend)
ON_MESSAGE(UWM_ON_FILE_PROGRESS_RECEIVE, OnFileProgressReceive)
ON_MESSAGE(UWM_ON_FILE_DONE_SEND, OnFileDoneSend)
ON_MESSAGE(UWM_ON_FILE_DONE_RECEIVE, OnFileDoneReceive)
ON_MESSAGE(UWM_ON_FILE_REJECT, OnFileReject)
ON_MESSAGE(UWM_ON_FILE_RECEIVE_REJECT, OnFileReceiveReject)
ON_MESSAGE(UWM_ON_FILE_ERROR_SEND, OnFileErrorSend)
ON_MESSAGE(UWM_ON_FILE_ERROR_RECEIVE, OnFileErrorReceive)
ON_MESSAGE(UWM_ON_FILE_ABORT_SEND, OnFileAbortSend)
ON_MESSAGE(UWM_ON_FILE_ABORT_RECEIVE, OnFileAbortReceive)
ON_CBN_KILLFOCUS(IDCB_NICK_NAME, OnKillfocusNickName)
ON_BN_CLICKED(IDBT_CLEAR_CHAT, OnClearChat)
ON_BN_CLICKED(IDBT_SEND_FILE, OnSendFile)
ON_BN_CLICKED(IDBT_SEND_CANCEL, OnSendCancel)
ON_BN_CLICKED(IDBT_RECEIVE_CANCEL, OnReceiveCancel)
ON_BN_CLICKED(IDRB_CONNECT, OnConnect)
ON_BN_CLICKED(IDRB_LISTEN, OnListen)
ON_BN_CLICKED(IDBT_RECEIVE_ACCEPT, OnReceiveAccept)
ON_BN_CLICKED(IDBT_RECEIVE_REJECT, OnReceiveReject)
ON_BN_CLICKED(IDCH_SHOW_BK, OnShowBk)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
ON_COMMAND(IDM_RESTORE, OnRestore)
ON_COMMAND(IDM_EXIT, OnExit)
END_MESSAGE_MAP()
/* ON_MESSAGE(UWM_TEST_MESSAGE, OnTestMessage)
LRESULT CSimpleChatDlg::OnTestMessage(WPARAM wParam, LPARAM)
{
return 0;
}
*/
void CSimpleChatDlg::OnGo()
{
CButton* pbtnConnect = (CButton*)GetDlgItem(IDRB_CONNECT);
CButton* pbtnListen = (CButton*)GetDlgItem(IDRB_LISTEN);
UpdateData();
// get the ip adress from the UI control for the local host machine
BYTE d0, d1, d2, d3;
m_ctrlAddress.GetAddress(d0, d1, d2, d3);
char buff[20];
sprintf(buff, "%d.%d.%d.%d", d0, d1, d2, d3);
if (atoi(m_strPort) <= 0) return;
string addr = "addr='";
addr += buff;
addr += "';port='";
addr += m_strPort;
addr += "';";
// this call is blocking, so if return from Connect() I must start the timer
if (pbtnConnect->GetCheck() == 1)
Connect(addr);
// this call is threaded, so the timer starts when there is a connection
if (pbtnListen->GetCheck() == 1)
Listen(addr);
}
void CSimpleChatDlg::Connect(string& addr)
{
// cleanup
DisconnectAll();
try {
CNetIPAddress address;
address.SetConnectString(addr.c_str());
CNetSockConnector* pConnector = new CNetSockConnector;
// attach it to the connector
m_NetStream.Attach(pConnector);
// connect to the address of the remote host
m_NetStream.GetConnection()->Connect(address);
} catch (CNetException e) {
delete m_NetStream.Detach();
MessageBox("The peer appears to be offline. Please try again later.", "Simple Chat", MB_ICONINFORMATION|MB_OK);
DisconnectAll();
return;
}
// send first message - login
if (!SendLogin(nsLogin)) return;
// set up the acceptor thread for incomming connections for file transfer
ListenForFiles();
DisableControls();
}
void CSimpleChatDlg::ListenForFiles()
{
// prepare the thread for accepting,
// let it find an available port in the range from 4000 to 5000
if (!m_acceptorFileThread.SetAddress(4000, 5000)) {
MessageBox("Failed to listen on the given address and port. Please correct your parameters.", "Simple Chat", MB_ICONINFORMATION|MB_OK);
return;
}
m_acceptorFileThread.Subscribe(this);
// start accepting for incoming files transfers
// (none blocking, so the thread can successfuly terminate
// if application exists while thread is accepting)
m_acceptorFileThread.Start();
// now let the remote peer now that I have the file acceptor on such port
SendLocalFileAcceptAddr();
}
LRESULT CSimpleChatDlg::OnAcceptForFile(WPARAM wParam, LPARAM)
{
CNotifAcceptForFile* pEvent = (CNotifAcceptForFile*)wParam;
CNetConnection* pConnection = pEvent->getConnection();
// a file is going to be received through this connection
// so create a receiver thread
CReceiverThread* pThread = new CReceiverThread;
// pass the provider connection
// Note: pConnection will be deleted by the receiver thread
pThread->SetProviderConnector(pConnection);
pThread->Subscribe(this);
// add the thread into the thread storage
g_getThreadStorage().Add(pThread);
// start receiving!
pThread->Start();
// save the thread's id these buttons control
CWnd* pReceive = GetDlgItem(IDST_RECEIVE);
pReceive->SetWindowContextHelpId(pThread->GetThreadId());
delete pEvent;
return 0;
}
LRESULT CSimpleChatDlg::OnFile(WPARAM wParam, LPARAM)
{
delete (CNotifEvent*)wParam;
return 0;
}
LRESULT CSimpleChatDlg::OnFileHeaderReceived(WPARAM wParam, LPARAM)
{
const CNotifSaveAsFile* pEvent = (const CNotifSaveAsFile*)wParam;
const CNetFileTransmitInfo info = pEvent->getInfo();
// save the thread id
m_nThreadId = pEvent->getThreadId();
CString strMsg;
if (!m_strPeer.IsEmpty()) {
strMsg = "The peer (" + m_strPeer + ") sends you file '";
strMsg += info.GetData().m_strProviderName.c_str();
strMsg += "'";
}
else {
strMsg = "The peer sends you file '";
strMsg += info.GetData().m_strProviderName.c_str();
strMsg += "'";
}
// notify user that there is a file awaits to be accepted
WriteSystem("");
WriteSystem(strMsg);
WriteSystem("Press <Accept...> button to download this file, or press <Reject> button to cancel.");
WriteSystem("");
GetDlgItem(IDBT_RECEIVE_ACCEPT)->EnableWindow(TRUE);
GetDlgItem(IDBT_RECEIVE_REJECT)->EnableWindow(TRUE);
delete pEvent;
return 0;
}
void CSimpleChatDlg::OnReceiveAccept()
{
// get the thread
CNetThread* p = g_getThreadStorage().GetByThreadId(m_nThreadId);
CReceiverThread* pThread = dynamic_cast<CReceiverThread*>(p);
if (!pThread) return;
// reset it
m_nThreadId = 0;
GetDlgItem(IDBT_RECEIVE_ACCEPT)->EnableWindow(FALSE);
GetDlgItem(IDBT_RECEIVE_REJECT)->EnableWindow(FALSE);
// set file's status as accepted
pThread->SetFileStatus(nsAccept);
// next notification will be OnFileSaveAs
}
void CSimpleChatDlg::OnReceiveReject()
{
// get the thread
CNetThread* p = g_getThreadStorage().GetByThreadId(m_nThreadId);
CReceiverThread* pThread = dynamic_cast<CReceiverThread*>(p);
if (!pThread) return;
// reset it
m_nThreadId = 0;
GetDlgItem(IDBT_RECEIVE_ACCEPT)->EnableWindow(FALSE);
GetDlgItem(IDBT_RECEIVE_REJECT)->EnableWindow(FALSE);
// set file's status as accepted
pThread->SetFileStatus(nsReject);
// next notification will be OnFileReceiveReject
}
LRESULT CSimpleChatDlg::OnFileSaveAs(WPARAM wParam, LPARAM)
{
const CNotifSaveAsFile* pEvent = (const CNotifSaveAsFile*)wParam;
const CNetFileTransmitInfo info = pEvent->getInfo();
// get the thread
CNetThread* p = g_getThreadStorage().GetByThreadId(pEvent->getThreadId());
CReceiverThread* pThread = dynamic_cast<CReceiverThread*>(p);
if (!pThread) {
delete pEvent;
return 0;
}
// prompt user to choose the name of the file and the destination directory
CFileDialog dlg(FALSE, NULL, info.GetData().m_strProviderName.c_str(), OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST, "All Files (*.*)|*.*||", this);
// if user canceled out, then set the status of the file as 'reject'
if (dlg.DoModal() == IDCANCEL) {
// reject the file
pThread->SetFileStatus(nsReject);
delete pEvent;
return 0;
}
// ok it seems that the file is wanted
// enable cancel button
GetDlgItem(IDBT_RECEIVE_CANCEL)->EnableWindow(TRUE);
GetDlgItem(IDST_PERCENTAGE_RECEIVED)->ShowWindow(SW_SHOW);
GetDlgItem(IDST_PERCENTAGE_RECEIVED)->UpdateWindow();
// form the connect string
CString strPath = dlg.GetPathName();
// form the connect string for a brand new file
CString strConnect = "file = '";
strConnect += strPath;
strConnect += "'; openopt = 'trunc'";
// create the provider address. this will be deleted by the thread
CNetFileAddress* pConAddr = new CNetFileAddress;
pConAddr->SetConnectString(strConnect);
// now set the address to the thread
pThread->SetConsumerAddress(pConAddr);
pThread->SetFileStatus(nsPathSet);
delete pEvent;
return 0;
}
LRESULT CSimpleChatDlg::OnFileProgressReceive(WPARAM wParam, LPARAM)
{
const CNotifProgressFile_Receive* pEvent = (const CNotifProgressFile_Receive*)wParam;
const CNetFileTransmitInfo& info = pEvent->getInfo();
char szBuff[200];
sprintf(szBuff, "%d%% completed", info.GetCompletePercentage());
SetDlgItemText(IDST_PERCENTAGE_RECEIVED, szBuff);
m_ctrlProgressReceived.SetPos(info.GetCompletePercentage());
sprintf(szBuff, "%.2d of %.2d Kbytes", info.GetData().m_nCurrentSz / 1024, info.GetData().m_nTotalSz / 1024);
SetDlgItemText(IDST_RECEIVED, szBuff);
delete pEvent;
return 0;
}
LRESULT CSimpleChatDlg::OnFileProgressSend(WPARAM wParam, LPARAM)
{
const CNotifProgressFile_Send* pEvent = (const CNotifProgressFile_Send*)wParam;
const CNetFileTransmitInfo& info = pEvent->getInfo();
char szBuff[100];
sprintf(szBuff, "%d%% completed", info.GetCompletePercentage());
SetDlgItemText(IDST_PERCENTAGE_SENT, szBuff);
m_ctrlProgressSent.SetPos(info.GetCompletePercentage());
sprintf(szBuff, "%.2d of %.2d Kbytes", info.GetData().m_nCurrentSz / 1024, info.GetData().m_nTotalSz / 1024);
SetDlgItemText(IDST_SENT, szBuff);
delete pEvent;
return 0;
}
LRESULT CSimpleChatDlg::OnFileDoneReceive(WPARAM wParam, LPARAM)
{
const CNotifDoneFile_Receive* pEvent = (const CNotifDoneFile_Receive*)wParam;
const CNetFileTransmitInfo info = pEvent->getInfo();
m_ctrlProgressReceived.SetPos(0);
SetDlgItemText(IDST_PERCENTAGE_RECEIVED, "");
SetDlgItemText(IDST_RECEIVED, "Done.");
// disable cancel button
GetDlgItem(IDBT_RECEIVE_CANCEL)->EnableWindow(FALSE);
GetDlgItem(IDST_PERCENTAGE_RECEIVED)->ShowWindow(SW_HIDE);
delete pEvent;
return 0;
}
LRESULT CSimpleChatDlg::OnFileDoneSend(WPARAM wParam, LPARAM)
{
const CNotifDoneFile_Send* pEvent = (const CNotifDoneFile_Send*)wParam;
const CNetFileTransmitInfo info = pEvent->getInfo();
m_ctrlProgressSent.SetPos(0);
SetDlgItemText(IDST_PERCENTAGE_SENT, "");
SetDlgItemText(IDST_SENT, "Done.");
// enable the send button
GetDlgItem(IDBT_SEND_FILE)->EnableWindow(TRUE);
// disable cancel button
GetDlgItem(IDBT_SEND_CANCEL)->EnableWindow(FALSE);
GetDlgItem(IDST_PERCENTAGE_SENT)->ShowWindow(SW_HIDE);
delete pEvent;
return 0;
}
LRESULT CSimpleChatDlg::OnFileReceiveReject(WPARAM wParam, LPARAM)
{
SetDlgItemText(IDST_RECEIVED, "You have rejected the download.");
// disable cancel button
GetDlgItem(IDBT_RECEIVE_CANCEL)->EnableWindow(FALSE);
GetDlgItem(IDBT_RECEIVE_ACCEPT)->EnableWindow(FALSE);
GetDlgItem(IDBT_RECEIVE_REJECT)->EnableWindow(FALSE);
delete (CNotifEvent*)wParam;
return 0;
}
LRESULT CSimpleChatDlg::OnFileReject(WPARAM wParam, LPARAM)
{
SetDlgItemText(IDST_SENT, "File is rejected by remote peer.");
// enable the send button
GetDlgItem(IDBT_SEND_FILE)->EnableWindow(TRUE);
// disable cancel button
GetDlgItem(IDBT_SEND_CANCEL)->EnableWindow(FALSE);
delete (CNotifEvent*)wParam;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -