青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

平凡的天才

目的是為人類(lèi)造福
posts - 20, comments - 41, trackbacks - 0, articles - 6
  C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

http://blog.csdn.net/lixiaosan/archive/2006/04/07/653563.aspx

以下未經(jīng)說(shuō)明,listctrl默認(rèn)view 風(fēng)格為report

相關(guān)類(lèi)及處理函數(shù)

MFC:CListCtrl類(lèi)

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ì)資料

      直觀(guān)的理解: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)格線(xiàn)(只適用與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í)(包括用鍵盤(pán)和鼠標(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的派生類(lèi)

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)型來(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后,再I(mǎi)nsertColumn()后導(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. 類(lèi)似于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派生類(lèi)

          (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,字符串改變顏色的類(lèi)
                   http://www.codeproject.com/listctrl/quicklist.asp
 
                   支持排序,subitem可編輯,subitem圖標(biāo),subitem改變顏色的類(lèi)
                   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)    類(lèi)vb屬性對(duì)話(huà)框
                    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

posted @ 2007-11-20 14:08 平凡的天才 閱讀(9110) | 評(píng)論 (3)編輯 收藏

   當(dāng)前流行的Windows操作系統(tǒng)能同時(shí)運(yùn)行幾個(gè)程序(獨(dú)立運(yùn)行的程序又稱(chēng)之為進(jìn)程),對(duì)于同一個(gè)程序,它又可以分成若干個(gè)獨(dú)立的執(zhí)行流,我們稱(chēng)之 為線(xiàn)程,線(xiàn)程提供了多任務(wù)處理的能力。用進(jìn)程和線(xiàn)程的觀(guān)點(diǎn)來(lái)研究軟件是當(dāng)今普遍采用的方法,進(jìn)程和線(xiàn)程的概念的出現(xiàn),對(duì)提高軟件的并行性有著重要的意義。 現(xiàn)在的大型應(yīng)用軟件無(wú)一不是多線(xiàn)程多任務(wù)處理,單線(xiàn)程的軟件是不可想象的。因此掌握多線(xiàn)程多任務(wù)設(shè)計(jì)方法對(duì)每個(gè)程序員都是必需要掌握的。本實(shí)例針對(duì)多線(xiàn)程 技術(shù)在應(yīng)用中經(jīng)常遇到的問(wèn)題,如線(xiàn)程間的通信、同步等,分別進(jìn)行探討,并利用多線(xiàn)程技術(shù)進(jìn)行線(xiàn)程之間的通信,實(shí)現(xiàn)了數(shù)字的簡(jiǎn)單排序。  

一、 實(shí)現(xiàn)方法

1、理解線(xiàn)程

要講解線(xiàn)程,不得不說(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)程的終止而死亡。線(xiàn)程的基本思想很簡(jiǎn)單,它是一個(gè)獨(dú)立的執(zhí)行流,是進(jìn)程內(nèi)部的一個(gè)獨(dú)立的執(zhí)行單元,相當(dāng)于一個(gè)子程序,它對(duì)應(yīng)于Visual C++中的CwinThread類(lèi)對(duì)象。單獨(dú)一個(gè)執(zhí)行程序運(yùn)行時(shí),缺省地包含的一個(gè)主線(xiàn)程,主線(xiàn)程以函數(shù)地址的形式出現(xiàn),提供程序的啟動(dòng)點(diǎn),如main ()或WinMain()函數(shù)等。當(dāng)主線(xiàn)程終止時(shí),進(jìn)程也隨之終止。根據(jù)實(shí)際需要,應(yīng)用程序可以分解成許多獨(dú)立執(zhí)行的線(xiàn)程,每個(gè)線(xiàn)程并行的運(yùn)行在同一進(jìn)程 中。

一個(gè)進(jìn)程中的所有線(xiàn)程都在該進(jìn)程的虛擬地址空間中,使用該進(jìn)程的全局變量和系統(tǒng)資源。操作系統(tǒng)給每個(gè)線(xiàn)程分配不同的CPU時(shí)間片,在某一個(gè)時(shí)刻, CPU只執(zhí)行一個(gè)時(shí)間片內(nèi)的線(xiàn)程,多個(gè)時(shí)間片中的相應(yīng)線(xiàn)程在CPU內(nèi)輪流執(zhí)行,由于每個(gè)時(shí)間片時(shí)間很短,所以對(duì)用戶(hù)來(lái)說(shuō),仿佛各個(gè)線(xiàn)程在計(jì)算機(jī)中是并行處 理的。操作系統(tǒng)是根據(jù)線(xiàn)程的優(yōu)先級(jí)來(lái)安排CPU的時(shí)間,優(yōu)先級(jí)高的線(xiàn)程優(yōu)先運(yùn)行,優(yōu)先級(jí)低的線(xiàn)程則繼續(xù)等待。

線(xiàn)程被分為兩種:用戶(hù)界面線(xiàn)程和工作線(xiàn)程(又稱(chēng)為后臺(tái)線(xiàn)程)。用戶(hù)界面線(xiàn)程通常用來(lái)處理用戶(hù)的輸入并響應(yīng)各種事件和消息,其實(shí),應(yīng)用程序的主執(zhí)行線(xiàn)程 CWinAPP對(duì)象就是一個(gè)用戶(hù)界面線(xiàn)程,當(dāng)應(yīng)用程序啟動(dòng)時(shí)自動(dòng)創(chuàng)建和啟動(dòng),同樣它的終止也意味著該程序的結(jié)束,進(jìn)程終止。工作線(xiàn)程用來(lái)執(zhí)行程序的后臺(tái)處 理任務(wù),比如計(jì)算、調(diào)度、對(duì)串口的讀寫(xiě)操作等,它和用戶(hù)界面線(xiàn)程的區(qū)別是它不用從CWinThread類(lèi)派生來(lái)創(chuàng)建,對(duì)它來(lái)說(shuō)最重要的是如何實(shí)現(xiàn)工作線(xiàn)程 任務(wù)的運(yùn)行控制函數(shù)。工作線(xiàn)程和用戶(hù)界面線(xiàn)程啟動(dòng)時(shí)要調(diào)用同一個(gè)函數(shù)的不同版本;最后需要讀者明白的是,一個(gè)進(jìn)程中的所有線(xiàn)程共享它們父進(jìn)程的變量,但同 時(shí)每個(gè)線(xiàn)程可以擁有自己的變量。

2、線(xiàn)程的管理和操作

(一)線(xiàn)程的啟動(dòng)

創(chuàng)建一個(gè)用戶(hù)界面線(xiàn)程,首先要從類(lèi)CwinThread產(chǎn)生一個(gè)派生類(lèi),同時(shí)必須使用DECLARE_DYNCREATE和 IMPLEMENT_DYNCREATE來(lái)聲明和實(shí)現(xiàn)這個(gè)CwinThread派生類(lèi)。第二步是根據(jù)需要重載該派生類(lèi)的一些成員函數(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)該用戶(hù)界面線(xiàn)程,其中第一個(gè)參數(shù)為指向定義的用戶(hù)界面線(xiàn)程類(lèi)指針變量,第二個(gè)參數(shù)為線(xiàn)程的優(yōu)先級(jí),第三個(gè)參數(shù)為線(xiàn)程所對(duì)應(yīng)的堆棧大小,第四個(gè)參數(shù)為線(xiàn) 程創(chuàng)建時(shí)的附加標(biāo)志,缺省為正常狀態(tài),如為CREATE_SUSPENDED則線(xiàn)程啟動(dòng)后為掛起狀態(tài)。

對(duì)于工作線(xiàn)程來(lái)說(shuō),啟動(dòng)一個(gè)線(xiàn)程,首先需要編寫(xiě)一個(gè)希望與應(yīng)用程序的其余部分并行運(yùn)行的函數(shù)如Fun1(),接著定義一個(gè)指向CwinThread對(duì) 象的指針變量*pThread,調(diào)用AfxBeginThread(Fun1,param,priority)函數(shù),返回值賦給pThread變量的同時(shí) 一并啟動(dòng)該線(xiàn)程來(lái)執(zhí)行上面的Fun1()函數(shù),其中Fun1是線(xiàn)程要運(yùn)行的函數(shù)的名字,也既是上面所說(shuō)的控制函數(shù)的名字,param是準(zhǔn)備傳送給線(xiàn)程函數(shù) Fun1的任意32位值,priority則是定義該線(xiàn)程的優(yōu)先級(jí)別,它是預(yù)定義的常數(shù),讀者可參考MSDN。

(二)線(xiàn)程的優(yōu)先級(jí)

以下的CwinThread類(lèi)的成員函數(shù)用于線(xiàn)程優(yōu)先級(jí)的操作:

int GetThreadPriority();
BOOL SetThradPriority()(int nPriority);

  上述的二個(gè)函數(shù)分別用來(lái)獲取和設(shè)置線(xiàn)程的優(yōu)先級(jí),這里的優(yōu)先級(jí),是相對(duì)于該線(xiàn)程所處的優(yōu)先權(quán)層次而言的,處于同一優(yōu)先權(quán)層次的線(xiàn)程,優(yōu) 先級(jí)高的線(xiàn)程先運(yùn)行;處于不同優(yōu)先權(quán)層次上的線(xiàn)程,誰(shuí)的優(yōu)先權(quán)層次高,誰(shuí)先運(yùn)行。至于優(yōu)先級(jí)設(shè)置所需的常數(shù),自己參考MSDN就可以了,要注意的是要想設(shè) 置線(xiàn)程的優(yōu)先級(jí),這個(gè)線(xiàn)程在創(chuàng)建時(shí)必須具有THREAD_SET_INFORMATION訪(fǎng)問(wèn)權(quán)限。對(duì)于線(xiàn)程的優(yōu)先權(quán)層次的設(shè)置,CwinThread類(lèi) 沒(méi)有提供相應(yīng)的函數(shù),但是可以通過(guò)Win32 SDK函數(shù)GetPriorityClass()和SetPriorityClass()來(lái)實(shí)現(xiàn)。

(三)線(xiàn)程的懸掛和恢復(fù)

CWinThread類(lèi)中包含了應(yīng)用程序懸掛和恢復(fù)它所創(chuàng)建的線(xiàn)程的函數(shù),其中SuspendThread()用來(lái)懸掛線(xiàn)程,暫停線(xiàn)程的執(zhí)行; ResumeThread()用來(lái)恢復(fù)線(xiàn)程的執(zhí)行。如果你對(duì)一個(gè)線(xiàn)程連續(xù)若干次執(zhí)行SuspendThread(),則需要連續(xù)執(zhí)行相應(yīng)次的 ResumeThread()來(lái)恢復(fù)線(xiàn)程的運(yùn)行。

(四)結(jié)束線(xiàn)程

終止線(xiàn)程有三種途徑,線(xiàn)程可以在自身內(nèi)部調(diào)用AfxEndThread()來(lái)終止自身的運(yùn)行;可以在線(xiàn)程的外部調(diào)用BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode )來(lái)強(qiáng)行終止一個(gè)線(xiàn)程的運(yùn)行,然后調(diào)用CloseHandle()函數(shù)釋放線(xiàn)程所占用的堆棧;第三種方法是改變?nèi)肿兞浚咕€(xiàn)程的執(zhí)行函數(shù)返回,則該線(xiàn)程 終止。下面以第三種方法為例,給出部分代碼:

////////////////////////////////////////////////////////////////
//////CtestView message handlers
/////Set to True to end thread
Bool bend=FALSE;//定義的全局變量,用于控制線(xiàn)程的運(yùn)行;
//The Thread Function;
UINT ThreadFunction(LPVOID pParam)//線(xiàn)程函數(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)線(xiàn)程
pThread->m_bAutoDelete=FALSE;//線(xiàn)程為手動(dòng)刪除
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{
bend=TRUE;//改變變量,線(xiàn)程結(jié)束
WaitForSingleObject(pThread->m_hThread,INFINITE);//等待線(xiàn)程結(jié)束
delete pThread;//刪除線(xiàn)程
Cview::OnDestroy();
}

  3、線(xiàn)程之間的通信

通常情況下,一個(gè)次級(jí)線(xiàn)程要為主線(xiàn)程完成某種特定類(lèi)型的任務(wù),這就隱含著表示在主線(xiàn)程和次級(jí)線(xiàn)程之間需要建立一個(gè)通信的通道。一般情況下,有下面的幾 種方法實(shí)現(xiàn)這種通信任務(wù):使用全局變量(上一節(jié)的例子其實(shí)使用的就是這種方法)、使用事件對(duì)象、使用消息。這里我們主要介紹后兩種方法。

(一) 利用用戶(hù)定義的消息通信

在Windows程序設(shè)計(jì)中,應(yīng)用程序的每一個(gè)線(xiàn)程都擁有自己的消息隊(duì)列,甚至工作線(xiàn)程也不例外,這樣一來(lái),就使得線(xiàn)程之間利用消息來(lái)傳遞信息就變的 非常簡(jiǎn)單。首先用戶(hù)要定義一個(gè)用戶(hù)消息,如下所示:#define WM_USERMSG WMUSER+100;在需要的時(shí)候,在一個(gè)線(xiàn)程中調(diào)用::PostMessage((HWND)param,WM_USERMSG,0,0)或 CwinThread::PostThradMessage()來(lái)向另外一個(gè)線(xiàn)程發(fā)送這個(gè)消息,上述函數(shù)的四個(gè)參數(shù)分別是消息將要發(fā)送到的目的窗口的句 柄、要發(fā)送的消息標(biāo)志符、消息的參數(shù)WPARAM和LPARAM。下面的代碼是對(duì)上節(jié)代碼的修改,修改后的結(jié)果是在線(xiàn)程結(jié)束時(shí)顯示一個(gè)對(duì)話(huà)框,提示線(xiàn)程結(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;
}

  上面的例子是工作者線(xiàn)程向用戶(hù)界面線(xiàn)程發(fā)送消息,對(duì)于工作者線(xiàn)程,如果它的設(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ù)在判斷該線(xiàn)程的消息隊(duì)列為空時(shí),線(xiàn)程將系統(tǒng)分配給它的時(shí)間片讓給其它線(xiàn)程,不無(wú)效的占用CPU的時(shí)間,如果消息隊(duì) 列不為空,就獲取這個(gè)消息,判斷這個(gè)消息的內(nèi)容并進(jìn)行相應(yīng)的處理。

(二)用事件對(duì)象實(shí)現(xiàn)通信

在線(xiàn)程之間傳遞信號(hào)進(jìn)行通信比較復(fù)雜的方法是使用事件對(duì)象,用MFC的Cevent類(lèi)的對(duì)象來(lái)表示。事件對(duì)象處于兩種狀態(tài)之一:有信號(hào)和無(wú)信號(hào),線(xiàn)程可以監(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í)線(xiàn)程在這里懸停
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)線(xiàn)程
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、線(xiàn)程之間的同步

前面我們講過(guò),各個(gè)線(xiàn)程可以訪(fǎng)問(wèn)進(jìn)程中的公共變量,所以使用多線(xiàn)程的過(guò)程中需要注意的問(wèn)題是如何防止兩個(gè)或兩個(gè)以上的線(xiàn)程同時(shí)訪(fǎng)問(wèn)同一個(gè)數(shù)據(jù),以免破 壞數(shù)據(jù)的完整性。保證各個(gè)線(xiàn)程可以在一起適當(dāng)?shù)膮f(xié)調(diào)工作稱(chēng)為線(xiàn)程之間的同步。前面一節(jié)介紹的事件對(duì)象實(shí)際上就是一種同步形式。Visual C++中使用同步類(lèi)來(lái)解決操作系統(tǒng)的并行性而引起的數(shù)據(jù)不安全的問(wèn)題,MFC支持的七個(gè)多線(xiàn)程的同步類(lèi)可以分成兩大類(lèi):同步對(duì)象 (CsyncObject、Csemaphore、Cmutex、CcriticalSection和Cevent)和同步訪(fǎng)問(wèn)對(duì)象 (CmultiLock和CsingleLock)。本節(jié)主要介紹臨界區(qū)(critical section)、互斥(mutexe)、信號(hào)量(semaphore),這些同步對(duì)象使各個(gè)線(xiàn)程協(xié)調(diào)工作,程序運(yùn)行起來(lái)更安全。

(一) 臨界區(qū)

臨界區(qū)是保證在某一個(gè)時(shí)間只有一個(gè)線(xiàn)程可以訪(fǎng)問(wèn)數(shù)據(jù)的方法。使用它的過(guò)程中,需要給各個(gè)線(xiàn)程提供一個(gè)共享的臨界區(qū)對(duì)象,無(wú)論哪個(gè)線(xiàn)程占有臨界區(qū)對(duì)象, 都可以訪(fǎng)問(wèn)受到保護(hù)的數(shù)據(jù),這時(shí)候其它的線(xiàn)程需要等待,直到該線(xiàn)程釋放臨界區(qū)對(duì)象為止,臨界區(qū)被釋放后,另外的線(xiàn)程可以強(qiáng)占這個(gè)臨界區(qū),以便訪(fǎng)問(wèn)共享的數(shù) 據(jù)。臨界區(qū)對(duì)應(yīng)著一個(gè)CcriticalSection對(duì)象,當(dāng)線(xiàn)程需要訪(fǎ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è)線(xiàn)程可以?shī)Z取臨界區(qū)對(duì)象并訪(fǎng)問(wèn)受保護(hù)的數(shù)據(jù)。同時(shí)啟動(dòng)兩個(gè)線(xiàn)程,它 們對(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)用程序的線(xiàn)程間實(shí)現(xiàn)同步,還可以在不同的進(jìn)程間實(shí)現(xiàn)同步,從而實(shí)現(xiàn)資源的安全共享。 互斥與Cmutex類(lèi)的對(duì)象相對(duì)應(yīng),使用互斥對(duì)象時(shí),必須創(chuàng)建一個(gè)CSingleLock或CMultiLock對(duì)象,用于實(shí)際的訪(fǎng)問(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è)線(xiàn)程訪(fǎng)問(wèn)同一個(gè)資源,創(chuàng)建一個(gè)信號(hào)量需要用Csemaphore類(lèi)聲明一個(gè)對(duì)象,一 旦創(chuàng)建了一個(gè)信號(hào)量對(duì)象,就可以用它來(lái)對(duì)資源的訪(fǎng)問(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è)線(xiàn)程,執(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è)子線(xiàn)程;

3、 輸入代碼,編譯運(yùn)行程序。

三、 程序代碼

//////////////////////////////////////////////////////////////////////////////////////
// sequence.cpp : Defines the entry point for the console application.
/*
主要用到的WINAPI線(xiàn)程控制函數(shù),有關(guān)詳細(xì)說(shuō)明請(qǐng)查看MSDN;
線(xiàn)程建立函數(shù):
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全屬性結(jié)構(gòu)指針,可為NULL;
DWORD dwStackSize, // 線(xiàn)程棧大小,若為0表示使用默認(rèn)值;
LPTHREAD_START_ROUTINE lpStartAddress, // 指向線(xiàn)程函數(shù)的指針;
LPVOID lpParameter, // 傳遞給線(xiàn)程函數(shù)的參數(shù),可以保存一個(gè)指針值;
DWORD dwCreationFlags, // 線(xiàn)程建立是的初始標(biāo)記,運(yùn)行或掛起;
LPDWORD lpThreadId // 指向接收線(xiàn)程號(hào)的DWORD變量;
);

對(duì)臨界資源控制的多線(xiàn)程控制的信號(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)量的名稱(chēng),字符數(shù)不可多于MAX_PATH;
//如果遇到同名的其他信號(hào)量函數(shù)就會(huì)失敗,如果遇
//到同類(lèi)信號(hào)同名也要注意變化;
);

HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全屬性結(jié)構(gòu)指針,可為NULL
BOOL bInitialOwner, // 當(dāng)前建立互斥量是否占有該互斥量TRUE表示占有,
//這樣其他線(xiàn)程就不能獲得此互斥量也就無(wú)法進(jìn)入由
//該互斥量控制的臨界區(qū)。FALSE表示不占有該互斥量
LPCTSTR lpName // 信號(hào)量的名稱(chēng),字符數(shù)不可多于MAX_PATH如果
//遇到同名的其他信號(hào)量函數(shù)就會(huì)失敗,
//如果遇到同類(lèi)信號(hào)同名也要注意變化;
);

//初始化臨界區(qū)信號(hào),使用前必須先初始化
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection // 臨界區(qū)變量指針
);

//阻塞函數(shù)
//如果等待的信號(hào)量不可用,那么線(xiàn)程就會(huì)掛起,直到信號(hào)可用
//線(xiàn)程才會(huì)被喚醒,該函數(shù)會(huì)自動(dòng)修改信號(hào),如Event,線(xiàn)程被喚醒之后
//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)記是否所有子線(xiàn)程都執(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)有線(xiàn)程進(jìn)入臨界區(qū)并擁有此信號(hào)
static long ThreadCompleted = 0;
/*用來(lái)標(biāo)記四個(gè)子線(xiàn)程中已完成線(xiàn)程的個(gè)數(shù),當(dāng)一個(gè)子線(xiàn)程完成時(shí)就對(duì)ThreadCompleted進(jìn)行加一操作, 要使用InterlockedIncrement(long* lpAddend)和InterlockedDecrement(long* lpAddend)進(jìn)行加減操作*/

//下面的結(jié)構(gòu)是用于傳送排序的數(shù)據(jù)給各個(gè)排序子線(xiàn)程
struct MySafeArray
{
long* data;
int iLength;
};

//打印每一個(gè)線(xiàn)程的排序結(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è)線(xiàn)程函數(shù)的必要條件才可以使用CreateThread
建立一個(gè)線(xiàn)程。
(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類(lèi)型
(4) 如果函數(shù)是類(lèi)成員函數(shù),必須聲明為static函數(shù),在CreateThread時(shí)函數(shù)指針有特殊的寫(xiě)法。如下(函數(shù)是類(lèi)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è)子線(xiàn)程分別對(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è)子線(xiàn)程
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);
//在主線(xiàn)程中執(zhí)行行快速排序,其他排序在子線(xiàn)程中執(zhí)行
QuickSort(data5, 0, iDataLen - 1);
PrintResult(data5, iDataLen, "Quick Sort");
WaitForSingleObject(evtTerminate, INFINITE); //等待所有的子線(xiàn)程結(jié)束
//所有的子線(xiàn)程結(jié)束后,主線(xiàn)程才可以結(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); //返回前使線(xiàn)程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線(xiàn)程都已執(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); //返回前使線(xiàn)程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate);//檢查是否其他線(xiàn)程都已執(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); //返回前使線(xiàn)程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線(xiàn)程都已執(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); //返回前使線(xiàn)程完成數(shù)標(biāo)記加1
if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線(xiàn)程都已執(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è)線(xiàn)程都要使用這個(gè)函數(shù)進(jìn)行輸出,而且只有一個(gè)顯示器,產(chǎn)生多個(gè)線(xiàn)程
//競(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)記有線(xiàn)程進(jìn)入臨界區(qū)
//WaitForSingleObject(mtxPrint, INFINITE); //等待互斥量空置(沒(méi)有線(xiàn)程擁有它)
int i;
printf("%s: ", HeadStr);
for (i=0; i {
printf("%d,", Array[i]);
Sleep(100); //延時(shí)(可以去掉)
/*只是使得多線(xiàn)程對(duì)臨界區(qū)訪(fǎng)問(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ō),線(xiàn)程的應(yīng)用給應(yīng)用程序提供了高效、快速、安全的數(shù)據(jù)處理能力。本實(shí)例講述了線(xiàn)程處理中經(jīng)常遇到的問(wèn)題,希望對(duì)讀者朋友有一定的幫助,起到拋磚引玉的作用。

轉(zhuǎn)自(http://hi.baidu.com/laodun/blog/item/8ab8f3241af3f7318644f916.html)

posted @ 2007-09-13 20:13 平凡的天才 閱讀(1257) | 評(píng)論 (0)編輯 收藏

  • 需要構(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 []嗎?
  • posted @ 2007-04-17 16:44 平凡的天才 閱讀(891) | 評(píng)論 (0)編輯 收藏

    1.賦值(=),下標(biāo)([]),調(diào)用( () )和成員訪(fǎng)問(wèn)箭頭(->)等操作符必須定義為成員,將這些定義為非成員函數(shù)將在編譯時(shí)標(biāo)記為錯(cuò)誤

    2.像賦值一樣,復(fù)合賦值操作符通常應(yīng)定義為類(lèi)的成員,與賦值不同的是,不一定非得這樣做,如果定義非成員復(fù)合賦值操作符,不會(huì)出現(xiàn)編譯錯(cuò)誤.
    3.改變對(duì)象狀態(tài)或與給頂類(lèi)型緊密聯(lián)系的其他一些操作符,如自增,自減和自解引用,通常應(yīng)定義為類(lèi)成員
    4.對(duì)稱(chēng)的操作符號(hào),如算術(shù)操作符,相等操作符,關(guān)系操作符和位操作符,最好定義為普通非成員函數(shù).

    posted @ 2007-04-13 11:40 平凡的天才 閱讀(976) | 評(píng)論 (0)編輯 收藏

    既可以在函數(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í)參才是有效的.

    posted @ 2007-04-13 10:43 平凡的天才 閱讀(986) | 評(píng)論 (0)編輯 收藏

    有些成員必須在構(gòu)造函數(shù)初始化列表中進(jìn)行初始化.對(duì)于這樣的成員,在構(gòu)造函數(shù)函數(shù)體中對(duì)它們賦值不起作用.必須使用初始化列表的情況有以下幾種:
    1.沒(méi)有默認(rèn)構(gòu)造函數(shù)的類(lèi)類(lèi)型的成員(這是因?yàn)闃?gòu)造函數(shù)分為兩個(gè)階段:1.初始化階段,2.普通計(jì)算階段.初始化階段發(fā)生在計(jì)算階段開(kāi)始之前)
    2.成員為const或引用類(lèi)型.
     

    posted @ 2007-04-10 12:01 平凡的天才 閱讀(1763) | 評(píng)論 (0)編輯 收藏

       可以聲明一個(gè)類(lèi)而不定義它
       class Screen;//declaration of the Screen class
       這個(gè)聲明,有時(shí)候被稱(chēng)為前向聲明(forward declaration),在程序中引入了類(lèi)類(lèi)型的Screen.在聲明之后,定義之前,類(lèi)Screen是一個(gè)不完全類(lèi)型(incompete type),即已知Screen是一個(gè)類(lèi)型,但不知道包含哪些成員.
       不完全類(lèi)型只能以有限方式使用,不能定義該類(lèi)型的對(duì)象,不完全類(lèi)型只能用于定義指向該類(lèi)型的指針及引用,或者用于聲明(而不是定義)使用該類(lèi)型作為形參類(lèi)型或返回類(lèi)型的函數(shù).

    posted @ 2007-04-09 23:24 平凡的天才 閱讀(5333) | 評(píng)論 (3)編輯 收藏

    PostMessage 只是把消息放入隊(duì)列,不管其他程序是否處理都返回,然后繼續(xù)執(zhí)行 ;
    SendMessage 必須等待其他程序處理消息后才返回,繼續(xù)執(zhí)行。
    PostMessage
    的返回值表示 PostMessage 函數(shù)執(zhí)行是否正確 ;
    SendMessage 的返回值表示其他程序處理消息后的返回值。
    使用這兩個(gè)發(fā)送消息函數(shù)的最重要的是要看你的程序是否要對(duì)消息的滯后性關(guān)注否 ,PostMessage 會(huì)造成消息的滯后性 , SendMessage 則不會(huì) , 但如果 SendMessage 消息處理失敗 , 則會(huì)造成程序停止 !

    posted @ 2007-03-09 11:31 平凡的天才 閱讀(7401) | 評(píng)論 (4)編輯 收藏

    轉(zhuǎn)載自http://blog.csdn.net/Image_Graphics/archive/2006/11/22/1405436.aspx


    1. 怎樣使用MFC發(fā)送一個(gè)消息用MFC發(fā)送一個(gè)消息的方法是,
    ????首先,應(yīng)獲取接收消息的CWnd類(lèi)對(duì)象的指針;
    ????然后,調(diào)用CWnd的成員函數(shù)SendMessage( )。
    ????????LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
    ????????pWnd指針指向目標(biāo)CWnd類(lèi)對(duì)象。變量Msg是消息,wParam和lParam變量包含消息的參數(shù),如鼠標(biāo)單擊哪里或選擇了什么菜單項(xiàng)。目標(biāo)窗口返回的消息結(jié)果放在變量Res中。
    ????????發(fā)送消息到一個(gè)沒(méi)有CWnd類(lèi)對(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,則所有鍵盤(pán)或鼠標(biāo)的消息將被截獲。wRemoveMsg變量指定PeekMessage( )是否應(yīng)該真正地從隊(duì)列中刪除該消息。(GetMessage( )總是刪除消息)。該變量可以取兩個(gè)值:
    ????■ PM_REMOVE,PeekMessage( )將刪除消息。
    ????■ PM_NOREMOVE,PeekMessage( )將把消息留在隊(duì)列里,并返回它的一個(gè)拷貝。
    ????當(dāng)然,如果把消息留在消息隊(duì)列中,然后再次調(diào)用PeekMessage( )查看相同類(lèi)型的消息,則將返回完全相同的消息。
    ????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類(lèi)可調(diào)用reTranslateMessage在發(fā)送消息給它之前進(jìn)行消息翻譯,例如,CFrameWnd用PreTranslateMessage( )將加速鍵(如,Ctrl+S存儲(chǔ)文件)轉(zhuǎn)換為命令消息。翻譯前的消息通常被處理掉,而翻譯后的消息(如果有的話(huà))將被重新寄送到隊(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è)專(zhuān)門(mén)處理該類(lèi)型消息的類(lèi)的成員函數(shù)處理。因此,MFC不采用case語(yǔ)句,而采用更加復(fù)雜和回旋的方法。但它允許用私有類(lèi)處理消息,而只需做下面三件事情:
    ??? ■ 從將要接收消息的CWnd類(lèi)對(duì)象派生類(lèi)(對(duì)于命令消息是CCmdTarget)。
    ??? ■ 在派生類(lèi)中寫(xiě)一個(gè)處理消息的成員函數(shù)。
    ??? ■ 在類(lèi)中定義一個(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( )搜索類(lèi)的消息映像,以找到一個(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ù)接收消息的類(lèi),OnCmdMsg( )將在一個(gè)稱(chēng)為命令傳遞(Command Routing)的過(guò)程中潛在地傳遞命令消息和控件通知。例如,如果擁有該窗口的類(lèi)是一個(gè)框架類(lèi),則命令和通知消息也被傳遞到視圖和文檔類(lèi),并為該類(lèi)尋找一個(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ò)程。

    posted @ 2007-03-09 11:22 平凡的天才 閱讀(7533) | 評(píng)論 (0)編輯 收藏

    ?????????在file類(lèi)中,對(duì)靜態(tài)方法的每一次調(diào)用都會(huì)進(jìn)行安全檢查,而由于fileinfo類(lèi)是一個(gè)實(shí)例類(lèi),因此只需要進(jìn)行一次安全檢查,如何判斷應(yīng)該使用哪個(gè)類(lèi)呢?有個(gè)經(jīng)驗(yàn)法則可以參考:如果對(duì)文件進(jìn)行單次操作,file類(lèi)是比較好的選擇,而如果要對(duì)文件進(jìn)行多個(gè)操作,那么使用fileinfo類(lèi)會(huì)有更高的效率。

    posted @ 2007-03-06 19:29 平凡的天才 閱讀(3178) | 評(píng)論 (0)編輯 收藏

    僅列出標(biāo)題
    共2頁(yè): 1 2 
    青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩亚洲一区三区| 免费日韩精品中文字幕视频在线| 激情综合中文娱乐网| 久久精品人人做人人爽电影蜜月| 一本色道久久综合亚洲精品不| 久久久99国产精品免费| 最新亚洲一区| 91久久精品国产91性色| 亚洲激情成人| 亚洲国产欧美日韩| 1000部精品久久久久久久久| 国产字幕视频一区二区| 一区二区视频在线观看| 国产在线一区二区三区四区| 国产欧美日韩视频一区二区| 国产精品自拍三区| 一区二区在线观看av| 日韩亚洲欧美精品| 亚欧成人精品| 免费视频一区| 在线视频亚洲一区| 久久一区亚洲| 国产精品伦一区| 国产区欧美区日韩区| 亚洲精品在线视频观看| 亚洲三级免费电影| 一个人看的www久久| 久久精品一区蜜桃臀影院 | 欧美在线免费看| 免费亚洲电影在线观看| 国产精品第十页| 日韩一级精品| 亚洲电影一级黄| 久久婷婷影院| 在线观看精品| 免费观看在线综合| 久久久久久免费| 国产一区二区成人| 久久精品国产一区二区电影| 亚洲免费成人av| 欧美日韩1区2区| 日韩午夜av在线| 亚洲人午夜精品| 欧美精品一区二区三区视频| 亚洲国产经典视频| 久热国产精品视频| 久久久亚洲国产天美传媒修理工| 99视频精品全国免费| 一区二区久久久久| 国产精品红桃| 久久亚洲春色中文字幕久久久| 亚洲一区二区三区高清| 国产日韩欧美制服另类| 久久久久9999亚洲精品| 美女黄毛**国产精品啪啪| 亚洲日本va午夜在线影院| 亚洲精品国精品久久99热| 欧美日韩在线观看一区二区| 麻豆精品传媒视频| 一区二区三区鲁丝不卡| 亚洲欧美欧美一区二区三区| 国语自产精品视频在线看8查询8| 美日韩精品视频| 国产精品vvv| 欧美激情精品久久久久| 国产精品美女诱惑| 亚洲国产精品一区在线观看不卡| 欧美国产日韩精品| 狂野欧美一区| 国产色产综合色产在线视频| 一区二区欧美精品| 亚洲人成人77777线观看| 久久久久久9999| 久久五月天婷婷| 国产一区二区三区av电影 | 国产视频在线观看一区| 亚洲国产日本| 91久久精品网| 欧美高清在线| 免费久久99精品国产| 国产日韩欧美高清| 亚洲欧美国产毛片在线| 欧美一区成人| 国产综合婷婷| 久久综合色一综合色88| 免费成人高清| 正在播放欧美一区| 国产欧美日本一区视频| 久久精品一区二区三区四区| 毛片一区二区三区| 极品少妇一区二区三区精品视频| 一区二区日韩免费看| 国产午夜亚洲精品理论片色戒| 亚洲欧美制服中文字幕| 日韩一二三区视频| 欧美福利视频网站| 中文日韩在线视频| 老司机免费视频一区二区三区| 亚洲国产综合视频在线观看| 欧美日韩精品三区| 午夜影院日韩| 99视频+国产日韩欧美| 欧美专区在线观看一区| 亚洲精品美女在线观看| 国产亚洲欧美日韩精品| 美女露胸一区二区三区| 亚洲图片欧美日产| 欧美激情视频在线播放| 午夜欧美大尺度福利影院在线看 | 亚洲一级黄色片| 亚洲激情一区二区三区| 久久蜜桃资源一区二区老牛| 亚洲欧美日韩国产中文在线| 亚洲精品日产精品乱码不卡| 曰韩精品一区二区| 国产一区二区三区日韩欧美| 国产精品一二| 国产欧美日本在线| 国产日韩欧美制服另类| 国产精品大片| 国产日韩成人精品| 欧美大片va欧美在线播放| 欧美不卡在线视频| 欧美成人一二三| 夜色激情一区二区| 亚洲深夜影院| 欧美一区二区三区视频在线| 亚洲欧美大片| 欧美r片在线| 国产日韩av一区二区| 亚洲激情女人| 欧美一区在线视频| 一本久久青青| 农村妇女精品| 亚洲国产精品综合| 一区二区国产日产| 亚洲精品一区二区网址| 亚洲免费影视| 亚洲一区二区免费看| 欧美亚洲日本网站| 午夜一级久久| 亚洲专区国产精品| 欧美激情影院| 久久女同互慰一区二区三区| 欧美性做爰毛片| 一本一本久久| av成人免费在线| 欧美日韩亚洲精品内裤| 亚洲国产日韩一区二区| 精品91久久久久| 久久久欧美精品sm网站| 欧美专区日韩视频| 国产欧美日韩91| 久久精品动漫| 亚洲一区二区三区成人在线视频精品 | 欧美一区二区视频在线观看2020| 欧美三级小说| 亚洲区国产区| 亚洲午夜伦理| 国产欧美亚洲视频| 欧美一区三区二区在线观看| 久久综合五月天婷婷伊人| 亚洲人午夜精品| 欧美日韩一区二区三区免费| 亚洲一区美女视频在线观看免费| 久久免费高清| 99精品久久| 国产日韩欧美在线| 欧美国产欧美综合| 亚洲欧美日韩国产综合精品二区| 美女网站在线免费欧美精品| 夜夜嗨av一区二区三区| 99国产精品国产精品久久 | 国产精品毛片a∨一区二区三区| 一区二区三区四区蜜桃| 久久久人成影片一区二区三区观看 | 亚洲欧洲一区二区在线观看| 欧美日韩精品免费看| 亚洲午夜一区二区| 久久久久网站| 中文av一区二区| 欧美激情一区在线| 久久综合色婷婷| 欧美国产视频在线| 欧美成人一二三| 亚洲人成高清| 国产精品久久久久久久久久久久久 | 国产精品极品美女粉嫩高清在线| 午夜日韩在线观看| 亚洲国产成人精品久久久国产成人一区 | 欧美一区二区三区久久精品| 91久久久久久久久| 国产精品入口66mio| 免费人成精品欧美精品| 亚洲制服少妇| 一本久久知道综合久久| 亚洲黄色三级| 免费欧美网站| 久久综合狠狠综合久久综青草 | 欧美jizz19性欧美|