• <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>

            平凡的天才

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

            2007年11月20日

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

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

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

            MFC:CListCtrl類

            SDK:以 “ListView_”開頭的一些宏。如 ListView_InsertColumn


            1. CListCtrl 風(fēng)格

                  LVS_ICON: 為每個item顯示大圖標(biāo)
                  LVS_SMALLICON: 為每個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請查閱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點中設(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中所有選中行的序號


                  方法一:
                  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中一項可見,即滾動滾動條

                m_list.EnsureVisible(i, FALSE);


            11. 得到listctrl列數(shù)

                int nHeadNum = m_list.GetHeaderCtrl()->GetItemCount();


            12. 刪除所有列

                  方法一:
                     while ( m_list.DeleteColumn (0))
                   因為你刪除了第一列后,后面的列會依次向上移動。

                  方法二:
                  int nColumns = 4;
                  for (int i=nColumns-1; i>=0; i--)
                      m_list.DeleteColumn (i);



            13. 得到單擊的listctrl的行列號

                  添加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. 判斷是否點擊在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);
                       //判斷是否點在checkbox上
                       if(nFlag == LVHT_ONITEMSTATEICON)
                       {
                            AfxMessageBox("點在listctrl的checkbox上");
                       }
                       *pResult = 0;
                  }



            15. 右鍵點擊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切換焦點時(包括用鍵盤和鼠標(biāo)切換item時),狀態(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. 得到另一個進(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ù)請參考msdn

             


            21. 在CListCtrl顯示文件,并根據(jù)文件類型來顯示圖標(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ù)量更新時,避免閃爍

                  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中選中某個item時動態(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ù)移動的問題

            Q151897: CListCtrl::InsertColumn() Causes Column Data to Shift
            http://support.microsoft.com/kb/151897/en-us



            26. 關(guān)于listctrl第一列始終居左的問題

            解決辦法:把第一列當(dāng)一個虛列,從第二列開始插入列及數(shù)據(jù),最后刪除第一列。
                 
            具體解釋參閱   http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/structures/lvcolumn.asp

             


            27. 鎖定column header的拖動

            http://msdn.microsoft.com/msdnmag/issues/03/06/CQA/



            28. 如何隱藏clistctrl的列

                把需隱藏的列的寬度設(shè)為0,然后檢測當(dāng)該列為隱藏列時,用上面第27點的鎖定column 的拖動來實現(xiàn)


            29. listctrl進(jìn)行大數(shù)據(jù)量操作時,使用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個字符的問題

            解決辦法:需要在item上放一個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)兩次的問題

            Q200054:
            PRB: OnTimer() Is Not Called Repeatedly for a List Control
            http://support.microsoft.com/kb/200054/en-us


            34. 以下為一些為實現(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)    動態(tài)改變subitem的顏色和背景色
                                http://www.codeproject.com/listctrl/highlightlistctrl.asp
                                http://www.codeguru.com/Cpp/controls/listbox/colorlistboxes/article.php/c4757/
             
                      (7)    類vb屬性對話框
                                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 平凡的天才 閱讀(9076) | 評論 (3)編輯 收藏

            2007年9月13日

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

            一、 實現(xiàn)方法

            1、理解線程

            要講解線程,不得不說一下進(jìn)程,進(jìn)程是應(yīng)用程序的執(zhí)行實例,每個進(jìn)程是由私有的虛擬地址空間、代碼、數(shù)據(jù)和其它系統(tǒng)資源組成。進(jìn)程在運行時創(chuàng)建的資源 隨著進(jìn)程的終止而死亡。線程的基本思想很簡單,它是一個獨立的執(zhí)行流,是進(jìn)程內(nèi)部的一個獨立的執(zhí)行單元,相當(dāng)于一個子程序,它對應(yīng)于Visual C++中的CwinThread類對象。單獨一個執(zhí)行程序運行時,缺省地包含的一個主線程,主線程以函數(shù)地址的形式出現(xiàn),提供程序的啟動點,如main ()或WinMain()函數(shù)等。當(dāng)主線程終止時,進(jìn)程也隨之終止。根據(jù)實際需要,應(yīng)用程序可以分解成許多獨立執(zhí)行的線程,每個線程并行的運行在同一進(jìn)程 中。

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

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

            2、線程的管理和操作

            (一)線程的啟動

            創(chuàng)建一個用戶界面線程,首先要從類CwinThread產(chǎn)生一個派生類,同時必須使用DECLARE_DYNCREATE和 IMPLEMENT_DYNCREATE來聲明和實現(xiàn)這個CwinThread派生類。第二步是根據(jù)需要重載該派生類的一些成員函數(shù)如: ExitInstance()、InitInstance()、OnIdle()、PreTranslateMessage()等函數(shù)。最后調(diào)用 AfxBeginThread()函數(shù)的一個版本:CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ) 啟動該用戶界面線程,其中第一個參數(shù)為指向定義的用戶界面線程類指針變量,第二個參數(shù)為線程的優(yōu)先級,第三個參數(shù)為線程所對應(yīng)的堆棧大小,第四個參數(shù)為線 程創(chuàng)建時的附加標(biāo)志,缺省為正常狀態(tài),如為CREATE_SUSPENDED則線程啟動后為掛起狀態(tài)。

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

            (二)線程的優(yōu)先級

            以下的CwinThread類的成員函數(shù)用于線程優(yōu)先級的操作:

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

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

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

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

            (四)結(jié)束線程

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

            ////////////////////////////////////////////////////////////////
            //////CtestView message handlers
            /////Set to True to end thread
            Bool bend=FALSE;//定義的全局變量,用于控制線程的運行;
            //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);//啟動線程
            pThread->m_bAutoDelete=FALSE;//線程為手動刪除
            Cview::OnInitialUpdate();
            }
            ////////////////////////////////////////////////////////////////
            Void CtestView::OnDestroy()
            {
            bend=TRUE;//改變變量,線程結(jié)束
            WaitForSingleObject(pThread->m_hThread,INFINITE);//等待線程結(jié)束
            delete pThread;//刪除線程
            Cview::OnDestroy();
            }

              3、線程之間的通信

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

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

            在Windows程序設(shè)計中,應(yīng)用程序的每一個線程都擁有自己的消息隊列,甚至工作線程也不例外,這樣一來,就使得線程之間利用消息來傳遞信息就變的 非常簡單。首先用戶要定義一個用戶消息,如下所示:#define WM_USERMSG WMUSER+100;在需要的時候,在一個線程中調(diào)用::PostMessage((HWND)param,WM_USERMSG,0,0)或 CwinThread::PostThradMessage()來向另外一個線程發(fā)送這個消息,上述函數(shù)的四個參數(shù)分別是消息將要發(fā)送到的目的窗口的句 柄、要發(fā)送的消息標(biāo)志符、消息的參數(shù)WPARAM和LPARAM。下面的代碼是對上節(jié)代碼的修改,修改后的結(jié)果是在線程結(jié)束時顯示一個對話框,提示線程結(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ā)送消息,對于工作者線程,如果它的設(shè)計模式也是消息驅(qū)動的,那么調(diào)用者可以向它發(fā)送初始化、退 出、執(zhí)行某種特定的處理等消息,讓它在后臺完成。在控制函數(shù)中可以直接使用::GetMessage()這個SDK函數(shù)進(jìn)行消息分檢和處理,自己實現(xiàn)一個 消息循環(huán)。GetMessage()函數(shù)在判斷該線程的消息隊列為空時,線程將系統(tǒng)分配給它的時間片讓給其它線程,不無效的占用CPU的時間,如果消息隊 列不為空,就獲取這個消息,判斷這個消息的內(nèi)容并進(jìn)行相應(yīng)的處理。

            (二)用事件對象實現(xiàn)通信

            在線程之間傳遞信號進(jìn)行通信比較復(fù)雜的方法是使用事件對象,用MFC的Cevent類的對象來表示。事件對象處于兩種狀態(tài)之一:有信號和無信號,線程可以監(jiān)視處于有信號狀態(tài)的事件,以便在適當(dāng)?shù)臅r候執(zhí)行對事件的操作。上述例子代碼修改如下:

            ////////////////////////////////////////////////////////////////////
            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事件有信號,無信號時線程在這里懸停
            If(result==Wait_OBJECT_0)
            Bend=TRUE;
            }
            ::PostMessage(hWnd,WM_USERMSG,0,0);
            return 0;
            }
            /////////////////////////////////////////////////////////////
            Void CtestView::OninitialUpdate()
            {
            hWnd=GetSafeHwnd();
            threadStart.SetEvent();//threadStart事件有信號
            pThread=AfxBeginThread(ThreadFunction,hWnd);//啟動線程
            pThread->m_bAutoDelete=FALSE;
            Cview::OnInitialUpdate();
            }
            ////////////////////////////////////////////////////////////////
            Void CtestView::OnDestroy()
            {
            threadEnd.SetEvent();
            WaitForSingleObject(pThread->m_hThread,INFINITE);
            delete pThread;
            Cview::OnDestroy();
            }

              運行這個程序,當(dāng)關(guān)閉程序時,才顯示提示框,顯示"Thread ended"。

              4、線程之間的同步

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

            (一) 臨界區(qū)

            臨界區(qū)是保證在某一個時間只有一個線程可以訪問數(shù)據(jù)的方法。使用它的過程中,需要給各個線程提供一個共享的臨界區(qū)對象,無論哪個線程占有臨界區(qū)對象, 都可以訪問受到保護(hù)的數(shù)據(jù),這時候其它的線程需要等待,直到該線程釋放臨界區(qū)對象為止,臨界區(qū)被釋放后,另外的線程可以強占這個臨界區(qū),以便訪問共享的數(shù) 據(jù)。臨界區(qū)對應(yīng)著一個CcriticalSection對象,當(dāng)線程需要訪問保護(hù)數(shù)據(jù)時,調(diào)用臨界區(qū)對象的Lock()成員函數(shù);當(dāng)對保護(hù)數(shù)據(jù)的操作完成 之后,調(diào)用臨界區(qū)對象的Unlock()成員函數(shù)釋放對臨界區(qū)對象的擁有權(quán),以使另一個線程可以奪取臨界區(qū)對象并訪問受保護(hù)的數(shù)據(jù)。同時啟動兩個線程,它 們對應(yīng)的函數(shù)分別為WriteThread()和ReadThread(),用以對公共數(shù)組組array[]操作,下面的代碼說明了如何使用臨界區(qū)對象:

            #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();
            }

              上述代碼運行的結(jié)果應(yīng)該是Destarray數(shù)組中的元素分別為1-9,而不是雜亂無章的數(shù),如果不使用同步,則不是這個結(jié)果,有興趣的讀者可以實驗一下。

            (二)互斥

            互斥與臨界區(qū)很相似,但是使用時相對復(fù)雜一些,它不僅可以在同一應(yīng)用程序的線程間實現(xiàn)同步,還可以在不同的進(jìn)程間實現(xiàn)同步,從而實現(xiàn)資源的安全共享。 互斥與Cmutex類的對象相對應(yīng),使用互斥對象時,必須創(chuàng)建一個CSingleLock或CMultiLock對象,用于實際的訪問控制,因為這里的例 子只處理單個互斥,所以我們可以使用CSingleLock對象,該對象的Lock()函數(shù)用于占有互斥,Unlock()用于釋放互斥。實現(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();
            }

             ?。ㄈ┬盘柫?br>
            信號量的用法和互斥的用法很相似,不同的是它可以同一時刻允許多個線程訪問同一個資源,創(chuàng)建一個信號量需要用Csemaphore類聲明一個對象,一 旦創(chuàng)建了一個信號量對象,就可以用它來對資源的訪問技術(shù)。要實現(xiàn)計數(shù)處理,先創(chuàng)建一個CsingleLock或CmltiLock對象,然后用該對象的 Lock()函數(shù)減少這個信號量的計數(shù)值,Unlock()反之。下面的代碼分別啟動三個線程,執(zhí)行時同時顯示二個消息框,然后10秒后第三個消息框才得 以顯示。

            /////////////////////////////////////////////////////////////////////////
            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、 啟動Visual C++6.0,生成一個32位的控制臺程序,將該程序命名為"sequence"

            2、 輸入要排續(xù)的數(shù)字,聲明四個子線程;

            3、 輸入代碼,編譯運行程序。

            三、 程序代碼

            //////////////////////////////////////////////////////////////////////////////////////
            // sequence.cpp : Defines the entry point for the console application.
            /*
            主要用到的WINAPI線程控制函數(shù),有關(guān)詳細(xì)說明請查看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ù),可以保存一個指針值;
            DWORD dwCreationFlags, // 線程建立是的初始標(biāo)記,運行或掛起;
            LPDWORD lpThreadId // 指向接收線程號的DWORD變量;
            );

            對臨界資源控制的多線程控制的信號函數(shù):

            HANDLE CreateEvent(
            LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性結(jié)構(gòu)指針,可為NULL;
            BOOL bManualReset, // 手動清除信號標(biāo)記,TRUE在WaitForSingleObject后必須手動//調(diào)用RetEvent清除信號。若為 FALSE則在WaitForSingleObject
            //后,系統(tǒng)自動清除事件信號;
            BOOL bInitialState, // 初始狀態(tài),TRUE有信號,F(xiàn)ALSE無信號;
            LPCTSTR lpName // 信號量的名稱,字符數(shù)不可多于MAX_PATH;
            //如果遇到同名的其他信號量函數(shù)就會失敗,如果遇
            //到同類信號同名也要注意變化;
            );

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

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

            //阻塞函數(shù)
            //如果等待的信號量不可用,那么線程就會掛起,直到信號可用
            //線程才會被喚醒,該函數(shù)會自動修改信號,如Event,線程被喚醒之后
            //Event信號會變得無信號,Mutex、Semaphore等也會變。
            DWORD WaitForSingleObject(
            HANDLE hHandle, // 等待對象的句柄
            DWORD dwMilliseconds // 等待毫秒數(shù),INFINITE表示無限等待
            );
            //如果要等待多個信號可以使用WaitForMutipleObject函數(shù)
            */

            #include "stdafx.h"
            #include "stdlib.h"
            #include "memory.h"
            HANDLE evtTerminate; //事件信號,標(biāo)記是否所有子線程都執(zhí)行完
            /*
            下面使用了三種控制方法,你可以注釋其中兩種,使用其中一種。
            注意修改時要連帶修改臨界區(qū)PrintResult里的相應(yīng)控制語句
            */
            HANDLE evtPrint; //事件信號,標(biāo)記事件是否已發(fā)生
            //CRITICAL_SECTION csPrint; //臨界區(qū)
            //HANDLE mtxPrint; //互斥信號,如有信號表明已經(jīng)有線程進(jìn)入臨界區(qū)并擁有此信號
            static long ThreadCompleted = 0;
            /*用來標(biāo)記四個子線程中已完成線程的個數(shù),當(dāng)一個子線程完成時就對ThreadCompleted進(jìn)行加一操作, 要使用InterlockedIncrement(long* lpAddend)和InterlockedDecrement(long* lpAddend)進(jìn)行加減操作*/

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

            //打印每一個線程的排序結(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); //插入排序
            /*以上四個函數(shù)的聲明必須適合作為一個線程函數(shù)的必要條件才可以使用CreateThread
            建立一個線程。
            (1)調(diào)用方法必須是__stdcall,即函數(shù)參數(shù)壓棧順序由右到左,而且由函數(shù)本身負(fù)責(zé)
            棧的恢復(fù), C和C++默認(rèn)是__cdecl, 所以要顯式聲明是__stdcall
            (2)返回值必須是unsigned long
            (3)參數(shù)必須是一個32位值,如一個指針值或long類型
            (4) 如果函數(shù)是類成員函數(shù),必須聲明為static函數(shù),在CreateThread時函數(shù)指針有特殊的寫法。如下(函數(shù)是類CThreadTest的成員函數(shù)中):
            static unsigned long _stdcall MyThreadFun(void* pParam);
            handleRet = CreateThread(NULL, 0, &CThreadTestDlg::MyThreadFun, NULL, 0, &ThreadID);
            之所以要聲明為static是由于,該函數(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;
            //為了對各個子線程分別對原始數(shù)據(jù)進(jìn)行排序和保存排序結(jié)果
            //分別分配內(nèi)存對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ù)), 因為long的長度
            //為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;
            //對信號量進(jìn)行初始化
            evtTerminate = CreateEvent(NULL, FALSE, FALSE, "Terminate");
            evtPrint = CreateEvent(NULL, FALSE, TRUE, "PrintResult");
            //分別建立各個子線程
            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;
            }

            /*
            冒泡排序思想(升序,降序同理,后面的算法一樣都是升序):從頭到尾對數(shù)據(jù)進(jìn)行兩兩比較進(jìn)行交換,小的放前大的放后。這樣一次下來,最大的元素就會被交換的最后,然后下一次
            循環(huán)就不用對最后一個元素進(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"); //向控制臺打印排序結(jié)果
            InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
            if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線程都已執(zhí)行完
            //若都執(zhí)行完則設(shè)置程序結(jié)束信號量
            return 0;
            }

            /*選擇排序思想:每一次都從無序的數(shù)據(jù)中找出最小的元素,然后和前面已經(jīng)有序的元素序列的后一個元素進(jìn)行交換,這樣整個源序列就會分成兩部分,前面一部 分是已經(jīng)排好序的有序序列,后面一部分是無序的,用于選出最小的元素。循環(huán)N次之后,前面的有序序列加長到跟源序列一樣長,后面的無序部分長度變?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++) //從無序的元素中找出最小的元素
            {
            if(Array[j] < lMin)
            {
            iMinPos = j;
            lMin = Array[j];
            }
            }
            //把選出的元素交換拼接到有序序列的最后
            lSwap = Array[i];
            Array[i] = Array[iMinPos];
            Array[iMinPos] = lSwap;
            }
            PrintResult(Array, iLength, "Select Sort"); //向控制臺打印排序結(jié)果
            InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
            if(ThreadCompleted == 4) SetEvent(evtTerminate);//檢查是否其他線程都已執(zhí)行完
            //若都執(zhí)行完則設(shè)置程序結(jié)束信號量
            return 0;
            }

            /*堆排序思想:堆:數(shù)據(jù)元素從1到N排列成一棵二叉樹,而且這棵樹的每一個子樹的根都是該樹中的元素的最小或最大的元素這樣如果一個無序數(shù)據(jù)集合是一個 堆那么,根元素就是最小或最大的元素堆排序就是不斷對剩下的數(shù)據(jù)建堆,把最小或最大的元素析透出來。下面的算法,就是從最后一個元素開始,依據(jù)一個節(jié)點比 父節(jié)點數(shù)值大的原則對所有元素進(jìn)行調(diào)整,這樣調(diào)整一次就形成一個堆,第一個元素就是最小的元素。然后再對剩下的無序數(shù)據(jù)再進(jìn)行建堆,注意這時后面的無序數(shù) 據(jù)元素的序數(shù)都要改變,如第一次建堆后,第二個元素就會變成堆的第一個元素。*/
            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é)點和父節(jié)點
            {
            p = (j - i - 1)/2 + i; //計算父節(jié)點數(shù)組下標(biāo)
            //注意到樹節(jié)點序數(shù)跟數(shù)組下標(biāo)不是等同的,因為建堆的元素個數(shù)逐個遞減
            if(Array[j] < Array[p]) //如果父節(jié)點數(shù)值大則交換父節(jié)點和字節(jié)點
            {
            swap = Array[j];
            Array[j] = Array[p];
            Array[p] = swap;
            }
            }
            }
            PrintResult(Array, iLength, "Heap Sort"); //向控制臺打印排序結(jié)果
            InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
            if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線程都已執(zhí)行完
            //若都執(zhí)行完則設(shè)置程序結(jié)束信號量
            return 0;
            }

            /*插入排序思想:把源數(shù)據(jù)序列看成兩半,前面一半是有序的,后面一半是無序的,把無序的數(shù)據(jù)從頭到尾逐個逐個的插入到前面的有序數(shù)據(jù)中,使得有序的數(shù)據(jù)的個數(shù)不斷增大,同時無序的數(shù)據(jù)個數(shù)就越來越少,最后所有元素都會變得有序。*/
            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]; //取出序列后面無序數(shù)據(jù)的第一個元素值
            for(j=i; j>0; j--) //和前面的有序數(shù)據(jù)逐個進(jìn)行比較找出合適的插入位置
            {
            if(Array[j - 1] >temp) //如果該元素比插入值大則后移
            Array[j] = Array[j - 1];
            else //如果該元素比插入值小,那么該位置的后一位就是插入元素的位置
            break;
            }
            Array[j] = temp;
            }
            PrintResult(Array, iLength, "Insert Sort"); //向控制臺打印排序結(jié)果
            InterlockedIncrement(&ThreadCompleted); //返回前使線程完成數(shù)標(biāo)記加1
            if(ThreadCompleted == 4) SetEvent(evtTerminate); //檢查是否其他線程都已執(zhí)行完
            //若都執(zhí)行完則設(shè)置程序結(jié)束信號量
            return 0;
            }

            /*快速排序思想:快速排序是分治思想的一種應(yīng)用,它先選取一個支點,然后把小于支點的元素交換到支點的前邊,把大于支點的元素交換到支點的右邊。然后再對支點左邊部分和右
            邊部分進(jìn)行同樣的處理,這樣若干次之后,數(shù)據(jù)就會變得有序。下面的實現(xiàn)使用了遞歸
            建立兩個游標(biāo):iLow,iHigh;iLow指向序列的第一個元素,iHigh指向最后一個先選第一個元素作為支點,并把它的值存貯在一個輔助變量里。 那么第一個位置就變?yōu)榭詹⒖梢苑胖闷渌脑亍? 這樣從iHigh指向的元素開始向前移動游標(biāo),iHigh查找比支點小的元素,如果找到,則把它放置到空置了的位置(現(xiàn)在是第一個位置),然后iHigh 游標(biāo)停止移動,這時iHigh指向的位置被空置,然后移動iLow游標(biāo)尋找比支點大的元素放置到iHigh指向的空置的位置,如此往復(fù)直到iLow與 iHigh相等。最后使用遞歸對左右兩部分進(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值保存起來
            while (iLow < iHigh)
            {
            while (Array[iHigh] >= pivot && iHigh >iLow) //尋找比支點大的元素
            iHigh -- ;
            Array[iLow] = Array[iHigh]; //把找到的元素放置到空置的位置
            while (Array[iLow] < pivot && iLow < iHigh) //尋找比支點小的元素
            iLow ++ ;
            Array[iHigh] = Array[iLow]; //把找到的元素放置到空置的位置
            }
            Array[iLow] = pivot; //把支點值放置到支點位置,這時支點位置是空置的
            //對左右部分分別進(jìn)行遞歸處理
            QuickSort(Array, iLowSaved, iHigh-1);
            QuickSort(Array, iLow+1, iHighSaved);
            return 0;
            }

            //每一個線程都要使用這個函數(shù)進(jìn)行輸出,而且只有一個顯示器,產(chǎn)生多個線程
            //競爭對控制臺的使用權(quán)。
            void PrintResult(long* Array, int iLength, const char* HeadStr)
            {
            WaitForSingleObject(evtPrint, INFINITE); //等待事件有信號
            //EnterCriticalSection(&csPrint); //標(biāo)記有線程進(jìn)入臨界區(qū)
            //WaitForSingleObject(mtxPrint, INFINITE); //等待互斥量空置(沒有線程擁有它)
            int i;
            printf("%s: ", HeadStr);
            for (i=0; i {
            printf("%d,", Array[i]);
            Sleep(100); //延時(可以去掉)
            /*只是使得多線程對臨界區(qū)訪問的問題比較容易看得到
            如果你把臨界控制的語句注釋掉,輸出就會變得很凌亂,各個排序的結(jié)果會
            分插間隔著輸出,如果不延時就不容易看到這種不對臨界區(qū)控制的結(jié)果
            */
            }
            printf("%d\n", Array[i]);
            SetEvent(evtPrint); //把事件信號量恢復(fù),變?yōu)橛行盘?br> }

              四、 小結(jié)

            對復(fù)雜的應(yīng)用程序來說,線程的應(yīng)用給應(yīng)用程序提供了高效、快速、安全的數(shù)據(jù)處理能力。本實例講述了線程處理中經(jīng)常遇到的問題,希望對讀者朋友有一定的幫助,起到拋磚引玉的作用。

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

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

            2007年4月17日

          1. 需要構(gòu)造器嗎?
          2. 數(shù)據(jù)成員是private的嗎?它可以是const的嗎?
          3. 需要默認(rèn)構(gòu)造器嗎?
          4. 是不是每個構(gòu)造器初始化了所有成員?
          5. 需要析構(gòu)器嗎?它需要虛化嗎?
          6. 需要拷貝構(gòu)造器嗎?
          7. 需要assigment operator嗎?它能正確自賦值嗎?
          8. 需要關(guān)系操作符嗎?
          9. 在函數(shù)形參上使用了const嗎?在成員函數(shù)之后呢?
          10. 刪除數(shù)組成員時用delete []嗎?
          11. posted @ 2007-04-17 16:44 平凡的天才 閱讀(869) | 評論 (0)編輯 收藏

            2007年4月13日

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

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

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

            既可以在函數(shù)聲明也可以在函數(shù)定義中指定默認(rèn)實參.但是在一個文件中,只能為一個形參指定默認(rèn)實參一次下面的例子是錯誤的,
            //ff.h
            int ff(int=0);
            //ff.cc
            #include "ff.h"
            int ff(int i=0){}//error
            如果在函數(shù)定義的形參表中提供默認(rèn)實參,那么只有在包含該函數(shù)定義的源文件中調(diào)用該函數(shù)時,默認(rèn)實參才是有效的.

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

            2007年4月10日

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

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

            2007年4月9日

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

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

            2007年3月9日

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

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

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


            1. 怎樣使用MFC發(fā)送一個消息用MFC發(fā)送一個消息的方法是,
            ????首先,應(yīng)獲取接收消息的CWnd類對象的指針;
            ????然后,調(diào)用CWnd的成員函數(shù)SendMessage( )。
            ????????LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
            ????????pWnd指針指向目標(biāo)CWnd類對象。變量Msg是消息,wParam和lParam變量包含消息的參數(shù),如鼠標(biāo)單擊哪里或選擇了什么菜單項。目標(biāo)窗口返回的消息結(jié)果放在變量Res中。
            ????????發(fā)送消息到一個沒有CWnd類對象的窗口,可以用下列目標(biāo)窗口的句柄直接調(diào)用Windows API:
            ????????LRESULT Res=::SendMessage(HWND hWnd, UINT Msg,? WPARAM wParam, LPARAM lParam);
            ????????這里的hWnd是目標(biāo)窗口的句柄。
            2. 怎樣用MFC寄送一個消息
            ????用MFC寄送一個消息與發(fā)送一個消息幾乎相同,但寄送時用PostMessage( ) ,而不是用SendMessage( );返回值Res也不一樣,Res不是一個由目標(biāo)窗口返回的值,而是一個布爾值,用來表示消息是否成功地放到消息隊列中。
            3. 檢索一個寄送消息
            ????正常情況下,一旦消息被寄送后,應(yīng)用程序在后臺發(fā)送它。但是在特殊情況下,需要你自己去刪除一個消息,例如想在應(yīng)用程序接收到某種消息之前停止應(yīng)用程序。有兩種方法可以從應(yīng)用程序消息隊列中刪除一個消息,但這兩種方法都沒有涉及MFC。
            ■ 第一種方法:在不干擾任何事情之下窺視消息隊列,看看一個消息是否在那里。
            ????BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ) ;
            ■ 第二種方法:實際上是等待,一直等到一個新的消息到達(dá)隊列為止,然后刪除并返回該消息。
            ????BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
            ????在這兩種方法中,變量hWnd指定要截獲消息的窗口,如果該變量設(shè)為NULL,所有窗口消息將被截獲。wMsgFilterMin和wMsgFilterMax變量與SendMessage( )中的變量Msg相對應(yīng),指定查看消息的范圍。如果用"0,0",則所有的消息都將被截獲。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,則所有鍵盤或鼠標(biāo)的消息將被截獲。wRemoveMsg變量指定PeekMessage( )是否應(yīng)該真正地從隊列中刪除該消息。(GetMessage( )總是刪除消息)。該變量可以取兩個值:
            ????■ PM_REMOVE,PeekMessage( )將刪除消息。
            ????■ PM_NOREMOVE,PeekMessage( )將把消息留在隊列里,并返回它的一個拷貝。
            ????當(dāng)然,如果把消息留在消息隊列中,然后再次調(diào)用PeekMessage( )查看相同類型的消息,則將返回完全相同的消息。
            ????lpMsg變量是一個指向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怎樣接收一個寄送的消息
            ??? MFC處理一個寄送和發(fā)送消息的唯一明顯不同是寄送的消息要在應(yīng)用程序的消息隊列中花費一些時間。在消息泵(message pump)彈出它之前,它要一直在隊列中。
            ??? 消息泵
            ??? MFC應(yīng)用程序中的消息泵在CWinApp的成員函數(shù)Run()中。應(yīng)用程序開始運行時,Run()就被調(diào)用,Run()把時間分割成兩部分。一部分用來執(zhí)行后臺處理,如取消臨時CWnd對象;另一部分用來檢查消息隊列。當(dāng)一個新的消息進(jìn)來時,Run()抽取它—即用GetMessage( )從隊列中取出該消息,運行兩個消息翻譯函數(shù),然后用DispatchMessage( )函數(shù)調(diào)用該消息預(yù)期的目標(biāo)窗口進(jìn)程。
            ??? 消息泵調(diào)用的兩個翻譯函數(shù)是PreTranslateMessage( )和::TranslateMessage( )。目標(biāo)窗口的MFC類可調(diào)用reTranslateMessage在發(fā)送消息給它之前進(jìn)行消息翻譯,例如,CFrameWnd用PreTranslateMessage( )將加速鍵(如,Ctrl+S存儲文件)轉(zhuǎn)換為命令消息。翻譯前的消息通常被處理掉,而翻譯后的消息(如果有的話)將被重新寄送到隊列里。::TranslateMessage是一個窗口函數(shù),將原始鍵碼轉(zhuǎn)換為鍵字符。消息一旦被DispatchMessage()發(fā)送,MFC處理它就像處理SendMessage()發(fā)送的消息一樣。
            5. MFC怎樣處理一個接收到的消息
            ??? 處理接收到的消息的目的非常簡單:將消息指向一個函數(shù),該函數(shù)通過消息中的消息標(biāo)識符處理它。非MFC窗口用簡單的case語句來實現(xiàn)該目標(biāo),每個case語句執(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ù)揭粋€默認(rèn)的消息處理函數(shù),但是,case語句不能很好地適應(yīng)C++和封裝技術(shù)。在C++環(huán)境中,要求消息被一個專門處理該類型消息的類的成員函數(shù)處理。因此,MFC不采用case語句,而采用更加復(fù)雜和回旋的方法。但它允許用私有類處理消息,而只需做下面三件事情:
            ??? ■ 從將要接收消息的CWnd類對象派生類(對于命令消息是CCmdTarget)。
            ??? ■ 在派生類中寫一個處理消息的成員函數(shù)。
            ??? ■ 在類中定義一個查找表(叫做消息映像),該表具有成員函數(shù)的條目和它要處理的消息的標(biāo)識符。
            ??? 然后,MFC依次調(diào)用下面的函數(shù),指引輸入消息到處理函數(shù)。
            ??? 1) AfxWndProc( )接收消息,尋找消息所屬的CWnd對象,然后調(diào)用AfxCallWndProc( )。
            ??? 2) AfxCallWndProc( )存儲消息(消息標(biāo)識符和參數(shù))供未來參考,然后調(diào)用WindowProc( )。
            ??? 3) WindowProc( ) 發(fā)送消息給OnWndMsg( ) ,然后,如果消息未被處理,則發(fā)送給DefWindowproc( )。
            ??? 4) OnWndMsg( )要么為WM_COMMAND消息調(diào)用OnCommand( ),要么為WM_NOTIFY消息調(diào)用OnNotify( )。任何被遺漏的消息都將是一個窗口消息。OnWndMsg( )搜索類的消息映像,以找到一個能處理任何窗口消息的處理函數(shù)。如果OnWndMsg( )不能找到這樣的處理函數(shù),則把消息返回到WindowProc( ),由它將消息發(fā)送給DefWindowProc( )。
            ??? 5) OnCommand()查看這是不是一個控件通知(lParam不是NULL);如果它是,OnCommand( )就試圖將消息映射到制造通知的控件;如果它不是一個控件通知,或者控件拒絕映射的消息,OnCommand( )就調(diào)用OnCmdMsg( )。
            ??? 6) OnNotify( )也試圖將消息映射到制造通知的控件;如果映射不成功, OnNotify( )就調(diào)用相同的OnCmdMsg( )函數(shù)。
            ??? 7) 根據(jù)接收消息的類,OnCmdMsg( )將在一個稱為命令傳遞(Command Routing)的過程中潛在地傳遞命令消息和控件通知。例如,如果擁有該窗口的類是一個框架類,則命令和通知消息也被傳遞到視圖和文檔類,并為該類尋找一個消息處理函數(shù)。
            為什么要消息映像?
            ??? 這畢竟是C++語言;為什么OnWndMsg( )不為每個窗口消息調(diào)用一個預(yù)定義的虛擬函數(shù)?因為它太占CPU。若是那樣,當(dāng)掃描一個消息映像以加速該過程時,OnWndMsg( )可能會做出意想不到的事情,并陷入?yún)R編器。注意通過重載WindowProc( )、OnWndMsg( )、OnCommand( )、OnNotify( ) 或OnCmdMsg( )可以修改這一過程。重載OnWndMsg( )可以在窗口消息被排序之前插入該過程。重載OnCommand( )或OnNotify( )可以在消息被反射之前插入該過程。

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

            2007年3月6日

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

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

            久久精品成人国产午夜| 99久久人人爽亚洲精品美女 | 粉嫩小泬无遮挡久久久久久 | 伊人久久无码精品中文字幕| 久久天堂电影网| 国内精品久久久久影院优| 婷婷综合久久中文字幕蜜桃三电影| 一级做a爰片久久毛片免费陪| 久久婷婷五月综合色99啪ak| 精品久久人人爽天天玩人人妻 | 日本精品久久久久影院日本 | 久久精品无码一区二区WWW| 亚洲综合久久夜AV | 狠狠色丁香久久婷婷综合蜜芽五月| 亚洲国产成人精品女人久久久 | 国产精品美女久久久m| 国产成人无码精品久久久性色| 亚洲中文字幕无码一久久区| 亚洲AV日韩AV天堂久久| 99re久久精品国产首页2020| 亚洲精品高清国产一久久| 99久久国产综合精品五月天喷水| 精品久久综合1区2区3区激情| 国产午夜精品久久久久九九电影| 久久93精品国产91久久综合| 亚洲日本va午夜中文字幕久久| 久久综合偷偷噜噜噜色| 欧美大香线蕉线伊人久久| 成人久久久观看免费毛片| 国产伊人久久| 久久精品中文字幕一区| 国产成人久久精品一区二区三区| 91久久精品国产成人久久| 国内精品伊人久久久影院| 久久精品亚洲中文字幕无码麻豆| 国产成人综合久久久久久| 精品一二三区久久aaa片| 精品久久一区二区三区| 日日狠狠久久偷偷色综合96蜜桃| 青草国产精品久久久久久| 久久精品无码一区二区三区免费|