|
2007年11月20日
http://blog.csdn.net/lixiaosan/archive/2006/04/07/653563.aspx
以下未經(jīng)說(shuō)明,listctrl默認(rèn)view 風(fēng)格為report
相關(guān)類及處理函數(shù)
MFC:CListCtrl類
SDK:以 “ListView_”開(kāi)頭的一些宏。如 ListView_InsertColumn
1.
CListCtrl 風(fēng)格
LVS_ICON: 為每個(gè)item顯示大圖標(biāo) LVS_SMALLICON: 為每個(gè)item顯示小圖標(biāo)
LVS_LIST: 顯示一列帶有小圖標(biāo)的item LVS_REPORT: 顯示item詳細(xì)資料
直觀的理解:windows資源管理器,“查看”標(biāo)簽下的“大圖標(biāo),小圖標(biāo),列表,詳細(xì)資料”
2. 設(shè)置listctrl 風(fēng)格及擴(kuò)展風(fēng)格
LONG lStyle; lStyle = GetWindowLong(m_list.m_hWnd,
GWL_STYLE);//獲取當(dāng)前窗口style lStyle &= ~LVS_TYPEMASK;
//清除顯示方式位 lStyle |= LVS_REPORT; //設(shè)置style
SetWindowLong(m_list.m_hWnd, GWL_STYLE, lStyle);//設(shè)置style DWORD
dwStyle = m_list.GetExtendedStyle(); dwStyle |=
LVS_EX_FULLROWSELECT;//選中某行使整行高亮(只適用與report風(fēng)格的listctrl) dwStyle |=
LVS_EX_GRIDLINES;//網(wǎng)格線(只適用與report風(fēng)格的listctrl) dwStyle |=
LVS_EX_CHECKBOXES;//item前生成checkbox控件 m_list.SetExtendedStyle(dwStyle);
//設(shè)置擴(kuò)展風(fēng)格 注:listview的style請(qǐng)查閱msdn http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceshellui5/html/wce50lrflistviewstyles.asp
3. 插入數(shù)據(jù)
m_list.InsertColumn( 0, "ID",
LVCFMT_LEFT, 40 );//插入列 m_list.InsertColumn( 1, "NAME", LVCFMT_LEFT, 50
); int nRow = m_list.InsertItem(0, “11”);//插入行
m_list.SetItemText(nRow, 1, “jacky”);//設(shè)置數(shù)據(jù)
4.
一直選中item
選中style中的Show selection always,或者在上面第2點(diǎn)中設(shè)置LVS_SHOWSELALWAYS
5. 選中和取消選中一行
int nIndex = 0; //選中 m_list.SetItemState(nIndex,
LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED); //取消選中
m_list.SetItemState(nIndex, 0, LVIS_SELECTED|LVIS_FOCUSED);
6. 得到listctrl中所有行的checkbox的狀態(tài)
m_list.SetExtendedStyle(LVS_EX_CHECKBOXES); CString
str; for(int i=0; i<m_list.GetItemCount(); i++)
{ if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED ||
m_list.GetCheck(i)) {
str.Format(_T("第%d行的checkbox為選中狀態(tài)"), i);
AfxMessageBox(str); } }
7. 得到listctrl中所有選中行的序號(hào)
方法一: CString
str; for(int i=0; i<m_list.GetItemCount(); i++)
{ if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED
) { str.Format(_T("選中了第%d行"),
i); AfxMessageBox(str); } }
方法二: POSITION pos =
m_list.GetFirstSelectedItemPosition(); if (pos == NULL)
TRACE0("No items were selected!\n"); else {
while (pos) { int nItem =
m_list.GetNextSelectedItem(pos); TRACE1("Item %d was
selected!\n", nItem); // you could do your own processing on
nItem here } }
8. 得到item的信息
TCHAR szBuf[1024]; LVITEM lvi; lvi.iItem =
nItemIndex; lvi.iSubItem = 0; lvi.mask = LVIF_TEXT;
lvi.pszText = szBuf; lvi.cchTextMax = 1024;
m_list.GetItem(&lvi);
關(guān)于得到設(shè)置item的狀態(tài),還可以參考msdn文章 Q173242: Use Masks to Set/Get Item
States in CListCtrl http://support.microsoft.com/kb/173242/en-us
9.
得到listctrl的所有列的header字符串內(nèi)容
LVCOLUMN lvcol; char str[256]; int
nColNum; CString strColumnName[4];//假如有4列
nColNum = 0; lvcol.mask = LVCF_TEXT; lvcol.pszText =
str; lvcol.cchTextMax = 256; while(m_list.GetColumn(nColNum,
&lvcol)) { strColumnName[nColNum] =
lvcol.pszText; nColNum++; }
10. 使listctrl中一項(xiàng)可見(jiàn),即滾動(dòng)滾動(dòng)條
m_list.EnsureVisible(i, FALSE);
11. 得到listctrl列數(shù)
int
nHeadNum = m_list.GetHeaderCtrl()->GetItemCount();
12. 刪除所有列
方法一: while ( m_list.DeleteColumn (0))
因?yàn)槟銊h除了第一列后,后面的列會(huì)依次向上移動(dòng)。
方法二: int nColumns = 4; for (int i=nColumns-1;
i>=0; i--) m_list.DeleteColumn (i);
13. 得到單擊的listctrl的行列號(hào)
添加listctrl控件的NM_CLICK消息相應(yīng)函數(shù) void
CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
{ // 方法一: /* DWORD dwPos =
GetMessagePos(); CPoint point( LOWORD(dwPos), HIWORD(dwPos)
); m_list.ScreenToClient(&point);
LVHITTESTINFO lvinfo; lvinfo.pt = point;
lvinfo.flags = LVHT_ABOVE; int nItem =
m_list.SubItemHitTest(&lvinfo); if(nItem != -1)
{ CString strtemp;
strtemp.Format("單擊的是第%d行第%d列", lvinfo.iItem,
lvinfo.iSubItem); AfxMessageBox(strtemp);
} */ // 方法二: /*
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView->iItem != -1) { CString
strtemp;
strtemp.Format("單擊的是第%d行第%d列",
pNMListView->iItem, pNMListView->iSubItem);
AfxMessageBox(strtemp); } */ *pResult =
0; }
14. 判斷是否點(diǎn)擊在listctrl的checkbox上
添加listctrl控件的NM_CLICK消息相應(yīng)函數(shù) void
CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
{ DWORD dwPos = GetMessagePos(); CPoint point(
LOWORD(dwPos), HIWORD(dwPos) );
m_list.ScreenToClient(&point); LVHITTESTINFO
lvinfo; lvinfo.pt = point; lvinfo.flags =
LVHT_ABOVE; UINT nFlag; int nItem =
m_list.HitTest(point, &nFlag); //判斷是否點(diǎn)在checkbox上
if(nFlag == LVHT_ONITEMSTATEICON) {
AfxMessageBox("點(diǎn)在listctrl的checkbox上"); } *pResult =
0; }
15. 右鍵點(diǎn)擊listctrl的item彈出菜單
添加listctrl控件的NM_RCLICK消息相應(yīng)函數(shù) void
CTest6Dlg::OnRclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{ NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(pNMListView->iItem != -1) { DWORD dwPos =
GetMessagePos(); CPoint point( LOWORD(dwPos), HIWORD(dwPos)
); CMenu menu; VERIFY(
menu.LoadMenu( IDR_MENU1 ) ); CMenu* popup =
menu.GetSubMenu(0); ASSERT( popup != NULL
); popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y, this ); } *pResult = 0;
}
16.
item切換焦點(diǎn)時(shí)(包括用鍵盤和鼠標(biāo)切換item時(shí)),狀態(tài)的一些變化順序
添加listctrl控件的LVN_ITEMCHANGED消息相應(yīng)函數(shù) void
CTest6Dlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
{ NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
CString sTemp; if((pNMListView->uOldState &
LVIS_FOCUSED) == LVIS_FOCUSED &&
(pNMListView->uNewState & LVIS_FOCUSED) == 0)
{ sTemp.Format("%d losted
focus",pNMListView->iItem); } else
if((pNMListView->uOldState & LVIS_FOCUSED) == 0
&& (pNMListView->uNewState & LVIS_FOCUSED) ==
LVIS_FOCUSED) { sTemp.Format("%d got
focus",pNMListView->iItem); }
if((pNMListView->uOldState & LVIS_SELECTED) == LVIS_SELECTED
&& (pNMListView->uNewState & LVIS_SELECTED) ==
0) { sTemp.Format("%d losted
selected",pNMListView->iItem); } else
if((pNMListView->uOldState & LVIS_SELECTED) == 0
&& (pNMListView->uNewState & LVIS_SELECTED) ==
LVIS_SELECTED) { sTemp.Format("%d got
selected",pNMListView->iItem); } *pResult
= 0; }
17.
得到另一個(gè)進(jìn)程里的listctrl控件的item內(nèi)容
http://www.codeproject.com/threads/int64_memsteal.asp
18.
選中l(wèi)istview中的item
Q131284: How To Select a Listview Item
Programmatically http://support.microsoft.com/kb/131284/en-us
19.
如何在CListView中使用CListCtrl的派生類
http://www.codeguru.com/cpp/controls/listview/introduction/article.php/c919/
20. listctrl的subitem添加圖標(biāo)
m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES);
m_list.SetItem(..); //具體參數(shù)請(qǐng)參考msdn
21.
在CListCtrl顯示文件,并根據(jù)文件類型來(lái)顯示圖標(biāo)
網(wǎng)上找到的代碼,share BOOL CTest6Dlg::OnInitDialog()
{ CDialog::OnInitDialog(); HIMAGELIST
himlSmall; HIMAGELIST himlLarge; SHFILEINFO
sfi; char cSysDir[MAX_PATH]; CString strBuf;
memset(cSysDir, 0, MAX_PATH);
GetWindowsDirectory(cSysDir, MAX_PATH); strBuf =
cSysDir; sprintf(cSysDir, "%s", strBuf.Left(strBuf.Find("\\")+1));
himlSmall = (HIMAGELIST)SHGetFileInfo ((LPCSTR)cSysDir,
0, &sfi,
sizeof(SHFILEINFO),
SHGFI_SYSICONINDEX | SHGFI_SMALLICON ); himlLarge =
(HIMAGELIST)SHGetFileInfo((LPCSTR)cSysDir, 0,
&sfi,
sizeof(SHFILEINFO), SHGFI_SYSICONINDEX |
SHGFI_LARGEICON); if (himlSmall &&
himlLarge) { ::SendMessage(m_list.m_hWnd,
LVM_SETIMAGELIST, (WPARAM)LVSIL_SMALL,
(LPARAM)himlSmall); ::SendMessage(m_list.m_hWnd,
LVM_SETIMAGELIST, (WPARAM)LVSIL_NORMAL,
(LPARAM)himlLarge); } return TRUE; // return TRUE
unless you set the focus to a control } void
CTest6Dlg::AddFiles(LPCTSTR lpszFileName, BOOL bAddToDocument)
{ int nIcon = GetIconIndex(lpszFileName, FALSE,
FALSE); CString strSize; CFileFind filefind;
// get file size if
(filefind.FindFile(lpszFileName)) {
filefind.FindNextFile(); strSize.Format("%d",
filefind.GetLength()); } else
strSize = "0"; // split path and filename
CString strFileName = lpszFileName; CString strPath;
int nPos = strFileName.ReverseFind('\\'); if (nPos
!= -1) { strPath =
strFileName.Left(nPos); strFileName = strFileName.Mid(nPos +
1); } // insert to list int
nItem = m_list.GetItemCount(); m_list.InsertItem(nItem,
strFileName, nIcon); m_list.SetItemText(nItem, 1,
strSize); m_list.SetItemText(nItem, 2,
strFileName.Right(3)); m_list.SetItemText(nItem, 3,
strPath); } int CTest6Dlg::GetIconIndex(LPCTSTR
lpszPath, BOOL bIsDir, BOOL bSelected) { SHFILEINFO
sfi; memset(&sfi, 0, sizeof(sfi)); if
(bIsDir) { SHGetFileInfo(lpszPath,
FILE_ATTRIBUTE_DIRECTORY,
&sfi,
sizeof(sfi), SHGFI_SMALLICON | SHGFI_SYSICONINDEX
| SHGFI_USEFILEATTRIBUTES |(bSelected ?
SHGFI_OPENICON : 0)); return sfi.iIcon;
} else { SHGetFileInfo (lpszPath,
FILE_ATTRIBUTE_NORMAL,
&sfi,
sizeof(sfi), SHGFI_SMALLICON | SHGFI_SYSICONINDEX
| SHGFI_USEFILEATTRIBUTES | (bSelected ?
SHGFI_OPENICON : 0)); return sfi.iIcon;
} return -1; }
22. listctrl內(nèi)容進(jìn)行大數(shù)據(jù)量更新時(shí),避免閃爍
m_list.SetRedraw(FALSE); //更新內(nèi)容
m_list.SetRedraw(TRUE); m_list.Invalidate();
m_list.UpdateWindow(); 或者參考
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_cwnd.3a3a.setredraw.asp
23. listctrl排序
Q250614:How To Sort Items in a CListCtrl in Report View http://support.microsoft.com/kb/250614/en-us
24.
在listctrl中選中某個(gè)item時(shí)動(dòng)態(tài)改變其icon或bitmap
Q141834: How to change the icon or the bitmap of a CListCtrl item in Visual
C++ http://support.microsoft.com/kb/141834/en-us
How to change the icon or the bitmap of a
CListCtrl item in Visual C++
Article ID |
: |
141834 |
Last Review |
: |
June 2, 2005 |
Revision |
: |
3.0 |
This article was previously published under Q141834
NOTE: Microsoft Visual C++ NET (2002) supported both
the managed code model that is provided by the .NET Framework and the unmanaged
native Windows code model. The information in this article applies to unmanaged
Visual C++ code only.
T>
SUMMARY
This article shows how to change the icon or bitmap of a
CListCtrl item when it is selected.
MORE INFORMATION
When you initialize the CListCtrl by calling
CListCtrl::InsertItem(), you can pass in a value of I_IMAGECALLBACK for the
index of the image. This means that the system expects you to fill in the image
index when you get an LVN_GETDISPINFO notification. Inside of the handler for
LVN_GETDISPINFO, you can check if the item is selected and set the appropriate
image index.
Sample Code
BEGIN_MESSAGE_MAP(CTestView, CView) //{{AFX_MSG_MAP(CTestView) ON_WM_CREATE() //}}AFX_MSG_MAP ON_NOTIFY (LVN_GETDISPINFO, IDI_LIST, OnGetDispInfo) END_MESSAGE_MAP()
int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1;
// m_pImage is a CTestView's member variable of type CImageList* // create the CImageList with 16x15 images m_pImage = new CImageList(); VERIFY (m_pImage->Create (16, 15, TRUE, 0, 1)); CBitmap bm; // IDR_MAINFRAME is the toolbar bitmap in a default AppWizard // project. bm.LoadBitmap (IDR_MAINFRAME); // This will automatically parse the bitmap into nine images. m_pImage->Add (&bm, RGB (192, 192, 192));
// m_pList is CTestView's member variable of type CListCtrl* // create the CListCtrl. m_pList = new CListCtrl(); VERIFY (m_pList->Create (WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_EDITLABELS, CRect (0, 0, 400, 400), this, IDI_LIST)); // Create column. m_pList->InsertColumn (0, "Button Number", LVCFMT_LEFT, 100); // Associate CImageList with CListCtrl. m_pList->SetImageList (m_pImage, LVSIL_SMALL);
char szTemp[10]; for (int iCntr = 0; iCntr < 9; iCntr++) { wsprintf (szTemp, "%d", iCntr); m_pList->InsertItem (LVIF_IMAGE | LVIF_TEXT, iCntr, szTemp, 0, 0, I_IMAGECALLBACK, 0L); } return 0; }
void CTestView::OnGetDispInfo (NMHDR* pnmhdr, LRESULT* pResult) { LV_DISPINFO* pdi = (LV_DISPINFO *) pnmhdr;
// Fill in the LV_ITEM structure with the image info. // When an item is selected, the image is set to the first // image (the new bitmap on the toolbar). // When it is not selected, the image index is equal to the // item number (that is, 0=new, 1=open, 2=save, and so on.) if (LVIS_SELECTED == m_pList->GetItemState (pdi->item.iItem, LVIS_SELECTED)) pdi->item.iImage = 0; else pdi->item.iImage = pdi->item.iItem; }
CTestView::~CTestView() { // Clean up. delete m_pImage; delete m_pList; }
25.
在添加item后,再InsertColumn()后導(dǎo)致整列數(shù)據(jù)移動(dòng)的問(wèn)題
Q151897: CListCtrl::InsertColumn() Causes Column Data to Shift http://support.microsoft.com/kb/151897/en-us
26. 關(guān)于listctrl第一列始終居左的問(wèn)題
解決辦法:把第一列當(dāng)一個(gè)虛列,從第二列開(kāi)始插入列及數(shù)據(jù),最后刪除第一列。 具體解釋參閱 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/structures/lvcolumn.asp
27. 鎖定column header的拖動(dòng)
http://msdn.microsoft.com/msdnmag/issues/03/06/CQA/
28. 如何隱藏clistctrl的列
把需隱藏的列的寬度設(shè)為0,然后檢測(cè)當(dāng)該列為隱藏列時(shí),用上面第27點(diǎn)的鎖定column 的拖動(dòng)來(lái)實(shí)現(xiàn)
29. listctrl進(jìn)行大數(shù)據(jù)量操作時(shí),使用virtual
list
http://www.microsoft.com/msj/archive/S2061.aspx http://www.codeguru.com/cpp/controls/listview/advanced/article.php/c4151/ http://www.codeproject.com/listctrl/virtuallist.asp
30. 關(guān)于item只能顯示259個(gè)字符的問(wèn)題
解決辦法:需要在item上放一個(gè)edit。
31. 響應(yīng)在listctrl的column
header上的鼠標(biāo)右鍵單擊
Q125694: How To Find Out Which Listview Column Was Right-Clicked http://support.microsoft.com/kb/125694/en-us
32. 類似于windows資源管理器的listview
Q234310: How to implement a ListView control that is similar to Windows
Explorer by using DirLV.exe http://support.microsoft.com/kb/234310/en-us
33. 在ListCtrl中OnTimer只響應(yīng)兩次的問(wèn)題
Q200054: PRB: OnTimer() Is Not Called Repeatedly for a List Control http://support.microsoft.com/kb/200054/en-us
34.
以下為一些為實(shí)現(xiàn)各種自定義功能的listctrl派生類
(1) 拖放
http://www.codeproject.com/listctrl/dragtest.asp
在CListCtrl和CTreeCtrl間拖放
http://support.microsoft.com/kb/148738/en-us
(2) 多功能listctrl
支持subitem可編輯,圖標(biāo),radiobutton,checkbox,字符串改變顏色的類 http://www.codeproject.com/listctrl/quicklist.asp
支持排序,subitem可編輯,subitem圖標(biāo),subitem改變顏色的類
http://www.codeproject.com/listctrl/ReportControl.asp
(3) subitem中顯示超鏈接 http://www.codeproject.com/listctrl/CListCtrlLink.asp
(4) subitem的tooltip提示 http://www.codeproject.com/listctrl/ctooltiplistctrl.asp
(5) subitem中顯示進(jìn)度條
http://www.codeproject.com/listctrl/ProgressListControl.asp
http://www.codeproject.com/listctrl/napster.asp
http://www.codeguru.com/Cpp/controls/listview/article.php/c4187/
(6) 動(dòng)態(tài)改變subitem的顏色和背景色 http://www.codeproject.com/listctrl/highlightlistctrl.asp
http://www.codeguru.com/Cpp/controls/listbox/colorlistboxes/article.php/c4757/
(7) 類vb屬性對(duì)話框
http://www.codeproject.com/listctrl/propertylistctrl.asp
http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c995/
http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c1041/
(8) 選中subitem(只高亮選中的item)
http://www.codeproject.com/listctrl/SubItemSel.asp
http://www.codeproject.com/listctrl/ListSubItSel.asp
(9) 改變行高 http://www.codeproject.com/listctrl/changerowheight.asp
(10) 改變行顏色 http://www.codeproject.com/listctrl/coloredlistctrl.asp
(11) 可編輯subitem的listctrl
http://www.codeproject.com/listctrl/nirs2000.asp
http://www.codeproject.com/listctrl/editing_subitems_in_listcontrol.asp
(12) subitem可編輯,插入combobox,改變行顏色,subitem的tooltip提示
http://www.codeproject.com/listctrl/reusablelistcontrol.asp
(13) header
中允許多行字符串 http://www.codeproject.com/listctrl/headerctrlex.asp
(14) 插入combobox http://www.codeguru.com/Cpp/controls/listview/editingitemsandsubitem/article.php/c979/
(15) 添加背景圖片 http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c4173/
http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c983/
http://www.vchelp.net/vchelp/archive.asp?type_id=9&class_id=1&cata_id=1&article_id=1088&search_term=
(16) 自適應(yīng)寬度的listctrl http://www.codeproject.com/useritems/AutosizeListCtrl.asp
(17) 改變ListCtrl高亮?xí)r的顏色(默認(rèn)為藍(lán)色)
處理 NM_CUSTOMDRAW http://www.codeproject.com/listctrl/lvcustomdraw.asp
(18) 改變header顏色 http://www.pocketpcdn.com/articles/hdr_color.html
原文地址
http://blog.csdn.net/lixiaosan/archive/2006/04/07/653563.aspx
2007年9月13日
當(dāng)前流行的Windows操作系統(tǒng)能同時(shí)運(yùn)行幾個(gè)程序(獨(dú)立運(yùn)行的程序又稱之為進(jìn)程),對(duì)于同一個(gè)程序,它又可以分成若干個(gè)獨(dú)立的執(zhí)行流,我們稱之
為線程,線程提供了多任務(wù)處理的能力。用進(jìn)程和線程的觀點(diǎn)來(lái)研究軟件是當(dāng)今普遍采用的方法,進(jìn)程和線程的概念的出現(xiàn),對(duì)提高軟件的并行性有著重要的意義。
現(xiàn)在的大型應(yīng)用軟件無(wú)一不是多線程多任務(wù)處理,單線程的軟件是不可想象的。因此掌握多線程多任務(wù)設(shè)計(jì)方法對(duì)每個(gè)程序員都是必需要掌握的。本實(shí)例針對(duì)多線程
技術(shù)在應(yīng)用中經(jīng)常遇到的問(wèn)題,如線程間的通信、同步等,分別進(jìn)行探討,并利用多線程技術(shù)進(jìn)行線程之間的通信,實(shí)現(xiàn)了數(shù)字的簡(jiǎn)單排序。
一、 實(shí)現(xiàn)方法
1、理解線程
要講解線程,不得不說(shuō)一下進(jìn)程,進(jìn)程是應(yīng)用程序的執(zhí)行實(shí)例,每個(gè)進(jìn)程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它系統(tǒng)資源組成。進(jìn)程在運(yùn)行時(shí)創(chuàng)建的資源
隨著進(jìn)程的終止而死亡。線程的基本思想很簡(jiǎn)單,它是一個(gè)獨(dú)立的執(zhí)行流,是進(jìn)程內(nèi)部的一個(gè)獨(dú)立的執(zhí)行單元,相當(dāng)于一個(gè)子程序,它對(duì)應(yīng)于Visual
C++中的CwinThread類對(duì)象。單獨(dú)一個(gè)執(zhí)行程序運(yùn)行時(shí),缺省地包含的一個(gè)主線程,主線程以函數(shù)地址的形式出現(xiàn),提供程序的啟動(dòng)點(diǎn),如main
()或WinMain()函數(shù)等。當(dāng)主線程終止時(shí),進(jìn)程也隨之終止。根據(jù)實(shí)際需要,應(yīng)用程序可以分解成許多獨(dú)立執(zhí)行的線程,每個(gè)線程并行的運(yùn)行在同一進(jìn)程
中。
一個(gè)進(jìn)程中的所有線程都在該進(jìn)程的虛擬地址空間中,使用該進(jìn)程的全局變量和系統(tǒng)資源。操作系統(tǒng)給每個(gè)線程分配不同的CPU時(shí)間片,在某一個(gè)時(shí)刻,
CPU只執(zhí)行一個(gè)時(shí)間片內(nèi)的線程,多個(gè)時(shí)間片中的相應(yīng)線程在CPU內(nèi)輪流執(zhí)行,由于每個(gè)時(shí)間片時(shí)間很短,所以對(duì)用戶來(lái)說(shuō),仿佛各個(gè)線程在計(jì)算機(jī)中是并行處
理的。操作系統(tǒng)是根據(jù)線程的優(yōu)先級(jí)來(lái)安排CPU的時(shí)間,優(yōu)先級(jí)高的線程優(yōu)先運(yùn)行,優(yōu)先級(jí)低的線程則繼續(xù)等待。
線程被分為兩種:用戶界面線程和工作線程(又稱為后臺(tái)線程)。用戶界面線程通常用來(lái)處理用戶的輸入并響應(yīng)各種事件和消息,其實(shí),應(yīng)用程序的主執(zhí)行線程
CWinAPP對(duì)象就是一個(gè)用戶界面線程,當(dāng)應(yīng)用程序啟動(dòng)時(shí)自動(dòng)創(chuàng)建和啟動(dòng),同樣它的終止也意味著該程序的結(jié)束,進(jìn)程終止。工作線程用來(lái)執(zhí)行程序的后臺(tái)處
理任務(wù),比如計(jì)算、調(diào)度、對(duì)串口的讀寫操作等,它和用戶界面線程的區(qū)別是它不用從CWinThread類派生來(lái)創(chuàng)建,對(duì)它來(lái)說(shuō)最重要的是如何實(shí)現(xiàn)工作線程
任務(wù)的運(yùn)行控制函數(shù)。工作線程和用戶界面線程啟動(dòng)時(shí)要調(diào)用同一個(gè)函數(shù)的不同版本;最后需要讀者明白的是,一個(gè)進(jìn)程中的所有線程共享它們父進(jìn)程的變量,但同
時(shí)每個(gè)線程可以擁有自己的變量。
2、線程的管理和操作
(一)線程的啟動(dòng)
創(chuàng)建一個(gè)用戶界面線程,首先要從類CwinThread產(chǎn)生一個(gè)派生類,同時(shí)必須使用DECLARE_DYNCREATE和
IMPLEMENT_DYNCREATE來(lái)聲明和實(shí)現(xiàn)這個(gè)CwinThread派生類。第二步是根據(jù)需要重載該派生類的一些成員函數(shù)如:
ExitInstance()、InitInstance()、OnIdle()、PreTranslateMessage()等函數(shù)。最后調(diào)用
AfxBeginThread()函數(shù)的一個(gè)版本:CWinThread* AfxBeginThread( CRuntimeClass*
pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize =
0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs =
NULL )
啟動(dòng)該用戶界面線程,其中第一個(gè)參數(shù)為指向定義的用戶界面線程類指針變量,第二個(gè)參數(shù)為線程的優(yōu)先級(jí),第三個(gè)參數(shù)為線程所對(duì)應(yīng)的堆棧大小,第四個(gè)參數(shù)為線
程創(chuàng)建時(shí)的附加標(biāo)志,缺省為正常狀態(tài),如為CREATE_SUSPENDED則線程啟動(dòng)后為掛起狀態(tài)。
對(duì)于工作線程來(lái)說(shuō),啟動(dòng)一個(gè)線程,首先需要編寫一個(gè)希望與應(yīng)用程序的其余部分并行運(yùn)行的函數(shù)如Fun1(),接著定義一個(gè)指向CwinThread對(duì)
象的指針變量*pThread,調(diào)用AfxBeginThread(Fun1,param,priority)函數(shù),返回值賦給pThread變量的同時(shí)
一并啟動(dòng)該線程來(lái)執(zhí)行上面的Fun1()函數(shù),其中Fun1是線程要運(yùn)行的函數(shù)的名字,也既是上面所說(shuō)的控制函數(shù)的名字,param是準(zhǔn)備傳送給線程函數(shù)
Fun1的任意32位值,priority則是定義該線程的優(yōu)先級(jí)別,它是預(yù)定義的常數(shù),讀者可參考MSDN。
(二)線程的優(yōu)先級(jí)
以下的CwinThread類的成員函數(shù)用于線程優(yōu)先級(jí)的操作:
int GetThreadPriority();
BOOL SetThradPriority()(int nPriority);
上述的二個(gè)函數(shù)分別用來(lái)獲取和設(shè)置線程的優(yōu)先級(jí),這里的優(yōu)先級(jí),是相對(duì)于該線程所處的優(yōu)先權(quán)層次而言的,處于同一優(yōu)先權(quán)層次的線程,優(yōu)
先級(jí)高的線程先運(yùn)行;處于不同優(yōu)先權(quán)層次上的線程,誰(shuí)的優(yōu)先權(quán)層次高,誰(shuí)先運(yùn)行。至于優(yōu)先級(jí)設(shè)置所需的常數(shù),自己參考MSDN就可以了,要注意的是要想設(shè)
置線程的優(yōu)先級(jí),這個(gè)線程在創(chuàng)建時(shí)必須具有THREAD_SET_INFORMATION訪問(wèn)權(quán)限。對(duì)于線程的優(yōu)先權(quán)層次的設(shè)置,CwinThread類
沒(méi)有提供相應(yīng)的函數(shù),但是可以通過(guò)Win32 SDK函數(shù)GetPriorityClass()和SetPriorityClass()來(lái)實(shí)現(xiàn)。
(三)線程的懸掛和恢復(fù)
CWinThread類中包含了應(yīng)用程序懸掛和恢復(fù)它所創(chuàng)建的線程的函數(shù),其中SuspendThread()用來(lái)懸掛線程,暫停線程的執(zhí)行;
ResumeThread()用來(lái)恢復(fù)線程的執(zhí)行。如果你對(duì)一個(gè)線程連續(xù)若干次執(zhí)行SuspendThread(),則需要連續(xù)執(zhí)行相應(yīng)次的
ResumeThread()來(lái)恢復(fù)線程的運(yùn)行。
(四)結(jié)束線程
終止線程有三種途徑,線程可以在自身內(nèi)部調(diào)用AfxEndThread()來(lái)終止自身的運(yùn)行;可以在線程的外部調(diào)用BOOL
TerminateThread( HANDLE hThread, DWORD dwExitCode
)來(lái)強(qiáng)行終止一個(gè)線程的運(yùn)行,然后調(diào)用CloseHandle()函數(shù)釋放線程所占用的堆棧;第三種方法是改變?nèi)肿兞浚咕€程的執(zhí)行函數(shù)返回,則該線程
終止。下面以第三種方法為例,給出部分代碼:
////////////////////////////////////////////////////////////////
//////CtestView message handlers
/////Set to True to end thread
Bool bend=FALSE;//定義的全局變量,用于控制線程的運(yùn)行;
//The Thread Function;
UINT ThreadFunction(LPVOID pParam)//線程函數(shù)
{
while(!bend)
{
Beep(100,100);
Sleep(1000);
}
return 0;
}
/////////////////////////////////////////////////////////////
CwinThread *pThread;
HWND hWnd;
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
pThread=AfxBeginThread(ThradFunction,hWnd);//啟動(dòng)線程
pThread->m_bAutoDelete=FALSE;//線程為手動(dòng)刪除
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{
bend=TRUE;//改變變量,線程結(jié)束
WaitForSingleObject(pThread->m_hThread,INFINITE);//等待線程結(jié)束
delete pThread;//刪除線程
Cview::OnDestroy();
}
3、線程之間的通信
通常情況下,一個(gè)次級(jí)線程要為主線程完成某種特定類型的任務(wù),這就隱含著表示在主線程和次級(jí)線程之間需要建立一個(gè)通信的通道。一般情況下,有下面的幾
種方法實(shí)現(xiàn)這種通信任務(wù):使用全局變量(上一節(jié)的例子其實(shí)使用的就是這種方法)、使用事件對(duì)象、使用消息。這里我們主要介紹后兩種方法。
(一) 利用用戶定義的消息通信
在Windows程序設(shè)計(jì)中,應(yīng)用程序的每一個(gè)線程都擁有自己的消息隊(duì)列,甚至工作線程也不例外,這樣一來(lái),就使得線程之間利用消息來(lái)傳遞信息就變的
非常簡(jiǎn)單。首先用戶要定義一個(gè)用戶消息,如下所示:#define WM_USERMSG
WMUSER+100;在需要的時(shí)候,在一個(gè)線程中調(diào)用::PostMessage((HWND)param,WM_USERMSG,0,0)或
CwinThread::PostThradMessage()來(lái)向另外一個(gè)線程發(fā)送這個(gè)消息,上述函數(shù)的四個(gè)參數(shù)分別是消息將要發(fā)送到的目的窗口的句
柄、要發(fā)送的消息標(biāo)志符、消息的參數(shù)WPARAM和LPARAM。下面的代碼是對(duì)上節(jié)代碼的修改,修改后的結(jié)果是在線程結(jié)束時(shí)顯示一個(gè)對(duì)話框,提示線程結(jié)
束:
UINT ThreadFunction(LPVOID pParam)
{
while(!bend)
{
Beep(100,100);
Sleep(1000);
}
::PostMessage(hWnd,WM_USERMSG,0,0);
return 0;
}
////////WM_USERMSG消息的響應(yīng)函數(shù)為OnThreadended(WPARAM wParam,
LPARAM lParam)
LONG CTestView::OnThreadended(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Thread ended.");
Retrun 0;
}
上面的例子是工作者線程向用戶界面線程發(fā)送消息,對(duì)于工作者線程,如果它的設(shè)計(jì)模式也是消息驅(qū)動(dòng)的,那么調(diào)用者可以向它發(fā)送初始化、退
出、執(zhí)行某種特定的處理等消息,讓它在后臺(tái)完成。在控制函數(shù)中可以直接使用::GetMessage()這個(gè)SDK函數(shù)進(jìn)行消息分檢和處理,自己實(shí)現(xiàn)一個(gè)
消息循環(huán)。GetMessage()函數(shù)在判斷該線程的消息隊(duì)列為空時(shí),線程將系統(tǒng)分配給它的時(shí)間片讓給其它線程,不無(wú)效的占用CPU的時(shí)間,如果消息隊(duì)
列不為空,就獲取這個(gè)消息,判斷這個(gè)消息的內(nèi)容并進(jìn)行相應(yīng)的處理。
(二)用事件對(duì)象實(shí)現(xiàn)通信
在線程之間傳遞信號(hào)進(jìn)行通信比較復(fù)雜的方法是使用事件對(duì)象,用MFC的Cevent類的對(duì)象來(lái)表示。事件對(duì)象處于兩種狀態(tài)之一:有信號(hào)和無(wú)信號(hào),線程可以監(jiān)視處于有信號(hào)狀態(tài)的事件,以便在適當(dāng)?shù)臅r(shí)候執(zhí)行對(duì)事件的操作。上述例子代碼修改如下:
////////////////////////////////////////////////////////////////////
Cevent threadStart ,threadEnd;
UINT ThreadFunction(LPVOID pParam)
{
::WaitForSingleObject(threadStart.m_hObject,INFINITE);
AfxMessageBox("Thread start.");
while(!bend)
{
Beep(100,100);
Sleep(1000);
Int result=::WaitforSingleObject(threadEnd.m_hObject,0);
//等待threadEnd事件有信號(hào),無(wú)信號(hào)時(shí)線程在這里懸停
If(result==Wait_OBJECT_0)
Bend=TRUE;
}
::PostMessage(hWnd,WM_USERMSG,0,0);
return 0;
}
/////////////////////////////////////////////////////////////
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
threadStart.SetEvent();//threadStart事件有信號(hào)
pThread=AfxBeginThread(ThreadFunction,hWnd);//啟動(dòng)線程
pThread->m_bAutoDelete=FALSE;
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{
threadEnd.SetEvent();
WaitForSingleObject(pThread->m_hThread,INFINITE);
delete pThread;
Cview::OnDestroy();
}
運(yùn)行這個(gè)程序,當(dāng)關(guān)閉程序時(shí),才顯示提示框,顯示"Thread ended"。
4、線程之間的同步
前面我們講過(guò),各個(gè)線程可以訪問(wèn)進(jìn)程中的公共變量,所以使用多線程的過(guò)程中需要注意的問(wèn)題是如何防止兩個(gè)或兩個(gè)以上的線程同時(shí)訪問(wèn)同一個(gè)數(shù)據(jù),以免破
壞數(shù)據(jù)的完整性。保證各個(gè)線程可以在一起適當(dāng)?shù)膮f(xié)調(diào)工作稱為線程之間的同步。前面一節(jié)介紹的事件對(duì)象實(shí)際上就是一種同步形式。Visual
C++中使用同步類來(lái)解決操作系統(tǒng)的并行性而引起的數(shù)據(jù)不安全的問(wèn)題,MFC支持的七個(gè)多線程的同步類可以分成兩大類:同步對(duì)象
(CsyncObject、Csemaphore、Cmutex、CcriticalSection和Cevent)和同步訪問(wèn)對(duì)象
(CmultiLock和CsingleLock)。本節(jié)主要介紹臨界區(qū)(critical
section)、互斥(mutexe)、信號(hào)量(semaphore),這些同步對(duì)象使各個(gè)線程協(xié)調(diào)工作,程序運(yùn)行起來(lái)更安全。
(一) 臨界區(qū)
臨界區(qū)是保證在某一個(gè)時(shí)間只有一個(gè)線程可以訪問(wèn)數(shù)據(jù)的方法。使用它的過(guò)程中,需要給各個(gè)線程提供一個(gè)共享的臨界區(qū)對(duì)象,無(wú)論哪個(gè)線程占有臨界區(qū)對(duì)象,
都可以訪問(wèn)受到保護(hù)的數(shù)據(jù),這時(shí)候其它的線程需要等待,直到該線程釋放臨界區(qū)對(duì)象為止,臨界區(qū)被釋放后,另外的線程可以強(qiáng)占這個(gè)臨界區(qū),以便訪問(wèn)共享的數(shù)
據(jù)。臨界區(qū)對(duì)應(yīng)著一個(gè)CcriticalSection對(duì)象,當(dāng)線程需要訪問(wèn)保護(hù)數(shù)據(jù)時(shí),調(diào)用臨界區(qū)對(duì)象的Lock()成員函數(shù);當(dāng)對(duì)保護(hù)數(shù)據(jù)的操作完成
之后,調(diào)用臨界區(qū)對(duì)象的Unlock()成員函數(shù)釋放對(duì)臨界區(qū)對(duì)象的擁有權(quán),以使另一個(gè)線程可以?shī)Z取臨界區(qū)對(duì)象并訪問(wèn)受保護(hù)的數(shù)據(jù)。同時(shí)啟動(dòng)兩個(gè)線程,它
們對(duì)應(yīng)的函數(shù)分別為WriteThread()和ReadThread(),用以對(duì)公共數(shù)組組array[]操作,下面的代碼說(shuō)明了如何使用臨界區(qū)對(duì)象:
#include "afxmt.h"
int array[10],destarray[10];
CCriticalSection Section;
UINT WriteThread(LPVOID param)
{
Section.Lock();
for(int x=0;x<10;x++)
array[x]=x;
Section.Unlock();
}
UINT ReadThread(LPVOID param)
{
Section.Lock();
For(int x=0;x<10;x++)
Destarray[x]=array[x];
Section.Unlock();
}
上述代碼運(yùn)行的結(jié)果應(yīng)該是Destarray數(shù)組中的元素分別為1-9,而不是雜亂無(wú)章的數(shù),如果不使用同步,則不是這個(gè)結(jié)果,有興趣的讀者可以實(shí)驗(yàn)一下。
(二)互斥
互斥與臨界區(qū)很相似,但是使用時(shí)相對(duì)復(fù)雜一些,它不僅可以在同一應(yīng)用程序的線程間實(shí)現(xiàn)同步,還可以在不同的進(jìn)程間實(shí)現(xiàn)同步,從而實(shí)現(xiàn)資源的安全共享。
互斥與Cmutex類的對(duì)象相對(duì)應(yīng),使用互斥對(duì)象時(shí),必須創(chuàng)建一個(gè)CSingleLock或CMultiLock對(duì)象,用于實(shí)際的訪問(wèn)控制,因?yàn)檫@里的例
子只處理單個(gè)互斥,所以我們可以使用CSingleLock對(duì)象,該對(duì)象的Lock()函數(shù)用于占有互斥,Unlock()用于釋放互斥。實(shí)現(xiàn)代碼如下:
#include "afxmt.h"
int array[10],destarray[10];
CMutex Section;
UINT WriteThread(LPVOID param)
{
CsingleLock singlelock;
singlelock (&Section);
singlelock.Lock();
for(int x=0;x<10;x++)
array[x]=x;
singlelock.Unlock();
}
UINT ReadThread(LPVOID param)
{
CsingleLock singlelock;
singlelock (&Section);
singlelock.Lock();
For(int x=0;x<10;x++)
Destarray[x]=array[x];
singlelock.Unlock();
}
(三)信號(hào)量
信號(hào)量的用法和互斥的用法很相似,不同的是它可以同一時(shí)刻允許多個(gè)線程訪問(wèn)同一個(gè)資源,創(chuàng)建一個(gè)信號(hào)量需要用Csemaphore類聲明一個(gè)對(duì)象,一
旦創(chuàng)建了一個(gè)信號(hào)量對(duì)象,就可以用它來(lái)對(duì)資源的訪問(wèn)技術(shù)。要實(shí)現(xiàn)計(jì)數(shù)處理,先創(chuàng)建一個(gè)CsingleLock或CmltiLock對(duì)象,然后用該對(duì)象的
Lock()函數(shù)減少這個(gè)信號(hào)量的計(jì)數(shù)值,Unlock()反之。下面的代碼分別啟動(dòng)三個(gè)線程,執(zhí)行時(shí)同時(shí)顯示二個(gè)消息框,然后10秒后第三個(gè)消息框才得
以顯示。
/////////////////////////////////////////////////////////////////////////
Csemaphore *semaphore;
Semaphore=new Csemaphore(2,2);
HWND hWnd=GetSafeHwnd();
AfxBeginThread(threadProc1,hWnd);
AfxBeginThread(threadProc2,hWnd);
AfxBeginThread(threadProc3,hWnd);
UINT ThreadProc1(LPVOID param)
{
CsingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread1 had access","Thread1",MB_OK);
return 0;
}
UINT ThreadProc2(LPVOID param)
{
CSingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread2 had access","Thread2",MB_OK);
return 0;
}
UINT ThreadProc3(LPVOID param)
{
CsingleLock singelLock(semaphore);
singleLock.Lock();
Sleep(10000);
::MessageBox((HWND)param,"Thread3 had access","Thread3",MB_OK);
return 0;
}
二、 編程步驟
1、 啟動(dòng)Visual C++6.0,生成一個(gè)32位的控制臺(tái)程序,將該程序命名為"sequence"
2、 輸入要排續(xù)的數(shù)字,聲明四個(gè)子線程;
3、 輸入代碼,編譯運(yùn)行程序。
三、 程序代碼
//////////////////////////////////////////////////////////////////////////////////////
// sequence.cpp : Defines the entry point for the console application.
/*
主要用到的WINAPI線程控制函數(shù),有關(guān)詳細(xì)說(shuō)明請(qǐng)查看MSDN;
線程建立函數(shù):
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全屬性結(jié)構(gòu)指針,可為NULL;
DWORD dwStackSize, // 線程棧大小,若為0表示使用默認(rèn)值;
LPTHREAD_START_ROUTINE lpStartAddress, // 指向線程函數(shù)的指針;
LPVOID lpParameter, // 傳遞給線程函數(shù)的參數(shù),可以保存一個(gè)指針值;
DWORD dwCreationFlags, // 線程建立是的初始標(biāo)記,運(yùn)行或掛起;
LPDWORD lpThreadId // 指向接收線程號(hào)的DWORD變量;
);
對(duì)臨界資源控制的多線程控制的信號(hào)函數(shù):
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性結(jié)構(gòu)指針,可為NULL;
BOOL bManualReset, // 手動(dòng)清除信號(hào)標(biāo)記,TRUE在WaitForSingleObject后必須手動(dòng)//調(diào)用RetEvent清除信號(hào)。若為 FALSE則在WaitForSingleObject
//后,系統(tǒng)自動(dòng)清除事件信號(hào);
BOOL bInitialState, // 初始狀態(tài),TRUE有信號(hào),F(xiàn)ALSE無(wú)信號(hào);
LPCTSTR lpName // 信號(hào)量的名稱,字符數(shù)不可多于MAX_PATH;
//如果遇到同名的其他信號(hào)量函數(shù)就會(huì)失敗,如果遇
//到同類信號(hào)同名也要注意變化;
);
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全屬性結(jié)構(gòu)指針,可為NULL
BOOL bInitialOwner, // 當(dāng)前建立互斥量是否占有該互斥量TRUE表示占有,
//這樣其他線程就不能獲得此互斥量也就無(wú)法進(jìn)入由
//該互斥量控制的臨界區(qū)。FALSE表示不占有該互斥量
LPCTSTR lpName // 信號(hào)量的名稱,字符數(shù)不可多于MAX_PATH如果
//遇到同名的其他信號(hào)量函數(shù)就會(huì)失敗,
//如果遇到同類信號(hào)同名也要注意變化;
);
//初始化臨界區(qū)信號(hào),使用前必須先初始化
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // 臨界區(qū)變量指針
);
//阻塞函數(shù)
//如果等待的信號(hào)量不可用,那么線程就會(huì)掛起,直到信號(hào)可用
//線程才會(huì)被喚醒,該函數(shù)會(huì)自動(dòng)修改信號(hào),如Event,線程被喚醒之后
//Event信號(hào)會(huì)變得無(wú)信號(hào),Mutex、Semaphore等也會(huì)變。
DWORD WaitForSingleObject(
HANDLE hHandle, // 等待對(duì)象的句柄
DWORD dwMilliseconds // 等待毫秒數(shù),INFINITE表示無(wú)限等待
);
//如果要等待多個(gè)信號(hào)可以使用WaitForMutipleObject函數(shù)
*/
#include "stdafx.h"
#include "stdlib.h"
#include "memory.h"
HANDLE evtTerminate; //事件信號(hào),標(biāo)記是否所有子線程都執(zhí)行完
/*
下面使用了三種控制方法,你可以注釋其中兩種,使用其中一種。
注意修改時(shí)要連帶修改臨界區(qū)PrintResult里的相應(yīng)控制語(yǔ)句
*/
HANDLE evtPrint; //事件信號(hào),標(biāo)記事件是否已發(fā)生
//CRITICAL_SECTION csPrint; //臨界區(qū)
//HANDLE mtxPrint; //互斥信號(hào),如有信號(hào)表明已經(jīng)有線程進(jìn)入臨界區(qū)并擁有此信號(hào)
static long ThreadCompleted = 0;
/*用來(lái)標(biāo)記四個(gè)子線程中已完成線程的個(gè)數(shù),當(dāng)一個(gè)子線程完成時(shí)就對(duì)ThreadCompleted進(jìn)行加一操作,
要使用InterlockedIncrement(long* lpAddend)和InterlockedDecrement(long*
lpAddend)進(jìn)行加減操作*/
//下面的結(jié)構(gòu)是用于傳送排序的數(shù)據(jù)給各個(gè)排序子線程
struct MySafeArray
{
long* data;
int iLength;
};
//打印每一個(gè)線程的排序結(jié)果
void PrintResult(long* Array, int iLength, const char* HeadStr = "sort");
//排序函數(shù)
unsigned long __stdcall BubbleSort(void* theArray); //冒泡排序
unsigned long __stdcall SelectSort(void* theArray); //選擇排序
unsigned long __stdcall HeapSort(void* theArray); //堆排序
unsigned long __stdcall InsertSort(void* theArray); //插入排序
/*以上四個(gè)函數(shù)的聲明必須適合作為一個(gè)線程函數(shù)的必要條件才可以使用CreateThread
建立一個(gè)線程。
(1)調(diào)用方法必須是__stdcall,即函數(shù)參數(shù)壓棧順序由右到左,而且由函數(shù)本身負(fù)責(zé)
棧的恢復(fù), C和C++默認(rèn)是__cdecl, 所以要顯式聲明是__stdcall
(2)返回值必須是unsigned long
(3)參數(shù)必須是一個(gè)32位值,如一個(gè)指針值或long類型
(4) 如果函數(shù)是類成員函數(shù),必須聲明為static函數(shù),在CreateThread時(shí)函數(shù)指針有特殊的寫法。如下(函數(shù)是類CThreadTest的成員函數(shù)中):
static unsigned long _stdcall MyThreadFun(void* pParam);
handleRet = CreateThread(NULL, 0, &CThreadTestDlg::MyThreadFun, NULL, 0, &ThreadID);
之所以要聲明為static是由于,該函數(shù)必須要獨(dú)立于對(duì)象實(shí)例來(lái)使用,即使沒(méi)有聲明實(shí)例也可以使用。*/
int QuickSort(long* Array, int iLow, int iHigh); //快速排序
int main(int argc, char* argv[])
{
long data[] = {123,34,546,754,34,74,3,56};
int iDataLen = 8;
//為了對(duì)各個(gè)子線程分別對(duì)原始數(shù)據(jù)進(jìn)行排序和保存排序結(jié)果
//分別分配內(nèi)存對(duì)data數(shù)組的數(shù)據(jù)進(jìn)行復(fù)制
long *data1, *data2, *data3, *data4, *data5;
MySafeArray StructData1, StructData2, StructData3, StructData4;
data1 = new long[iDataLen];
memcpy(data1, data, iDataLen << 2); //把data中的數(shù)據(jù)復(fù)制到data1中
//內(nèi)存復(fù)制 memcpy(目標(biāo)內(nèi)存指針, 源內(nèi)存指針, 復(fù)制字節(jié)數(shù)), 因?yàn)閘ong的長(zhǎng)度
//為4字節(jié),所以復(fù)制的字節(jié)數(shù)為iDataLen << 2, 即等于iDataLen*4
StructData1.data = data1;
StructData1.iLength = iDataLen;
data2 = new long[iDataLen];
memcpy(data2, data, iDataLen << 2);
StructData2.data = data2;
StructData2.iLength = iDataLen;
data3 = new long[iDataLen];
memcpy(data3, data, iDataLen << 2);
StructData3.data = data3;
StructData3.iLength = iDataLen;
data4 = new long[iDataLen];
memcpy(data4, data, iDataLen << 2);
StructData4.data = data4;
StructData4.iLength = iDataLen;
data5 = new long[iDataLen];
memcpy(data5, data, iDataLen << 2);
unsigned long TID1, TID2, TID3, TID4;
//對(duì)信號(hào)量進(jìn)行初始化
evtTerminate = CreateEvent(NULL, FALSE, FALSE, "Terminate");
evtPrint = CreateEvent(NULL, FALSE, TRUE, "PrintResult");
//分別建立各個(gè)子線程
CreateThread(NULL, 0, &BubbleSort, &StructData1, NULL, &TID1);
CreateThread(NULL, 0, &SelectSort, &StructData2, NULL, &TID2);
CreateThread(NULL, 0, &HeapSort, &StructData3, NULL, &TID3);
CreateThread(NULL, 0, &InsertSort, &StructData4, NULL, &TID4);
//在主線程中執(zhí)行行快速排序,其他排序在子線程中執(zhí)行
QuickSort(data5, 0, iDataLen - 1);
PrintResult(data5, iDataLen, "Quick Sort");
WaitForSingleObject(evtTerminate, INFINITE); //等待所有的子線程結(jié)束
//所有的子線程結(jié)束后,主線程才可以結(jié)束
delete[] data1;
delete[] data2;
delete[] data3;
delete[] data4;
CloseHandle(evtPrint);
return 0;
}
/*
冒泡排序思想(升序,降序同理,后面的算法一樣都是升序):從頭到尾對(duì)數(shù)據(jù)進(jìn)行兩兩比較進(jìn)行交換,小的放前大的放后。這樣一次下來(lái),最大的元素就會(huì)被交換的最后,然后下一次
循環(huán)就不用對(duì)最后一個(gè)元素進(jìn)行比較交換了,所以呢每一次比較交換的次數(shù)都比上一次循環(huán)的次數(shù)少一,這樣N次之后數(shù)據(jù)就變得升序排列了*/
unsigned long __stdcall BubbleSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
int i, j=0;
long swap;
for (i = iLength-1; i >0; i--)
{
for(j = 0; j < i; j++)
{
if(Array[j] >Array[j+1]) //前比后大,交換
{
swap = Array[j];
Array[j] = Array[j+1];
Array[j+1] = swap;
}
}
}
PrintResult(Array, iLength, "Bubble Sort"); //向控制臺(tái)打印排序結(jié)果
InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線程都已執(zhí)行完
//若都執(zhí)行完則設(shè)置程序結(jié)束信號(hào)量
return 0;
}
/*選擇排序思想:每一次都從無(wú)序的數(shù)據(jù)中找出最小的元素,然后和前面已經(jīng)有序的元素序列的后一個(gè)元素進(jìn)行交換,這樣整個(gè)源序列就會(huì)分成兩部分,前面一部
分是已經(jīng)排好序的有序序列,后面一部分是無(wú)序的,用于選出最小的元素。循環(huán)N次之后,前面的有序序列加長(zhǎng)到跟源序列一樣長(zhǎng),后面的無(wú)序部分長(zhǎng)度變?yōu)?,排
序就完成了。*/
unsigned long __stdcall SelectSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
long lMin, lSwap;
int i, j, iMinPos;
for(i=0; i < iLength-1; i++)
{
lMin = Array[i];
iMinPos = i;
for(j=i + 1; j <= iLength-1; j++) //從無(wú)序的元素中找出最小的元素
{
if(Array[j] < lMin)
{
iMinPos = j;
lMin = Array[j];
}
}
//把選出的元素交換拼接到有序序列的最后
lSwap = Array[i];
Array[i] = Array[iMinPos];
Array[iMinPos] = lSwap;
}
PrintResult(Array, iLength, "Select Sort"); //向控制臺(tái)打印排序結(jié)果
InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate);//檢查是否其他線程都已執(zhí)行完
//若都執(zhí)行完則設(shè)置程序結(jié)束信號(hào)量
return 0;
}
/*堆排序思想:堆:數(shù)據(jù)元素從1到N排列成一棵二叉樹(shù),而且這棵樹(shù)的每一個(gè)子樹(shù)的根都是該樹(shù)中的元素的最小或最大的元素這樣如果一個(gè)無(wú)序數(shù)據(jù)集合是一個(gè)
堆那么,根元素就是最小或最大的元素堆排序就是不斷對(duì)剩下的數(shù)據(jù)建堆,把最小或最大的元素析透出來(lái)。下面的算法,就是從最后一個(gè)元素開(kāi)始,依據(jù)一個(gè)節(jié)點(diǎn)比
父節(jié)點(diǎn)數(shù)值大的原則對(duì)所有元素進(jìn)行調(diào)整,這樣調(diào)整一次就形成一個(gè)堆,第一個(gè)元素就是最小的元素。然后再對(duì)剩下的無(wú)序數(shù)據(jù)再進(jìn)行建堆,注意這時(shí)后面的無(wú)序數(shù)
據(jù)元素的序數(shù)都要改變,如第一次建堆后,第二個(gè)元素就會(huì)變成堆的第一個(gè)元素。*/
unsigned long __stdcall HeapSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
int i, j, p;
long swap;
for(i=0; i {
for(j = iLength - 1; j>i; j--) //從最后倒數(shù)上去比較字節(jié)點(diǎn)和父節(jié)點(diǎn)
{
p = (j - i - 1)/2 + i; //計(jì)算父節(jié)點(diǎn)數(shù)組下標(biāo)
//注意到樹(shù)節(jié)點(diǎn)序數(shù)跟數(shù)組下標(biāo)不是等同的,因?yàn)榻ǘ训脑貍€(gè)數(shù)逐個(gè)遞減
if(Array[j] < Array[p]) //如果父節(jié)點(diǎn)數(shù)值大則交換父節(jié)點(diǎn)和字節(jié)點(diǎn)
{
swap = Array[j];
Array[j] = Array[p];
Array[p] = swap;
}
}
}
PrintResult(Array, iLength, "Heap Sort"); //向控制臺(tái)打印排序結(jié)果
InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線程都已執(zhí)行完
//若都執(zhí)行完則設(shè)置程序結(jié)束信號(hào)量
return 0;
}
/*插入排序思想:把源數(shù)據(jù)序列看成兩半,前面一半是有序的,后面一半是無(wú)序的,把無(wú)序的數(shù)據(jù)從頭到尾逐個(gè)逐個(gè)的插入到前面的有序數(shù)據(jù)中,使得有序的數(shù)據(jù)的個(gè)數(shù)不斷增大,同時(shí)無(wú)序的數(shù)據(jù)個(gè)數(shù)就越來(lái)越少,最后所有元素都會(huì)變得有序。*/
unsigned long __stdcall InsertSort(void* theArray)
{
long* Array = ((MySafeArray*)theArray)->data;
int iLength = ((MySafeArray*)theArray)->iLength;
int i=1, j=0;
long temp;
for(i=1; i {
temp = Array[i]; //取出序列后面無(wú)序數(shù)據(jù)的第一個(gè)元素值
for(j=i; j>0; j--) //和前面的有序數(shù)據(jù)逐個(gè)進(jìn)行比較找出合適的插入位置
{
if(Array[j - 1] >temp) //如果該元素比插入值大則后移
Array[j] = Array[j - 1];
else //如果該元素比插入值小,那么該位置的后一位就是插入元素的位置
break;
}
Array[j] = temp;
}
PrintResult(Array, iLength, "Insert Sort"); //向控制臺(tái)打印排序結(jié)果
InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線程都已執(zhí)行完
//若都執(zhí)行完則設(shè)置程序結(jié)束信號(hào)量
return 0;
}
/*快速排序思想:快速排序是分治思想的一種應(yīng)用,它先選取一個(gè)支點(diǎn),然后把小于支點(diǎn)的元素交換到支點(diǎn)的前邊,把大于支點(diǎn)的元素交換到支點(diǎn)的右邊。然后再對(duì)支點(diǎn)左邊部分和右
邊部分進(jìn)行同樣的處理,這樣若干次之后,數(shù)據(jù)就會(huì)變得有序。下面的實(shí)現(xiàn)使用了遞歸
建立兩個(gè)游標(biāo):iLow,iHigh;iLow指向序列的第一個(gè)元素,iHigh指向最后一個(gè)先選第一個(gè)元素作為支點(diǎn),并把它的值存貯在一個(gè)輔助變量里。
那么第一個(gè)位置就變?yōu)榭詹⒖梢苑胖闷渌脑亍?
這樣從iHigh指向的元素開(kāi)始向前移動(dòng)游標(biāo),iHigh查找比支點(diǎn)小的元素,如果找到,則把它放置到空置了的位置(現(xiàn)在是第一個(gè)位置),然后iHigh
游標(biāo)停止移動(dòng),這時(shí)iHigh指向的位置被空置,然后移動(dòng)iLow游標(biāo)尋找比支點(diǎn)大的元素放置到iHigh指向的空置的位置,如此往復(fù)直到iLow與
iHigh相等。最后使用遞歸對(duì)左右兩部分進(jìn)行同樣處理*/
int QuickSort(long* Array, int iLow, int iHigh)
{
if(iLow >= iHigh) return 1; //遞歸結(jié)束條件
long pivot = Array[iLow];
int iLowSaved = iLow, iHighSaved = iHigh; //保未改變的iLow,iHigh值保存起來(lái)
while (iLow < iHigh)
{
while (Array[iHigh] >= pivot && iHigh >iLow) //尋找比支點(diǎn)大的元素
iHigh -- ;
Array[iLow] = Array[iHigh]; //把找到的元素放置到空置的位置
while (Array[iLow] < pivot && iLow < iHigh) //尋找比支點(diǎn)小的元素
iLow ++ ;
Array[iHigh] = Array[iLow]; //把找到的元素放置到空置的位置
}
Array[iLow] = pivot; //把支點(diǎn)值放置到支點(diǎn)位置,這時(shí)支點(diǎn)位置是空置的
//對(duì)左右部分分別進(jìn)行遞歸處理
QuickSort(Array, iLowSaved, iHigh-1);
QuickSort(Array, iLow+1, iHighSaved);
return 0;
}
//每一個(gè)線程都要使用這個(gè)函數(shù)進(jìn)行輸出,而且只有一個(gè)顯示器,產(chǎn)生多個(gè)線程
//競(jìng)爭(zhēng)對(duì)控制臺(tái)的使用權(quán)。
void PrintResult(long* Array, int iLength, const char* HeadStr)
{
WaitForSingleObject(evtPrint, INFINITE); //等待事件有信號(hào)
//EnterCriticalSection(&csPrint); //標(biāo)記有線程進(jìn)入臨界區(qū)
//WaitForSingleObject(mtxPrint, INFINITE); //等待互斥量空置(沒(méi)有線程擁有它)
int i;
printf("%s: ", HeadStr);
for (i=0; i {
printf("%d,", Array[i]);
Sleep(100); //延時(shí)(可以去掉)
/*只是使得多線程對(duì)臨界區(qū)訪問(wèn)的問(wèn)題比較容易看得到
如果你把臨界控制的語(yǔ)句注釋掉,輸出就會(huì)變得很凌亂,各個(gè)排序的結(jié)果會(huì)
分插間隔著輸出,如果不延時(shí)就不容易看到這種不對(duì)臨界區(qū)控制的結(jié)果
*/
}
printf("%d\n", Array[i]);
SetEvent(evtPrint); //把事件信號(hào)量恢復(fù),變?yōu)橛行盘?hào)
}
四、 小結(jié)
對(duì)復(fù)雜的應(yīng)用程序來(lái)說(shuō),線程的應(yīng)用給應(yīng)用程序提供了高效、快速、安全的數(shù)據(jù)處理能力。本實(shí)例講述了線程處理中經(jīng)常遇到的問(wèn)題,希望對(duì)讀者朋友有一定的幫助,起到拋磚引玉的作用。
轉(zhuǎn)自(http://hi.baidu.com/laodun/blog/item/8ab8f3241af3f7318644f916.html)
2007年4月17日
需要構(gòu)造器嗎?
數(shù)據(jù)成員是private的嗎?它可以是const的嗎?
需要默認(rèn)構(gòu)造器嗎?
是不是每個(gè)構(gòu)造器初始化了所有成員?
需要析構(gòu)器嗎?它需要虛化嗎?
需要拷貝構(gòu)造器嗎?
需要assigment operator嗎?它能正確自賦值嗎?
需要關(guān)系操作符嗎?
在函數(shù)形參上使用了const嗎?在成員函數(shù)之后呢?
刪除數(shù)組成員時(shí)用delete []嗎?
2007年4月13日
1.賦值(=),下標(biāo)([]),調(diào)用( () )和成員訪問(wèn)箭頭(->)等操作符必須定義為成員,將這些定義為非成員函數(shù)將在編譯時(shí)標(biāo)記為錯(cuò)誤
2.像賦值一樣,復(fù)合賦值操作符通常應(yīng)定義為類的成員,與賦值不同的是,不一定非得這樣做,如果定義非成員復(fù)合賦值操作符,不會(huì)出現(xiàn)編譯錯(cuò)誤. 3.改變對(duì)象狀態(tài)或與給頂類型緊密聯(lián)系的其他一些操作符,如自增,自減和自解引用,通常應(yīng)定義為類成員 4.對(duì)稱的操作符號(hào),如算術(shù)操作符,相等操作符,關(guān)系操作符和位操作符,最好定義為普通非成員函數(shù).
既可以在函數(shù)聲明也可以在函數(shù)定義中指定默認(rèn)實(shí)參.但是在一個(gè)文件中,只能為一個(gè)形參指定默認(rèn)實(shí)參一次下面的例子是錯(cuò)誤的, //ff.h int ff(int=0); //ff.cc #include "ff.h" int ff(int i=0){}//error 如果在函數(shù)定義的形參表中提供默認(rèn)實(shí)參,那么只有在包含該函數(shù)定義的源文件中調(diào)用該函數(shù)時(shí),默認(rèn)實(shí)參才是有效的.
2007年4月10日
有些成員必須在構(gòu)造函數(shù)初始化列表中進(jìn)行初始化.對(duì)于這樣的成員,在構(gòu)造函數(shù)函數(shù)體中對(duì)它們賦值不起作用.必須使用初始化列表的情況有以下幾種: 1.沒(méi)有默認(rèn)構(gòu)造函數(shù)的類類型的成員(這是因?yàn)闃?gòu)造函數(shù)分為兩個(gè)階段:1.初始化階段,2.普通計(jì)算階段.初始化階段發(fā)生在計(jì)算階段開(kāi)始之前) 2.成員為const或引用類型.
2007年4月9日
可以聲明一個(gè)類而不定義它 class Screen;//declaration of the Screen class 這個(gè)聲明,有時(shí)候被稱為前向聲明(forward declaration),在程序中引入了類類型的Screen.在聲明之后,定義之前,類Screen是一個(gè)不完全類型(incompete type),即已知Screen是一個(gè)類型,但不知道包含哪些成員. 不完全類型只能以有限方式使用,不能定義該類型的對(duì)象,不完全類型只能用于定義指向該類型的指針及引用,或者用于聲明(而不是定義)使用該類型作為形參類型或返回類型的函數(shù).
2007年3月9日
PostMessage
只是把消息放入隊(duì)列,不管其他程序是否處理都返回,然后繼續(xù)執(zhí)行
;
而
SendMessage
必須等待其他程序處理消息后才返回,繼續(xù)執(zhí)行。
PostMessage
的返回值表示
PostMessage
函數(shù)執(zhí)行是否正確
;
而
SendMessage
的返回值表示其他程序處理消息后的返回值。
使用這兩個(gè)發(fā)送消息函數(shù)的最重要的是要看你的程序是否要對(duì)消息的滯后性關(guān)注否
,PostMessage
會(huì)造成消息的滯后性
,
而
SendMessage
則不會(huì)
,
但如果
SendMessage
消息處理失敗
,
則會(huì)造成程序停止
!
轉(zhuǎn)載自http://blog.csdn.net/Image_Graphics/archive/2006/11/22/1405436.aspx
1. 怎樣使用MFC發(fā)送一個(gè)消息用MFC發(fā)送一個(gè)消息的方法是, ????首先,應(yīng)獲取接收消息的CWnd類對(duì)象的指針; ????然后,調(diào)用CWnd的成員函數(shù)SendMessage( )。 ????????LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam); ????????pWnd指針指向目標(biāo)CWnd類對(duì)象。變量Msg是消息,wParam和lParam變量包含消息的參數(shù),如鼠標(biāo)單擊哪里或選擇了什么菜單項(xiàng)。目標(biāo)窗口返回的消息結(jié)果放在變量Res中。 ????????發(fā)送消息到一個(gè)沒(méi)有CWnd類對(duì)象的窗口,可以用下列目標(biāo)窗口的句柄直接調(diào)用Windows API: ????????LRESULT Res=::SendMessage(HWND hWnd, UINT Msg,? WPARAM wParam, LPARAM lParam); ????????這里的hWnd是目標(biāo)窗口的句柄。 2. 怎樣用MFC寄送一個(gè)消息 ????用MFC寄送一個(gè)消息與發(fā)送一個(gè)消息幾乎相同,但寄送時(shí)用PostMessage( ) ,而不是用SendMessage( );返回值Res也不一樣,Res不是一個(gè)由目標(biāo)窗口返回的值,而是一個(gè)布爾值,用來(lái)表示消息是否成功地放到消息隊(duì)列中。 3. 檢索一個(gè)寄送消息 ????正常情況下,一旦消息被寄送后,應(yīng)用程序在后臺(tái)發(fā)送它。但是在特殊情況下,需要你自己去刪除一個(gè)消息,例如想在應(yīng)用程序接收到某種消息之前停止應(yīng)用程序。有兩種方法可以從應(yīng)用程序消息隊(duì)列中刪除一個(gè)消息,但這兩種方法都沒(méi)有涉及MFC。 ■ 第一種方法:在不干擾任何事情之下窺視消息隊(duì)列,看看一個(gè)消息是否在那里。 ????BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ; ■ 第二種方法:實(shí)際上是等待,一直等到一個(gè)新的消息到達(dá)隊(duì)列為止,然后刪除并返回該消息。 ????BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); ????在這兩種方法中,變量hWnd指定要截獲消息的窗口,如果該變量設(shè)為NULL,所有窗口消息將被截獲。wMsgFilterMin和wMsgFilterMax變量與SendMessage( )中的變量Msg相對(duì)應(yīng),指定查看消息的范圍。如果用"0,0",則所有的消息都將被截獲。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,則所有鍵盤或鼠標(biāo)的消息將被截獲。wRemoveMsg變量指定PeekMessage( )是否應(yīng)該真正地從隊(duì)列中刪除該消息。(GetMessage( )總是刪除消息)。該變量可以取兩個(gè)值: ????■ PM_REMOVE,PeekMessage( )將刪除消息。 ????■ PM_NOREMOVE,PeekMessage( )將把消息留在隊(duì)列里,并返回它的一個(gè)拷貝。 ????當(dāng)然,如果把消息留在消息隊(duì)列中,然后再次調(diào)用PeekMessage( )查看相同類型的消息,則將返回完全相同的消息。 ????lpMsg變量是一個(gè)指向MSG結(jié)構(gòu)的指針,MSG包含檢索到的消息。 ????typedef struct tagMSG { ????????????????????????HWND hwnd; // window handle message is intended for ????????????????????????UINT message; ????????????????????????WPARAM wParam; ????????????????????????LPARAM lParam; ????????????????????????DWORD time; // the time the message was put in the queue ????????????????????????POINT pt; // the location of the mouse cursor when the ?????????????????????????????????????? // message was put in the queue ????????????????????????} MSG; 4. MFC怎樣接收一個(gè)寄送的消息 ??? MFC處理一個(gè)寄送和發(fā)送消息的唯一明顯不同是寄送的消息要在應(yīng)用程序的消息隊(duì)列中花費(fèi)一些時(shí)間。在消息泵(message pump)彈出它之前,它要一直在隊(duì)列中。 ??? 消息泵 ??? MFC應(yīng)用程序中的消息泵在CWinApp的成員函數(shù)Run()中。應(yīng)用程序開(kāi)始運(yùn)行時(shí),Run()就被調(diào)用,Run()把時(shí)間分割成兩部分。一部分用來(lái)執(zhí)行后臺(tái)處理,如取消臨時(shí)CWnd對(duì)象;另一部分用來(lái)檢查消息隊(duì)列。當(dāng)一個(gè)新的消息進(jìn)來(lái)時(shí),Run()抽取它—即用GetMessage( )從隊(duì)列中取出該消息,運(yùn)行兩個(gè)消息翻譯函數(shù),然后用DispatchMessage( )函數(shù)調(diào)用該消息預(yù)期的目標(biāo)窗口進(jìn)程。 ??? 消息泵調(diào)用的兩個(gè)翻譯函數(shù)是PreTranslateMessage( )和::TranslateMessage( )。目標(biāo)窗口的MFC類可調(diào)用reTranslateMessage在發(fā)送消息給它之前進(jìn)行消息翻譯,例如,CFrameWnd用PreTranslateMessage( )將加速鍵(如,Ctrl+S存儲(chǔ)文件)轉(zhuǎn)換為命令消息。翻譯前的消息通常被處理掉,而翻譯后的消息(如果有的話)將被重新寄送到隊(duì)列里。::TranslateMessage是一個(gè)窗口函數(shù),將原始鍵碼轉(zhuǎn)換為鍵字符。消息一旦被DispatchMessage()發(fā)送,MFC處理它就像處理SendMessage()發(fā)送的消息一樣。 5. MFC怎樣處理一個(gè)接收到的消息 ??? 處理接收到的消息的目的非常簡(jiǎn)單:將消息指向一個(gè)函數(shù),該函數(shù)通過(guò)消息中的消息標(biāo)識(shí)符處理它。非MFC窗口用簡(jiǎn)單的case語(yǔ)句來(lái)實(shí)現(xiàn)該目標(biāo),每個(gè)case語(yǔ)句執(zhí)行一些函數(shù),或調(diào)用其他一些函數(shù)。 ??? MainWndProc(HWND hWnd, UINT message, W PARAM wParam,LPARAM lParam) ??? { ??????? switch(message) ??????? { ??????? case WM_CREATE: ??????????? : : : ??????? break; ??????? case WM_PAINT: ??????????? : : : ??????? break; ??????? default: ??????? return(DefWindowProc(hWnd,message,wParam,lParam)); ??????? } ??????? return(NULL); ??? } ??? 任何遺漏的消息將被傳輸?shù)揭粋€(gè)默認(rèn)的消息處理函數(shù),但是,case語(yǔ)句不能很好地適應(yīng)C++和封裝技術(shù)。在C++環(huán)境中,要求消息被一個(gè)專門處理該類型消息的類的成員函數(shù)處理。因此,MFC不采用case語(yǔ)句,而采用更加復(fù)雜和回旋的方法。但它允許用私有類處理消息,而只需做下面三件事情: ??? ■ 從將要接收消息的CWnd類對(duì)象派生類(對(duì)于命令消息是CCmdTarget)。 ??? ■ 在派生類中寫一個(gè)處理消息的成員函數(shù)。 ??? ■ 在類中定義一個(gè)查找表(叫做消息映像),該表具有成員函數(shù)的條目和它要處理的消息的標(biāo)識(shí)符。 ??? 然后,MFC依次調(diào)用下面的函數(shù),指引輸入消息到處理函數(shù)。 ??? 1) AfxWndProc( )接收消息,尋找消息所屬的CWnd對(duì)象,然后調(diào)用AfxCallWndProc( )。 ??? 2) AfxCallWndProc( )存儲(chǔ)消息(消息標(biāo)識(shí)符和參數(shù))供未來(lái)參考,然后調(diào)用WindowProc( )。 ??? 3) WindowProc( ) 發(fā)送消息給OnWndMsg( ) ,然后,如果消息未被處理,則發(fā)送給DefWindowproc( )。 ??? 4) OnWndMsg( )要么為WM_COMMAND消息調(diào)用OnCommand( ),要么為WM_NOTIFY消息調(diào)用OnNotify( )。任何被遺漏的消息都將是一個(gè)窗口消息。OnWndMsg( )搜索類的消息映像,以找到一個(gè)能處理任何窗口消息的處理函數(shù)。如果OnWndMsg( )不能找到這樣的處理函數(shù),則把消息返回到WindowProc( ),由它將消息發(fā)送給DefWindowProc( )。 ??? 5) OnCommand()查看這是不是一個(gè)控件通知(lParam不是NULL);如果它是,OnCommand( )就試圖將消息映射到制造通知的控件;如果它不是一個(gè)控件通知,或者控件拒絕映射的消息,OnCommand( )就調(diào)用OnCmdMsg( )。 ??? 6) OnNotify( )也試圖將消息映射到制造通知的控件;如果映射不成功, OnNotify( )就調(diào)用相同的OnCmdMsg( )函數(shù)。 ??? 7) 根據(jù)接收消息的類,OnCmdMsg( )將在一個(gè)稱為命令傳遞(Command Routing)的過(guò)程中潛在地傳遞命令消息和控件通知。例如,如果擁有該窗口的類是一個(gè)框架類,則命令和通知消息也被傳遞到視圖和文檔類,并為該類尋找一個(gè)消息處理函數(shù)。 為什么要消息映像? ??? 這畢竟是C++語(yǔ)言;為什么OnWndMsg( )不為每個(gè)窗口消息調(diào)用一個(gè)預(yù)定義的虛擬函數(shù)?因?yàn)樗糃PU。若是那樣,當(dāng)掃描一個(gè)消息映像以加速該過(guò)程時(shí),OnWndMsg( )可能會(huì)做出意想不到的事情,并陷入?yún)R編器。注意通過(guò)重載WindowProc( )、OnWndMsg( )、OnCommand( )、OnNotify( ) 或OnCmdMsg( )可以修改這一過(guò)程。重載OnWndMsg( )可以在窗口消息被排序之前插入該過(guò)程。重載OnCommand( )或OnNotify( )可以在消息被反射之前插入該過(guò)程。
2007年3月6日
?????????在file類中,對(duì)靜態(tài)方法的每一次調(diào)用都會(huì)進(jìn)行安全檢查,而由于fileinfo類是一個(gè)實(shí)例類,因此只需要進(jìn)行一次安全檢查,如何判斷應(yīng)該使用哪個(gè)類呢?有個(gè)經(jīng)驗(yàn)法則可以參考:如果對(duì)文件進(jìn)行單次操作,file類是比較好的選擇,而如果要對(duì)文件進(jìn)行多個(gè)操作,那么使用fileinfo類會(huì)有更高的效率。
|