基于MFC串口编程和曲线图绘制(visual studio2008,Teechart绘图控件)的程序总结
前言
今年剛進入公司按經理的要求為底盤測控機寫了一個小小的console。這也是第一次教認真的完成整個程序的編寫。程序不大,所用技術比較基礎也不前衛,屬于初級程序員的練手程序(知識的整理和搬運)。雖然如此,期間也由于一次選用的方案不正確而推掉重做了一次。要是有什么寫的不對的地方請留言賜教,謝謝。
介紹
以下是這次用到的主要知識內容: 1)MSCOMM串口控件編程; 2)Window API函數的串口編程; 3)Teechart繪圖控件編程; 4)Windows 函數多線程編程; 5)MFC主窗口和子窗口間的值傳遞和控件控制; 6)MFC自定義消息函數和子窗口的初始化函數; 7)MFC各種控件的使用:Combo box control,edit control,button control,text control,tab control,check-box control,以及兩個外插控件MScomm串口控件和Teechart繪圖控件。MScomm控件串口編程
第一次的方案選用MScomm控件實現串口數據的傳輸,之后改用windows API串口編程。其是全雙工,封裝性好,不需要太多的了解內部的實現方式和協議的細節,只需簡單的調用相應的API就能實現相應的功能。但其缺點是不夠靈活,而且*不支持多線程*(放棄使用的原因),要在其他主機使用該控件編寫的程序時需要注冊該控件(第三方控件都需要如此)。下面做簡單的使用總結,并提及一些個人使用中在意的細節。更詳細的使用方式其他博客有很多。使用方式如下:創建控件并添加響應函數和控制變量->配置串口參數并打開串口->在響應函數中實現數據接收和發送的處理->關閉串口。
下載好控件并安裝。控件下載路徑
在VS工具欄中,選擇工具(T)->選擇工具選項(x)->com組件->選中控件并點確認。
之后工具欄中就會有一個電話的圖形控件。
把該控件添加到界面視圖,并添加OnComm()響應事件和控制變量。就可以按自己要求實現相應的串口通訊功能了(初始化各屬性和參數->在OnComm()響應函數中實現數據處理功能)。 在該程序中使用的函數有: put_CommPort()//選擇串口 put_RThreshold()//設置觸發OnComm事件的條件 put_InputMode()//驗取數據方式 put_InBufferSize()//輸入緩沖區大小 put_OutBufferSize()//輸出緩沖區大小 put_Settings()//串口參數設置 put_InputLen()//設置接收區數據長度 put_PortOpen()//打開串口get_PortOpen()//獲取串口狀態 get_Input()//獲取緩沖區數據 get_CommEvent()//獲取串口事件OnComm()//響應函數個人的實現代碼片段:
//設置-----------------------------------if(m_CtrlComm.get_PortOpen())m_CtrlComm.put_PortOpen(FALSE);m_CtrlComm.put_CommPort(COM_NUM); //選擇串口號m_CtrlComm.put_RThreshold(2); //收到兩個字節引發OnComm事件m_CtrlComm.put_InputMode(1); //輸入模式選為二進制m_CtrlComm.put_InBufferSize(1024); //設置輸入緩沖區的大小,Bytesm_CtrlComm.put_OutBufferSize(1024); //設置輸出緩沖區的大小,Bytesm_CtrlComm.put_Settings(SerialPortMessage); //設置串口參數,波特率,奇偶校驗,數據位,停止位m_CtrlComm.put_InputMode(1); //以二進制方法驗取數據 // m_CtrlComm.put_RThreshold(1); //每當串口接收緩沖區中有多于或等于1個字符時將引發一個接收數據的OnComm事件m_CtrlComm.put_InputLen(0); //設置當前接收區數據長度為0 // m_ctrlComm.put_PortOpen(TRUE); //打開串口if(!m_CtrlComm.get_PortOpen())m_CtrlComm.put_PortOpen(TRUE);elseAfxMessageBox("Can not open serial port!");m_CtrlComm.get_Input();//事件處理------------------------------- void Ccomm12Dlg::OnCommMscomm1() {// TODO: 在此處添加消息處理程序代碼VARIANT variant_inp;COleSafeArray safearray_inp;long len,k;BYTE rxdata[2048];BYTE bt = NULL;if(m_CtrlComm.get_CommEvent() == 2) //事件值為2表示接收緩沖區內有字符{//以下你可以根據自己的通信協議加入處理代碼UpdateData(TRUE);variant_inp = m_CtrlComm.get_Input(); //讀緩沖區safearray_inp = variant_inp; //VARIANT型變量轉換為ColeSafeArray型變量len = safearray_inp.GetOneDimSize(); //得到有效數據長度for(k=0;k<len;k++)safearray_inp.GetElement(&k,rxdata+k); //轉換為BYTE型數組for(k=0;k<len;k++) //將數組轉換為Cstring型變量{bt = *(char*)(rxdata+k); //字符型//strtemp_recive_data.Format("%X",bt); //將字符送入臨時變量strtemp存放 m_EditRxData+=bt; //加入接收編輯框對應字符串 strtemp_recive_data+=bt;}............ }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
注意事項:控件在安裝后有些可能需要注冊才能正常使用控件。在接收的數據中,因為其接收的數據類型是VARIANT,需要將其轉為ColeSafeArray類型->BYTE類型。特別的,在數據需要以十六進制輸出去時要用到CByteArray數據類型!這里給出實現代碼:
// 去除空格并壓縮數據 int Ccomm12Dlg::String2Hex(CString str, CByteArray& senddata) {int hexdata,lowhexdata;int hexdatalen=0;int len=str.GetLength();senddata.SetSize(len/2);for(int i=0;i<len;){char lstr,hstr=str[i];if(hstr==' '){i++;continue;}i++;if(i>=len)break;lstr=str[i];hexdata=ConvertHexChar(hstr);lowhexdata=ConvertHexChar(lstr);if((hexdata==16)||(lowhexdata==16))break;elsehexdata=hexdata*16+lowhexdata;i++;senddata[hexdatalen]=(char)hexdata;hexdatalen++;}senddata.SetSize(hexdatalen);return hexdatalen; }// 將一個字符轉換為相應的十六進制 char Ccomm12Dlg::ConvertHexChar(char ch) {if((ch>='0')&&(ch<='9'))return ch-0x30;else if((ch>='A')&&(ch<='F'))return ch-'A'+10;else if((ch>='a')&&(ch<='f'))return ch-'a'+10;else return ch; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
Windows API 串口編程
在放棄MScomm串口控件后,使用API串口編程方式。該方式雖然相比MScomm控件函數實現方式比較復雜一點,但其實現功能更加的靈活,功能更加完善,在其他各戶機運行編程的程序也方便,重要的是它支持多線程,這就使其數據處理的效率要高,不需要擔心因為事件的堵塞而影響其他功能的操作或其他數據的處理。Windows API串口編程主要有*同步通信方式*及*異步通信方式*兩種。考慮到多線程方式的實現,當然選著使用異步通信方式。實現方法:
打開串口->配置串口(設置驅動類型、配置超時結構參數、配置數據塊結構參數)->創建監聽函數->使用數據讀取、數據寫入函數->關閉串口。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
個人的代碼實現片段:
//-------------------打開串口并設置串口 void CConsole10Dlg::OnBnClickedButtonPortopen() {// TODO: 在此添加控件通知處理程序代碼.................hCom = CreateFile(strSerialPortText,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING, //設置產生方式FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL, //異步通信NULL);if(hCom == INVALID_HANDLE_VALUE) //檢測打開串口操作是否成功{AfxMessageBox("串口打開失敗!");} //SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY); //設置事件驅動的類型SetupComm(hCom,2048,2048); //設置輸入、輸出緩沖區的大小PurgeComm(hCom,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); //清干凈輸入、輸出緩沖區COMMTIMEOUTS CommTimeOuts; //定義超時結構,并填寫該結構,總超時=時間系數*要求讀/寫的字符數+時間常量CommTimeOuts.ReadIntervalTimeout = 10; //讀間隔超時,無延時因為有WaitCommEvent等待數據CommTimeOuts.ReadTotalTimeoutMultiplier = 0; //讀時間系數CommTimeOuts.ReadTotalTimeoutConstant = 0; //讀時間常數CommTimeOuts.WriteTotalTimeoutMultiplier = 50; //寫時間系數CommTimeOuts.WriteTotalTimeoutConstant = 50; //寫時間常量 SetCommTimeouts(hCom,&CommTimeOuts); //設置讀寫操作所允許的超時DCB dcb; //定義數據控制塊結構GetCommState(hCom,&dcb); //讀串口原來的參數設置dcb.BaudRate = atol(strBaudRateText); //波特率dcb.ByteSize = atoi(strDataBitsText); //數據位dcb.fParity = TRUE;BYTE wCheck;if(strCheckDigitText == "奇校驗")wCheck = ODDPARITY;else if(strCheckDigitText == "偶校驗")wCheck = EVENPARITY;else if(strCheckDigitText == "無校驗")wCheck = NOPARITY;dcb.Parity = wCheck; //校驗位BYTE wStop;if(strStopBitText = "1")wStop = ONESTOPBIT;else if(strStopBitText = "1.5")wStop = ONE5STOPBITS;else if(strStopBitText = "2")wStop = TWOSTOPBITS;dcb.StopBits = wStop; //停止位dcb.fBinary = TRUE;SetCommState(hCom,&dcb); //串口參數配置if(!SetCommMask(hCom,EV_RXCHAR|EV_TXEMPTY))//設置接收字符和輸出緩沖串口事件需要監視AfxMessageBox("SetCommMask failed with error!");//創建事件對象m_ovRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);m_ovWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);m_ovWait.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);if(!(m_Thread =AfxBeginThread((AFX_THREADPROC)CommWatchProc,this)))//創建監聽線程AfxMessageBox("創建監聽線程失敗!");......... }............//-------------------------------監聽線程,實現數據的讀 UINT CommWatchProc(LPVOID pParam) {CConsole10Dlg *obj = (CConsole10Dlg*)pParam;COMSTAT ComStat = {0};DWORD WaitEvent = 0,Bytes = 0;BOOL Status = FALSE;DWORD Error = 0;BYTE lpBuffer[2048];if(hCom != NULL)ClearCommError(hCom,&Error,&ComStat);while(obj->m_IsOpen){Status = WaitCommEvent(hCom,&WaitEvent,&obj->m_ovRead);if(FALSE == Status&&GetLastError() == ERROR_IO_PENDING)Status = GetOverlappedResult(hCom,&obj->m_ovRead,&Bytes,TRUE); //查詢,等待重疊操作結束時返回ClearCommError(hCom,&Error,&ComStat);if(Status == TRUE//等待事件成功 && WaitEvent&EV_RXCHAR//緩存中有數據到達 && ComStat.cbInQue > 0)//有數據{memset(lpBuffer,0,sizeof(lpBuffer));Status = ReadFile(hCom,lpBuffer,sizeof(lpBuffer),&Bytes,&obj->m_ovRead);GetOverlappedResult(hCom,&obj->m_ovRead,&Bytes,TRUE);for(DWORD k = 0;k<Bytes;k++){obj->m_Edit_Rxdata += lpBuffer[k];.........}...........PurgeComm(hCom, PURGE_RXCLEAR|PURGE_RXABORT);}}return 0; }...........//-------------------------實現數據的寫入 BOOL CConsole10Dlg::CommWrite(CString wBuffer) {CByteArray test;int len = String2Hex(wBuffer,test);//轉換字符性BYTE temp[1024];for(int i = 0;i<len;i++)temp[i] = test.GetAt(i);LPBYTE buffer = (BYTE *)temp;BOOL rtn = FALSE;DWORD WriteSize = 0;PurgeComm(hCom,PURGE_TXCLEAR|PURGE_TXABORT);rtn = WriteFile(hCom,buffer,len,&WriteSize,&m_ovWrite);len = 0;if(FALSE == rtn && GetLastError() == ERROR_IO_PENDING)//后臺讀取{//等待數據寫入完成if(FALSE == GetOverlappedResult(hCom,&m_ovWrite,&WriteSize,TRUE))return FALSE;}len = WriteSize;return rtn; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
注意事項:就如前面所說,Windows API的串口編程比MScomm控件的串口編程要復雜一點。在創建串口時,特別注意CreateFile()中dwFlagsAndAttributes參數設置為異步通信:FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL。之后也要注意檢查DCB結構參數的設置。稍難實現的邏輯部分是,監聽函數中的數據讀取(檢測到有數據到達后,需進行狀態檢測、錯誤判斷之后才能正確讀取數據),這可以寫的簡單也可以寫的復雜但健壯一些(主要是錯誤信息的處理方式),由于使用異步通信,ReadFile()時會返回ERROR_IO_PENDING錯誤,需要用GetOverlappedResult()阻塞查詢狀態等待重疊操作的結束。在數據讀取時再次用GetOverlappedResult()阻塞等待數據的讀取完成。
Teechart繪圖控件編程
Teechart屬于第三方控件,同樣需要下載并注冊再添加進VS的工具箱才能使用。在其他電腦運行編寫的程序也需要再次注冊才能正常打開。其封裝性好,比起MFC自帶的繪圖功能要簡單好用。繪圖功能比較豐富,在這里我只是用其2D曲線繪圖功能。
使用方式:
下載安裝并注冊好->把控件添加進vs工具箱->添加控件設置好屬性->按需要實現功能。
控件下載連接
工具欄中:工具(T)->選擇工具箱項(X)->COM控件中選中Teechart Pro Activex control v5點確定
之后工具箱中出現Teechart控件圖標,拖入到程序窗口并添加控件變量實現所需功能。
這里還需添加Teechart的類和控制變量才能實現相應的功能:
在”類視圖“中右鍵->添加類->選中“Typelib中的MFC類”點添加
可用類庫中選擇TeeChart Pro Activex Control v5<1.0>,選擇需要的接口,(這里只需圖中選擇的類)并點完成。之后“類視圖”中就有了相應的類,“解決方案資源管理器”中也有了相應的頭文件。將需要用到Teechart控件函數的.ccp文件中添加這些頭文件就可以使用其封裝函數了。
我的程序里,創建了兩個Teechart控件,都是實現實時曲線的繪制,圖像在任意點能放大。
其中一個的配置要求是:Y軸固定顯示范圍,X軸顯示25個單元數并隨曲線向左移動;
另一個的配置要求是:Y軸固定顯示范圍,X軸隨曲線自動變化并保留整個X軸數據。
個人的實現代碼片段:
//第一個圖的繪制函數.......................... //清空之前的繪圖曲線 for(long i = 0;i<m_CtrlTchart1.get_SeriesCount();i++){CSeries serDemo = (CSeries)m_CtrlTchart1.Series(i);serDemo.Clear();}//重繪X軸(只能代碼實現),屬性頁面只能配置第一次繪圖時的屬性CAxes chartaxis = (CAxes)m_CtrlTchart1.get_Axis();CAxis chartaxisbottem = (CAxis)chartaxis.get_Bottom();chartaxisbottem.SetMinMax(0,25);//-------------if(!pDlg->CommWrite(ConstData))if(!(OnPaint_Thread =AfxBeginThread((AFX_THREADPROC)OnPaintTchart,pDlg)))//創建繪圖線程AfxMessageBox("創建繪圖線程失敗!");............... }UINT OnPaintTchart(LPVOID pParam) {CConsole10Dlg *pDlg = (CConsole10Dlg*)pParam;pDlg->m_OnPainData.Empty();//開始時清空繪圖前的數據while(pDlg->m_bOnPaint && !pDlg->m_IsTimeTest){.........................//---------------------繪圖函數是以下幾行代碼CSeries serDemo = (CSeries)pDlg->page1.m_CtrlTchart1.Series(0);serDemo.AddXY(X,Y,strtempX,0);//實現X軸的左移CAxes chartaxis = (CAxes)pDlg->page1.m_CtrlTchart1.get_Axis();CAxis chartaxisbottem = (CAxis)chartaxis.get_Bottom();chartaxisbottem.Scroll(1.0,TRUE);//---------------------------------其余都是數據的處理.................return 0; }//------------第二個圖的繪制函數 ..............CSeries serDmo = (CSeries)m_CtrlTchart2.Series(0);serDmo.Clear();CAxes chartaxis = (CAxes)m_CtrlTchart2.get_Axis();CAxis chartaxisbottem = (CAxis)chartaxis.get_Bottom();chartaxisbottem.put_Automatic(TRUE);....CString Data = ......if(!(m_TimeThread = AfxBeginThread((AFX_THREADPROC)OnPaintTimeFunction,pDlg)))AfxMessageBox("繪圖線程創建失敗!");pDlg->m_IsTimeTest = true;............. }UINT OnPaintTimeFunction(LPVOID pParam) {CConsole10Dlg *pDlg = (CConsole10Dlg*)pParam;pDlg->m_OnPainData.Empty();//開始時清空繪圖前的數據while((!pDlg->m_bOnPaint)&&pDlg->m_IsTimeTest){................. //----------------------------------圖像的繪制是以下兩行代碼CSeries serDemo = (CSeries)pDlg->page2.m_CtrlTchart2.Series(0);serDemo.AddXY(X,Y,strtempX,0); //----------------------------------其余都是接收數據的處理......................return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
注意事項:步驟中沒有給出控件控制變量的添加。在曲線的二次繪制時需要清除之前繪制的圖像,軸的屬性設置也需要代碼重新設置一次。其實現還是很簡單的。
Windows函數的多線程編程
多線程的實現還是有好幾種函數方式的(CreateThread(),_BeginThread(),_beginThreadex()),而在MFC中有兩種線程:用戶界面線程和輔助線程(AfxBeginThread())。 用戶界面線程通常用于處理用戶輸入及響應用戶生成的事件和消息。 輔助線程通常用于完成不需要用戶輸入的任務(如重新計算)。 Win32 API 不區分線程類型;它只需要了解線程的起始地址以開始執行線程。 MFC 為用戶界面中的事件提供消息泵,從而對用戶界面線程進行專門處理。線程還涉及線程間的數據傳輸、同步、安全,線程優先級,線程狀態,互斥和臨界區,線程池等。線程的編程和調試是BUG的常發區。我在此用輔助線程和使用了一個互斥對象確保數據的安全和同步問題。
使用的類和函數有: class CMutex : public CSyncObject {DECLARE_DYNAMIC(CMutex)// Constructor public:/* explicit */ CMutex(BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL,LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);// Implementation public:virtual ~CMutex();BOOL Unlock(); };//互斥對象BOOL Lock(DWORD dwTimeout)//鎖函數BOOL Unlock(void)//解鎖函數HANDLE AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,UINT nStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL)/*創建線程函數,比CreateThread()安全*/UINT CommWatchProc(LPVOID pParam);//線程函數,遵循UINT FunctionName(LPVOID pParam)形式UINT OnPaintTchart(LPVOID pParam);UINT OnPaintTimeFunction(LPVOID pParam);CloseHandle(Handle)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
個人的代碼片段:
//聲明全局函數(要在類外,線程函數不能屬于類)UINT CommWatchProc(LPVOID pParam);UINT OnPaintTchart(LPVOID pParam);UINT OnPaintTimeFunction(LPVOID pParam);//生成全局互斥類對象 CMutex hmutex(NULL,FALSE,NULL); //全局互斥對象//創建線程函數,并傳入窗口類的this指針參數,方便窗口類的變量和函數的使用 m_Thread =AfxBeginThread((AFX_THREADPROC)CommWatchProc,this)//創建監聽線程UINT CommWatchProc(LPVOID pParam) {CConsole10Dlg *obj = (CConsole10Dlg*)pParam;功能.....hmutex.Lock(INFINITE);//鎖定互斥對象*要保護數據*所在代碼段hmutex.Unlock();//釋放互斥對象return 0; }//關閉線程if(NULL != m_Thread){WaitForSingleObject(m_Thread,1000);//等待線程結束CloseHandle(m_Thread);m_Thread = NULL;}//其余兩個線程類似- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
注意事項:線程創建運行完后要記得銷毀,數據要初始化防止不必要的內存泄露。線程中要盡量少用互斥和臨界區或使用較高效率的臨界函數,提高運行效率。
MFC主窗口和子窗口之間的變量傳遞和控件使用
在編程時,遇到兩個窗口間的通信問題。其實也是兩個類之間的變量和函數的使用問題。要解決也很簡單:
主窗口類:CConsole10Dlg(有變量(pubilc):bool m_bOnPaint;
函數:void CommWrite(CString Data);
子窗口類:CDlg1(有變量(public):int m_Edit_ConstData;
主窗口獲取子窗口變量和函數的使用:
在主窗口類的頭文件(Console1.0Dlg.h)中添加子窗口頭文件,并定義子窗口類對象。通過該對象就可以調用子類窗口的變量和函數了!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
子窗口獲取主窗口變量和函數的使用:
在子窗口.cpp文件中加入主窗口頭文件,再直接使用GetParent()函數獲取主窗口句柄,根據該句柄就可以訪問主窗口變量和函數了!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
注意事項:頭文件的包含不能相互包含
MFC自定義消息函數和子窗口的初始化函數
自定義消息函數要記住宏”WM_USER”,該宏是Windows定義的宏,該宏值以內的消息變量都是系統自定義的消息變量,為了自定義消息變量不與系統消息變量發生沖突,只需在該宏上加一個正數值就行,之后要自己定義消息函數,并手動添加函數映射:
//.h文件中 #define WM_MESSAGE WM_USER + 100 //自定義消息IDafx_msg LRESULT OnChangeEdit(WPARAM wParam, LPARAM lParam);//自定義消息函數//.cpp文件中 BEGIN_MESSAGE_MAP(CConsole10Dlg, CDialog) ... ON_MESSAGE(WM_MESSAGE, &CConsole10Dlg::OnChangeEdit)//添加消息映射 ... END_MESSAGE_MAP()//在需要觸發自定義消息函數的地方調用PostMessage()函數,實現自定義消息函數的執行PostMessage(obj->m_hWnd,WM_MESSAGE,0,0);//函數的實現 LRESULT CConsole10Dlg::OnChangeEdit(WPARAM wParam, LPARAM lParam) {所需功能return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
再來,是子窗口的初始化函數的編寫。創建的子窗口是沒有初始化函數的,不過初始化函數是虛函數,子窗口類和主窗口類同是CDialog的派生類。因此,只需將主窗口的初始化函數復制到子窗口的實現文件里(.cpp)就行了!
BOOL CDlg1::OnInitDialog() { CDialog::OnInitDialog();初始化功能return TRUE; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
注意事項:子窗口的初始化函數中要將類名改為所在類的類名。
MFC各種控件的使用
控件的使用挑選在編寫中比較在意的知識點:
關于Edit control 的內容顯示,一是讓滾動條停在實時內容上,二是消除顯示時的閃爍問題。另外,該控件定義的CString 變量是有存儲容量的,要記得消除過多的內容。有以下實現代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
關于Static control 控件的內容動態顯示:
this->GetDlgItem(IDC_STATIC_1)->SetWindowTextA(m_Static1);- 1
總結
內容積累的太多,沒寫細,后面的內容也沒認真整理。程序的要求功能基本完成。自己寫完后回去看,還真不乍樣,第一次寫,就這樣吧。之后要出差一段時間,隔太久來寫,記憶的效率就打折扣了。
總結
以上是生活随笔為你收集整理的基于MFC串口编程和曲线图绘制(visual studio2008,Teechart绘图控件)的程序总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ST_Curve --- 一个专业的曲线
- 下一篇: DOM 节点类型及属性