www.gusucode.com > vc++下利用多线程机制实现串口的异步读和写 > vc++下利用多线程机制实现串口的异步读和写/Modemasync/ModemDlg.cpp

    // ModemDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Modem.h"
#include "ModemDlg.h"
#include "gsm_sms.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define  SMSCENTER  "13800280500"

/////////////////////////////////////////////////////////////////////////////
// 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)
	//}}AFX_MSG_MAP
	
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CModemDlg dialog

CModemDlg::CModemDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CModemDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CModemDlg)
	m_strPhoneNum = _T("85773661");	
	m_strSend = _T("");
	m_SndTimes = 1;
	m_bPortOpened = FALSE;  // 初始时串口未打开   
    m_bOnLine=FALSE;        // 初始MODEM未在线,离线状态
	m_bNewLine = FALSE;     // 显示时不自动换行
	m_bOnLine = FALSE;
	m_bSetTimer=FALSE;
	m_pThread = NULL;
    nStrLen = 0;  
	m_nBaud = 9600;
	m_nDataBits = 8;
	m_nParity = 0;
	m_nStopBits = 1;
	m_SendFlag = FALSE;
	m_WantSendFlag = FALSE;
	m_strPhone = _T("");
	m_strSms = _T("");
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_strPort[0] = _T("COM1");
	m_strPort[1] = _T("COM2");
}

void CModemDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CModemDlg)
	DDX_Control(pDX, IDC_EDIT_SMS, m_editSms);
	DDX_Control(pDX, IDC_EDIT_PHONE, m_editPhone);
	DDX_Control(pDX, IDC_BUTTON_DELIVER, m_btnDelivery);
	DDX_Control(pDX, IDC_EDIT_RECEIVE, m_editReceive);
	DDX_Control(pDX, IDC_EDIT_PHONENUM, m_editPhoneNum);
	DDX_Control(pDX, IDCANCEL, m_btnCancel);
	DDX_Control(pDX, IDC_EDIT_SEND, m_editSend);
	DDX_Control(pDX, IDC_BUTTON_SEND, m_btnSend);
	DDX_Control(pDX, IDC_BUTTON_OPENPORT, m_btnOpenPort);
	DDX_Control(pDX, IDC_BUTTON_DIAL, m_btnDial);
	DDX_Control(pDX, IDC_BUTTON_CLOSEPORT, m_btnClosePort);
	DDX_Control(pDX, IDC_COMBO_STOPBITS, m_combStopBits);
	DDX_Control(pDX, IDC_COMBO_PORT, m_combPort);
	DDX_Control(pDX, IDC_COMBO_PARITY, m_combParity);
	DDX_Control(pDX, IDC_COMBO_DATABITS, m_combDataBits);
	DDX_Control(pDX, IDC_COMBO_BAUD, m_combBaud);
	DDX_Text(pDX, IDC_EDIT_PHONENUM, m_strPhoneNum);
	DDX_Text(pDX, IDC_EDIT_SEND, m_strSend);
	DDX_Text(pDX, IDC_EDITSNDTIMES, m_SndTimes);
	DDX_Text(pDX, IDC_EDIT_PHONE, m_strPhone);
	DDX_Text(pDX, IDC_EDIT_SMS, m_strSms);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CModemDlg, CDialog)
	//{{AFX_MSG_MAP(CModemDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_OPENPORT, OnBtnOpenPort)
	ON_BN_CLICKED(IDC_BUTTON_CLOSEPORT, OnBtnClosePort)
	ON_BN_CLICKED(IDC_BUTTON_DIAL, OnBtnDial)
	ON_BN_CLICKED(IDC_BUTTON_SEND, OnBtnSend)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON_DELIVER, OnDeliver)
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_COMMNOTIFY, OnCommNotify)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CModemDlg message handlers

BOOL CModemDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	m_btnClosePort.EnableWindow(FALSE);
	m_btnDial.EnableWindow(FALSE);
	m_btnSend.EnableWindow(FALSE);

    ghWnd=this->GetSafeHwnd();

	m_editReceive.SetReadOnly(TRUE);

	m_combPort.InsertString(0,_T("COM1"));
	m_combPort.InsertString(1,_T("COM2"));
    m_combPort.SetCurSel(1);

	m_combParity.InsertString(0,_T("NONE"));
	m_combParity.InsertString(1,_T("EVEN"));
	m_combParity.InsertString(2,_T("ODD"));
	m_combParity.SetCurSel(0);

	m_combDataBits.InsertString(0,_T("5"));
	m_combDataBits.InsertString(1,_T("6"));
	m_combDataBits.InsertString(2,_T("7"));
	m_combDataBits.InsertString(3,_T("8"));
	m_combDataBits.SetCurSel(3);	

	m_combBaud.InsertString(0,_T("300"));
	m_combBaud.InsertString(1,_T("600"));
	m_combBaud.InsertString(2,_T("1200"));
	m_combBaud.InsertString(3,_T("2400"));
	m_combBaud.InsertString(4,_T("9600"));
	m_combBaud.InsertString(5,_T("14400"));
	m_combBaud.InsertString(6,_T("19200"));
	m_combBaud.InsertString(7,_T("38400"));
	m_combBaud.InsertString(8,_T("57600"));
	m_combBaud.SetCurSel(4);

	m_combStopBits.InsertString(0,_T("1"));
	m_combStopBits.InsertString(1,_T("1.5"));
	m_combStopBits.InsertString(2,_T("2"));
	m_combStopBits.SetCurSel(0);

	// 为WM_COMMNOTIFY消息创建事件对象,手工重置,初始化为有信号的
	if((m_hPostMsgEvent=CreateEvent(NULL, TRUE, TRUE, NULL))==NULL)
		return FALSE;

	memset(&m_osRead, 0, sizeof(OVERLAPPED));
	memset(&m_osWrite, 0, sizeof(OVERLAPPED));

	// 为异步读创建事件对象,手工重置,初始化为无信号的
	if((m_osRead.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL))==NULL)
		return FALSE;

	// 为异步写创建事件对象,手工重置,初始化为无信号的
	if((m_osWrite.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL))==NULL)
		return FALSE;
    
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CModemDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CModemDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CModemDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CModemDlg::OnCancel() 
{
    //程序结束时删除线程、关闭串口的操作
	if(m_bPortOpened)
		OnBtnClosePort();

	// 删除事件句柄
	if(m_hPostMsgEvent)
		CloseHandle(m_hPostMsgEvent);

	if(m_osRead.hEvent)
		CloseHandle(m_osRead.hEvent);

	if(m_osWrite.hEvent)
		CloseHandle(m_osWrite.hEvent);
	
	CDialog::OnCancel();
}

BOOL CModemDlg::OnBtnOpenPort() 
{
	COMMTIMEOUTS TimeOuts;

	if(m_bPortOpened)
		return FALSE;

	// 异步方式打开串口
	m_hCom=CreateFile(m_strPort[m_combPort.GetCurSel()], GENERIC_READ | GENERIC_WRITE, 0, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 
		NULL); 

	if(m_hCom==INVALID_HANDLE_VALUE)
		return FALSE;

	SetupComm(m_hCom,MAXBLOCK,MAXBLOCK);
    SetCommMask(m_hCom,EV_RXCHAR);

	// 把间隔超时设为最大,把总超时设为0将导致ReadFile立即返回并完成操作
	TimeOuts.ReadIntervalTimeout=MAXDWORD; 
	TimeOuts.ReadTotalTimeoutMultiplier=0; 
	TimeOuts.ReadTotalTimeoutConstant=0; 

	/* 设置写超时以指定WriteComm成员函数中的GetOverlappedResult函数的等待时间*/
	TimeOuts.WriteTotalTimeoutMultiplier=50;
	TimeOuts.WriteTotalTimeoutConstant=2000;

	SetCommTimeouts(m_hCom, &TimeOuts);

	if(ConfigConnection())
	{

		m_pThread=AfxBeginThread(CommProc, this, THREAD_PRIORITY_NORMAL, 
			0, CREATE_SUSPENDED, NULL); // 创建并挂起线程

		if(m_pThread==NULL)
		{
			CloseHandle(m_hCom);
			return FALSE;
		}
		else
		{
			m_bPortOpened=TRUE;
			m_pThread->ResumeThread(); // 恢复线程运行
			m_btnOpenPort.EnableWindow(FALSE);
			m_btnClosePort.EnableWindow(TRUE);
			m_btnDial.EnableWindow(TRUE);
	        int k=0;
	        CString strTemp="ATS0=1\r";
	        nStrLen=strTemp.GetLength();
	        for(k=0;k<nStrLen;k++)
			{
	            outbuf[k]=strTemp.GetAt(k);
			}
	        WriteComm(outbuf,k);
		}
	}
	else
	{
		CloseHandle(m_hCom);
		return FALSE;
	}
	
	return TRUE;	
}

//串口配置函数,成功返回TRUE,失败返回FALSE
BOOL CModemDlg::ConfigConnection()
{
	DCB dcb;

	//得到串口当前配置
	if(!GetCommState(m_hCom, &dcb))
		return FALSE;

    m_nBaud=m_combBaud.GetCurSel();
	m_nDataBits=m_combDataBits.GetCurSel();
	m_nParity=m_combParity.GetCurSel();
	m_nStopBits=m_combStopBits.GetCurSel();

	dcb.fBinary=TRUE;
	switch(m_nBaud)//波特率(数据传输速率)
	{
	   case 0:
            dcb.BaudRate=300;
			break;
	   case 1:
		    dcb.BaudRate=600;
			break;
	   case 2:
            dcb.BaudRate=1200;
			break;
	   case 3:
		    dcb.BaudRate=2400;
			break;
	   case 4:
            dcb.BaudRate=9600;
			break;
	   case 5:
		    dcb.BaudRate=14400;
			break;
	   case 6:
            dcb.BaudRate=19200;
			break;
	   case 7:
		    dcb.BaudRate=38400;
			break;
	   case 8:
		    dcb.BaudRate=57600;
			break;
	   default:
		    dcb.BaudRate=9600; 
			break;
	}

	switch(m_nDataBits)      // 每字节位数
	{
	   case 0:
		    dcb.ByteSize=5;
			break;
	   case 1:
		    dcb.ByteSize=6;
			break;
	   case 2:
		    dcb.ByteSize=7;
			break;
	   case 3:
		    dcb.ByteSize=8;
			break;
	   default:
		    dcb.ByteSize=8;
			break;
	}		

	dcb.fParity = TRUE;
	switch(m_nParity) // 校验设置
	{
	case 0: 
		dcb.Parity=NOPARITY;
		break;

	case 1: 
		dcb.Parity=EVENPARITY;
		break;

	case 2: 
		dcb.Parity=ODDPARITY;
		break;

	default:
		dcb.Parity=NOPARITY;
		break;
	}

	switch(m_nStopBits) // 停止位
	{
	case 0: 
		dcb.StopBits=ONESTOPBIT;
		break;

	case 1: 
		dcb.StopBits=ONE5STOPBITS;
		break;

	case 2:
		dcb.StopBits=TWOSTOPBITS;
		break;

	default:
		dcb.StopBits=ONE5STOPBITS;
		break;;
	}

	// 硬件流控制设置
	dcb.fOutxCtsFlow = FALSE;//TRUE;
	dcb.fRtsControl = FALSE;//TRUE;

	// XON/XOFF流控制设置
	dcb.fInX=dcb.fOutX = TRUE;
	dcb.XonChar = XON;
	dcb.XoffChar = XOFF;
	dcb.XonLim = 50;
	dcb.XoffLim = 50;

	return SetCommState(m_hCom, &dcb);
}

//WM_COMMNOTIFY消息处理函数,只处理接收到数据消息,即EV_RXCHAR消息
LRESULT CModemDlg::OnCommNotify(WPARAM wParam, LPARAM lParam)
{
	CString str;
	int nLength, nTextLength;
    DWORD dwModemStatus=0;

	//如果不是EV_RXCHAR消息,该函数直接返回
	if((m_bPortOpened==FALSE) || !(wParam & EV_RXCHAR|EV_TXEMPTY|EV_ERR)) 
	{
		//使m_hPostMsgEvent成为有信号,即允许发送下一个WM_COMMNOTIFY消息
		SetEvent(m_hPostMsgEvent); 
		return 0L;
	}
	if(wParam & EV_TXEMPTY){
		if(wParam & EV_ERR){
			//send completed
			if(m_WantSendFlag && m_SndTimes > 1){
				//set timmmer to ask for next send permission
				m_SendFlag = 1;
				SetCommMask(m_hCom,EV_RXCHAR);//ask the  send permission
				UpdateData(TRUE);
				m_SndTimes --;
				UpdateData(FALSE);
			}else{
				m_WantSendFlag = FALSE;
			}
		}else{
			//send permission
			m_SendFlag = 2;
			SetCommMask(m_hCom,EV_RXCHAR);//ask the comm to send				
		}
	}
	//从串口读128字节数据
	nLength = (int)lParam;//nLength=ReadComm(buf,128);
	
	// 进入在线状态后,如果未设置定时器,则设置一个定时器
	// 若超过定时器间隔时间没有串口事件,则WM_TIMER消息被触发,挂断连接
	/*if(!m_bSetTimer&&m_bOnLine)
	{
		SetTimer(1,3000,0);  // 设置定时器间隔为3000ms
		m_bSetTimer=TRUE;
	}
	else if(m_bSetTimer&&m_bOnLine)  
	{
		KillTimer(1);        // 3000ms内有串口事件时,则清除上次所设定时器
	    SetTimer(1,3000,0);  // 重新设置定时器为3000ms
	}
    */
	if((m_bOnLine==FALSE)&&(m_bPortOpened==TRUE))
	{
	    CString strTemp;
	    strTemp.Format("%s",inbuf);//这里应该用outbuf还是inbuf??

		//两个Modem建立连接后,会向串口返回“CONNECTXXXXX”字符串,
		//若起始接收字符串中有“C”则判断Modem已处于在线状态
		//if(strTemp.Find("C")!=-1)
		//{
		   m_bOnLine=TRUE;
		   m_btnDial.SetWindowText(_T("挂断(&H)"));
		   m_btnOpenPort.EnableWindow(FALSE);
		   m_btnClosePort.EnableWindow(TRUE);
		   m_btnSend.EnableWindow(TRUE);
		//}
	}

	if(nLength)
	{
		nTextLength=m_editReceive.GetWindowTextLength();
		m_editReceive.SetSel(nTextLength,nTextLength); //移动插入光标到正文末尾

		for(int i=0;i<nLength;i++)
		{
			switch(outbuf[i])
			{
			case '\r': // 回车
				if(!m_bNewLine) 
					break;

			case '\n': // 换行
				str+="\r\n";
				break;

			case '\b': // 退格
				m_editReceive.SetSel(-1, 0);
				m_editReceive.ReplaceSel(str);
				nTextLength=m_editReceive.GetWindowTextLength();
				m_editReceive.SetSel(nTextLength-1,nTextLength);
				m_editReceive.ReplaceSel(""); //回退一个字符
				str="";
				break;

			case '\a': // 振铃 
				MessageBeep((UINT)-1);
				break;

			default : 
				str+=outbuf[i];
			}
		}
		m_editReceive.SetSel(-1, 0);
		m_editReceive.ReplaceSel(str); // 向编辑视图中插入收到的字符
	}

	//使m_hPostMsgEvent成为有信号,即允许发送下一个WM_COMMNOTIFY消息
	SetEvent(m_hPostMsgEvent);  
	return 0L;
}

// 从串行口输入缓冲区中读入指定数量的字符
DWORD CModemDlg::ReadComm(char *buf, DWORD dwLength)
{
	DWORD length=0;
	COMSTAT ComStat;
	DWORD dwErrorFlags;

	ClearCommError(m_hCom,&dwErrorFlags,&ComStat);
	length=min(dwLength, ComStat.cbInQue);
	ReadFile(m_hCom,buf,length,&length,&m_osRead);

	return length;
}

// 将指定数量的字符从串行口输出
DWORD CModemDlg::WriteComm(char *buf, DWORD dwLength)
{
	BOOL fState;
	DWORD length=dwLength;
	COMSTAT ComStat;
	DWORD dwErrorFlags;

	ClearCommError(m_hCom,&dwErrorFlags,&ComStat);
	fState=WriteFile(m_hCom,buf,length,&length,&m_osWrite);

	if(!fState)
	{
		if(GetLastError()==ERROR_IO_PENDING)
		{
			GetOverlappedResult(m_hCom,&m_osWrite,&length,TRUE);// 等待
		}
		else
			length=0;
	}

	return length;
}

// 工作者线程,负责监视串行口
UINT CommProc(LPVOID pParam)
{
	OVERLAPPED os;
	DWORD dwMask, dwTrans;
	COMSTAT ComStat;
	DWORD dwErrorFlags;

	CModemDlg *pModemDlg=(CModemDlg*)pParam;

	memset(&os, 0, sizeof(OVERLAPPED));
	os.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

	if(os.hEvent==NULL)
	{
		AfxMessageBox("Can't create event object!");
		return (UINT)-1;
	}
	while(pModemDlg->m_bPortOpened)
	{
		dwMask=0;

		// 异步操作,等待串口事件,事件结果在dwMask中
		if(!WaitCommEvent(pModemDlg->m_hCom, &dwMask, &os)) 
		{
			if(GetLastError()==ERROR_IO_PENDING)  
			{			    
				GetOverlappedResult(pModemDlg->m_hCom, &os, &dwTrans, TRUE);            
			}
			else                                     //等待串口事件时出错
			{
				CloseHandle(os.hEvent);
				return (UINT)-1;
			}
		}
		
		//清除串口可能发生的错误
		ClearCommError(pModemDlg->m_hCom,&dwErrorFlags,&ComStat);
		if(ComStat.cbOutQue == 0){
			int nLength;
			if( pModemDlg->m_SendFlag == 1){
				SetCommMask(pModemDlg->m_hCom,EV_RXCHAR);
				pModemDlg->m_SendFlag = FALSE;
				PostMessage(pModemDlg->ghWnd,WM_COMMNOTIFY,EV_TXEMPTY,0);
			}else if(pModemDlg->m_SendFlag == 2){
				SetCommMask(pModemDlg->m_hCom,EV_RXCHAR);
				nLength = pModemDlg->WriteComm(pModemDlg->outbuf,pModemDlg->nStrLen);	
				pModemDlg->m_SendFlag = FALSE;
				memset(pModemDlg->outbuf,0,sizeof(pModemDlg->outbuf));//将发送缓冲区清零
				PostMessage(pModemDlg->ghWnd,WM_COMMNOTIFY,EV_TXEMPTY|EV_ERR,nLength);
			}

		}
		if(ComStat.cbInQue)
		{
			int nLength;
			// 无限等待WM_COMMNOTIFY消息被处理完
			WaitForSingleObject(pModemDlg->m_hPostMsgEvent, INFINITE);
			ResetEvent(pModemDlg->m_hPostMsgEvent);
            
			// 向对话框发送WM_COMMNOTIFY消息
			nLength=pModemDlg->ReadComm(pModemDlg->inbuf,128);//test
			PostMessage(pModemDlg->ghWnd, WM_COMMNOTIFY, dwMask, nLength); 
			continue;
		}
		
	}
	CloseHandle(os.hEvent);
	return 0;
}

//“关闭串口”按钮处理函数
void CModemDlg::OnBtnClosePort() 
{
	if(!m_bPortOpened)
		return;
	else
	{
		m_bPortOpened=FALSE;
	    m_btnOpenPort.EnableWindow(TRUE);
        m_btnClosePort.EnableWindow(FALSE);
	    m_btnDial.EnableWindow(FALSE);
	    m_btnSend.EnableWindow(FALSE);

	     //结束CommProc线程中WaitSingleObject函数的等待
         SetEvent(m_hPostMsgEvent); 

		 //结束CommProc线程中WaitCommEvent的等待
		 SetCommMask(m_hCom, 0); 

		 //等待辅助线程终止
	     WaitForSingleObject(m_pThread->m_hThread, INFINITE);
	     m_pThread=NULL;
	     CloseHandle(m_hCom);
	}
}

//“拨号”按钮处理函数
void CModemDlg::OnBtnDial() 
{
	int k=0;
	CString strTemp;

	//若Modem处于离线状态,则拨号
	if(m_bOnLine==FALSE)
	{
	   UpdateData(TRUE);
	   m_btnDial.SetWindowText(_T("挂  断(&H)"));
	   m_strPhoneNum="ATDT"+m_strPhoneNum+"\r";
	   nStrLen=m_strPhoneNum.GetLength();
	   for(k=0;k<nStrLen;k++)
	   {
	       outbuf[k]=m_strPhoneNum.GetAt(k);
	   }
	   WriteComm(outbuf,nStrLen);
	}
    //若Modem处于在线状态,则挂断
	else
	{
	   m_bOnLine=FALSE;
	   m_bSetTimer=FALSE;
	   m_btnDial.SetWindowText(_T("拨  号(&D)"));
	   m_btnSend.EnableWindow(FALSE);
	   //发送换码序列“+++”,切换到在线命令状态
	   Sleep(1000);
	   for(k=0;k<3;k++)
	   {
	       outbuf[k]='+';
	   }
	   Sleep(1000);
	   WriteComm(outbuf,3);
	   Sleep(5000);
	   //发送挂断命令
	   strTemp="ATH\r";
	   nStrLen=strTemp.GetLength();
	   for(k=0;k<nStrLen;k++)
	   {
	      outbuf[k]=strTemp.GetAt(k);
	   }
	   WriteComm(outbuf,nStrLen);
	}
}

//“发送”按钮处理函数
void CModemDlg::OnBtnSend() 
{
	UpdateData(TRUE);
	int k=0;
	DWORD mask;
	nStrLen=m_strSend.GetLength();
	if(nStrLen>128)
	{
	   AfxMessageBox(_T("发送数据超出接收缓冲区大小!"),MB_ICONINFORMATION | MB_OK,NULL);
	   return;
	}
	mask = EV_TXEMPTY|EV_RXCHAR;
	for(k=0;k<nStrLen;k++)
	{
	   outbuf[k]=m_strSend.GetAt(k);
	}
	m_SendFlag = 1;//ask for send permittion
	SetCommMask(m_hCom,EV_TXEMPTY|EV_RXCHAR);//ask for send permittion
	m_WantSendFlag = TRUE;
	//WriteComm(buf,nStrLen);	
}

// WM_TIMER消息处理函数
void CModemDlg::OnTimer(UINT nIDEvent) 
{
	if(m_bSetTimer)  
	{
		KillTimer(1);  // 清除定时器1
	    OnBtnDial();   // 挂断
	}
	
	CDialog::OnTimer(nIDEvent);
}

void CModemDlg::OnDeliver() //点击SEND之后的动作。
{
	// TODO: Add your control notification handler code here
	SM_PARAM SmsParam;//构造发送pdu
	CString SmsCenter = SMSCENTER;//信息中心号码
	CString SmsDstNo;//cell phone number
	CString SmsContent;//sms content

	int nPduLength;//pdu length
	int nLength;
	unsigned char nSmscLength;
	unsigned char cmd[16];
	unsigned char pdu[512];	
	
	memset(&SmsParam,0,sizeof(SM_PARAM));
	UpdateData(TRUE);
	SmsDstNo = m_strPhone;
	SmsContent = m_strSms;

	//检查手机号码是否正确
	if (SmsDstNo.GetLength()<11)
	{
		AfxMessageBox(_T"手机号码位数不对,请输入正确号码!");
		return;
	}

	//如果用户输入的手机号码带"+",去掉!
	if(SmsDstNo[0] == '+') SmsDstNo = SmsDstNo.Mid(1);
	if(SmsCenter[0] == '+') SmsCenter = SmsCenter.Mid(1);

	//在手机号码前加86
	if(SmsDstNo.Left(2) != "86") SmsDstNo = "86" + SmsDstNo;
	if(SmsCenter.Left(2) != "86") SmsCenter = "86" + SmsCenter;

	//填充pdu
	strcpy(SmsParam.SCA,SmsCenter);
	strcpy(SmsParam.TPA,SmsDstNo);
	strcpy(SmsParam.TP_UD,SmsContent);
	SmsParam.TP_PID = 0;
	SmsParam.TP_DCS = GSM_7BIT;

	nPduLength = gsmEncodePdu(&SmsParam);
	strcat(pdu,"\0x01a");
	gsmString2Bytes(pdu,&nSmscLength,2);
	nSmscLength++;

	sprintf(cmd,"AT+CMGS=%d\r",nPduLength/2 - nSmscLength);
    strncpy(outbuf,cmd,strlen(cmd));
	nStrLen = strlen(cmd);	
	m_SendFlag = 1;//ask for send permission
	SetCommMask(m_hCom,EV_TXEMPTY|EV_RXCHAR);//ask for send permittion
	m_WantSendFlag = TRUE;
}