利用Visual C++打造自己的IE瀏覽器
2005-06-29 09:03作者:劉濤出處:天極網責任編輯:方舟
IE瀏覽器作爲微軟Windows系統捆綁銷售的一個瀏覽工具,用來瀏覽千姿百態的網頁,目前它已經佔據了瀏覽器市場的半壁江山,成爲Windows用戶不可或缺的工具。首先,它的界面設計的很漂亮,如扁平按紐(按鈕上的圖像爲灰色,當鼠標放在按鈕上時,按鈕突起,這種狀態稱爲手柄,並且其上的圖像變得鮮豔醒目)、按鈕上的文字說明以及按鈕邊上的小黑三角形狀的下拉箭頭(單擊時顯示下拉菜單)、工具條上的地址輸入欄等,都體現了Windows2000的風格;其次,它的收藏欄可以收藏用戶喜愛的網絡地址,這一切都爲IE的流行打下了堅實的基礎。說了那麼多,也許讀者朋友們感覺到IE實現起來一定非常困難,其實IE也是個"紙老虎",實現它的難點主要是在界面效果和顯示收藏夾上,筆者在本實例中有針對性的敘述了IE界面、收藏網頁的顯示、網頁的瀏覽等功能的實現,仔細看過這篇文章後,相信讀者朋友們一定可以打造出一個屬於自己的瀏覽器。本實例中的程序代碼編譯運行後的界面效果如圖一所示:
圖一、瀏覽器的運行界面 |
一、實現方法
1、瀏覽器的界面實現
首先啓動Visual C++6.0,生成一個名爲mfcie單文檔項目,注意在此過程中不要選擇工具條和狀態條選項,這樣才能更方便我們在後續工作中用代碼實現Windwos2000風格的工具條、狀態條;在工具條中添加地址欄;項目的視圖類的基類設置爲ChtmlView,該類的Navigate2()成員函數專門用來現實超文本格式的文檔。在主框架類CmainFrame中定義CStatusBar m_wndStatusBar(狀態條對象)、CToolBar m_wndToolBar(工具欄對象)、CReBar m_wndReBar(、CComboBoxEx m_wndAddress(擴展的組合框對象,用來作爲地址欄)、CAnimateCtrl m_wndAnimate(動畫控件,用來在工具欄上顯示動畫)、圖像列表對象CImageList img(存放顯示在工具欄上的圖標)等對象。向當前項目AVI資源文件,ID標誌IDR_MFCAVI,添加Bitmap(位圖)資源,ID標誌分別爲IDB_COLDTOOLBAR、IDB_HOTTOOLBAR,分別如下所示:
圖二、包含按鈕圖標的位圖 |
1)IE風格工具條
IE風格界面的實現主要在主框架類的CMainFrame::OnCreate()函數中實現,它的主要思想如下: CReBar對象用來作爲工具條、地址欄、動畫控件的容器,CImageList對象然後分別裝載工具欄上按鈕的熱點圖像和正常狀態下顯示的圖像,並將該對象附給工具條對象,使之建立關聯。爲了顯示扁平工具欄,需要用CreateEx()函數創建CToolBar對象m_wndToolBar,用ModifyStyle()函數將工具欄的風格設爲扁平類型,注意這裏不能用CToolBar::Create() 或 CToolBar:: SetBarStyle()設置這種新風格。CToolBar 類不支持TBSTYLE_FLAT,要解決這個問題,必須繞過CToolBar類,使用CWnd::ModifyStyle()。要將某一個工具欄按鈕設置爲附帶有下拉按鈕,可以調用SetButtonInfo()設置按鈕的風格爲TBSTYLE_DROPDOWN。至於按鈕帶有中文提示,用工具欄的SetButtonText()就可以輕鬆實現了。下面是實現IE風格界面的代碼和註釋:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { CImageList img; //圖像列表對象; CString str; //字符串對象; if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndReBar.Create(this)) //創建CReBar對象; { TRACE0("Failed to create rebar/n"); return -1; } if (!m_wndToolBar.CreateEx(this)) //使用CreateEx()函數創建工具條對象; { TRACE0("Failed to create toolbar/n"); return -1; } //設置工具欄中的按鈕最大最小尺寸; m_wndToolBar.GetToolBarCtrl().SetButtonWidth(50, 150); //設置工具欄上的按鈕支持下拉箭頭風格; m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS); //向圖像列表裝載熱點圖像資源,IDB_HOTTOOLBAR爲熱點圖像資源ID img.Create(IDB_HOTTOOLBAR, 22, 0, RGB(255, 0, 255)); m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img); img.Detach(); //圖象列表裝載正常狀態的圖像資源,IDB_COLDTOOLBAR爲圖像資源ID img.Create(IDB_COLDTOOLBAR, 22, 0, RGB(255, 0, 255)); m_wndToolBar.GetToolBarCtrl().SetImageList(&img); img.Detach(); //設置工具條爲扁平風格 m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT | TBSTYLE_TRANSPARENT); //設置工具條上的按鈕個數爲9個; m_wndToolBar.SetButtons(NULL, 9); // 裝載字符串資源,設置按鈕上的文本和按鈕的標識號; m_wndToolBar.SetButtonInfo(0, ID_GO_BACK, TBSTYLE_BUTTON, 0); str.LoadString(IDS_BACK); m_wndToolBar.SetButtonText(0, str); m_wndToolBar.SetButtonInfo(1, ID_GO_FORWARD, TBSTYLE_BUTTON, 1); str.LoadString(IDS_FORWARD); m_wndToolBar.SetButtonText(1, str); m_wndToolBar.SetButtonInfo(2, ID_VIEW_STOP, TBSTYLE_BUTTON, 2); str.LoadString(IDS_STOP); m_wndToolBar.SetButtonText(2, str); m_wndToolBar.SetButtonInfo(3, ID_VIEW_REFRESH, TBSTYLE_BUTTON, 3); str.LoadString(IDS_REFRESH); m_wndToolBar.SetButtonText(3, str); m_wndToolBar.SetButtonInfo(4, ID_GO_START_PAGE, TBSTYLE_BUTTON, 4); str.LoadString(IDS_HOME); m_wndToolBar.SetButtonText(4, str); m_wndToolBar.SetButtonInfo(5, ID_GO_SEARCH_THE_WEB, TBSTYLE_BUTTON, 5); str.LoadString(IDS_SEARCH); m_wndToolBar.SetButtonText(5, str); m_wndToolBar.SetButtonInfo(6, ID_FAVORITES_DROPDOWN,TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 6); str.LoadString(IDS_FAVORITES); m_wndToolBar.SetButtonText(6, str); m_wndToolBar.SetButtonInfo(7, ID_FILE_PRINT, TBSTYLE_BUTTON, 7); str.LoadString(IDS_PRINT); m_wndToolBar.SetButtonText(7, str); m_wndToolBar.SetButtonInfo(8, ID_FONT_DROPDOWN, TBSTYLE_BUTTON | TBSTYLE_DROPDOWN, 8); str.LoadString(IDS_FONT); m_wndToolBar.SetButtonText(8, str); // 設置工具欄上的按鈕尺寸和顯示在按鈕上的圖標尺寸; CRect rectToolBar; m_wndToolBar.GetItemRect(0, &rectToolBar); m_wndToolBar.SetSizes(rectToolBar.Size(), CSize(30,20)); //創建組合框,用來作爲地址欄; if (!m_wndAddress.Create(CBS_DROPDOWN | WS_CHILD, CRect(0, 0, 200, 120), this, AFX_IDW_TOOLBAR + 1)) { TRACE0("Failed to create combobox/n"); return -1; } //創建動畫控件對象,並打開AVI資源IDR_MFCAVI; m_wndAnimate.Create(WS_CHILD | WS_VISIBLE, CRect(0, 0, 10, 10), this, AFX_IDW_TOOLBAR + 2); m_wndAnimate.Open(IDR_MFCAVI); //將工具條、地址欄、動畫控件等添加到CReBar對象中; m_wndReBar.AddBar(&m_wndToolBar); m_wndReBar.AddBar(&m_wndAnimate, NULL, NULL, RBBS_FIXEDSIZE | RBBS_FIXEDBMP); str.LoadString(IDS_ADDRESS); m_wndReBar.AddBar(&m_wndAddress, str, NULL, RBBS_FIXEDBMP | RBBS_BREAK); //再次設置工具條風格,使之有工具欄提示功能; m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_FIXED); //設置狀態條; if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar/n"); return -1; } …….//實現"Favorites"菜單的部分,該部分在第二部分介紹; return 0; } |
2)工具條上的下拉菜單
當用戶點擊按鈕上的下拉箭頭時,將出現相應的菜單,爲了實現這個功能,首先需要在CMainFrame.cpp文件的消息映射中添加消息映射:ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnDropDown);在CmainFrame.h文件中添加消息映射函數聲明:afx_msg void OnDropDown(NMHDR* pNotifyStruct, LRESULT* pResult);最後添加下面的代碼:
void CMainFrame::OnDropDown(NMHDR* pNotifyStruct, LRESULT* pResult) { NMTOOLBAR* pNMToolBar = (NMTOOLBAR*)pNotifyStruct; CRect rect; // 得到下拉箭頭的位置; m_wndToolBar.GetToolBarCtrl().GetRect(pNMToolBar->iItem, &rect); rect.top = rect.bottom; ::ClientToScreen(pNMToolBar->hdr.hwndFrom, &rect.TopLeft()); if(pNMToolBar->iItem == ID_FONT_DROPDOWN) //判斷是否爲選擇字體的下拉箭頭; { CMenu menu; CMenu* pPopup; menu.LoadMenu(IDR_FONT_POPUP); pPopup = menu.GetSubMenu(0); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, rect.left, rect.top + 1, AfxGetMainWnd()); } else if(pNMToolBar->iItem == ID_FAVORITES_DROPDOWN) {//判斷是否爲顯示收藏網頁的下拉箭頭; CMenu* pPopup; pPopup = GetMenu()->GetSubMenu(3); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, rect.left, rect.top + 1, AfxGetMainWnd()); } *pResult = TBDDRET_DEFAULT; } |
3)工具條上的動畫實現
爲了美化程序的界面,程序的複合工具條上放置了一個動畫控件,用來在適當的時機播放一個動畫片段,實現動畫效果。下面的代碼實現了創建動畫控件對象,並打開AVI資源IDR_MFCAVI:
m_wndAnimate.Create(WS_CHILD | WS_VISIBLE, CRect(0, 0, 10, 10), this, AFX_IDW_TOOLBAR + 2); m_wndAnimate.Open(IDR_MFCAVI); |
CanimateCtrl類提供了Play()、Seek()、Stop()、Close()等函數用來爲播放視頻文件服務,它們使用起來都非常簡單,這裏就不再贅述了。
4)地址欄的操作
當用戶在地址欄上輸入網頁地址並按下回車鍵後,瀏覽器將顯示該網頁的內容,並將在地址欄中記錄下該地址。因爲回車鍵按下後對應的消息ID爲IDOK,爲此,需要在CmainFrame類中添加消息映射ON_COMMAND(IDOK, OnNewAddressEnter)和消息響應函數afx_msg void OnNewAddressEnter()。該函數實現代碼如下:
void CMainFrame::OnNewAddressEnter() { CString str; //獲取地址欄中的字符串; m_wndAddress.GetEditCtrl()->GetWindowText(str); ((CMfcieView*)GetActiveView())->Navigate2(str, 0, NULL);//顯示該網頁; //將該網址添加到地址欄對應的組合框中; COMBOBOXEXITEM item; item.mask = CBEIF_TEXT; item.iItem = -1; item.pszText = (LPTSTR)(LPCTSTR)str; m_wndAddress.InsertItem(&item); } |
同理,還要在CmainFrame類中爲地址欄(ID 爲AFX_IDW_TOOLBAR + 1)添加消息映射ON_CBN_SELENDOK(AFX_IDW_TOOLBAR + 1,OnNewAddress)和消息響應函數OnNewAddress,用來處理用戶從地址欄組合框中選擇網址的操作,該函數的實現代碼如下:
void CMainFrame::OnNewAddress() { CString str; m_wndAddress.GetLBText(m_wndAddress.GetCurSel(), str); ((CMFCIEView*)GetActiveView())->Navigate2(str, 0, NULL); } |
2、實現收藏菜單
一般IE的用戶都有個習慣,那就是將自己喜歡的網址保存起來,以方便今後快速的登陸,爲了使我們的瀏覽器能夠顯示IE收藏過的網址,程序中設置了一個"Favorites"菜單,通過RegOpenKey()、RegQueryValueEx()等函數操作Windows的註冊表中的HKEY_CURRENT_USER//Software//Microsoft//Windows//CurrentVersion//Explorer//User Shell Folders項,將收藏的網址顯示到菜單上。爲此,實例中定義了兩個函數,實現代碼如下所示:
TCHAR GetDir( ) //得到存放用戶收藏網址的目錄; { TCHAR sz[MAX_PATH]; TCHAR szPath[MAX_PATH]; HKEY hKey; DWORD dwSize; CMenu* pMenu; // 得到"Favorites"菜單,並刪除空白的子菜單項; pMenu = GetMenu()->GetSubMenu(3); while(pMenu->DeleteMenu(0, MF_BYPOSITION)); // find out from the registry where the favorites are located. if(RegOpenKey(HKEY_CURRENT_USER, _T("Software//Microsoft//Windows//CurrentVersion//Explorer// User Shell Folders"), &hKey) != ERROR_SUCCESS) { TRACE0("Favorites folder not found/n"); return 0; } dwSize = sizeof(sz); RegQueryValueEx(hKey, _T("Favorites"), NULL, NULL, (LPBYTE)sz, &dwSize); ExpandEnvironmentStrings(sz, szPath, MAX_PATH); RegCloseKey(hKey); Return szPath } int CMainFrame::BuildFavoritesMenu(LPCTSTR pszPath, int nStartPos, CMenu* pMenu) { CString strPath(pszPath); CString strPath2; CString str; WIN32_FIND_DATA wfd; HANDLE h; int nPos; int nEndPos; int nNewEndPos; int nLastDir; TCHAR buf[INTERNET_MAX_PATH_LENGTH]; CStringArray astrFavorites; CStringArray astrDirs; CMenu* pSubMenu; if(strPath[strPath.GetLength() - 1] != _T(’//’)) strPath += _T(’//’); strPath2 = strPath; strPath += "*.*"; // 掃描當前目錄,首先搜索*.URL文件,然後是可能含有*.URL文件的子目錄; h = FindFirstFile(strPath, &wfd); if(h != INVALID_HANDLE_VALUE) { nEndPos = nStartPos; do { if((wfd.dwFileAttributes& (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM))==0) { str = wfd.cFileName; if(str.Right(4) == _T(".url")) {/*URL文件和INI文件格式類似,所以我們可以使用 GetPrivateProfileString() 來得到我們所需要的信息。*/ ::GetPrivateProfileString(_T("InternetShortcut"), T("URL"), _T(""),buf,INTERNET_MAX_PATH_LENGTH, strPath2 + str); str = str.Left(str.GetLength() - 4); // 判斷是否已經重複; for(nPos = nStartPos ; nPos < nEndPos ; ++nPos) { if(str.CompareNoCase(astrFavorites[nPos]) < 0) break; } astrFavorites.InsertAt(nPos, str);//添加該字符串; m_astrFavoriteURLs.InsertAt(nPos, buf);//保留相應的地址 ++nEndPos; } } } while(FindNextFile(h, &wfd)); FindClose(h); // 將找到的項目添加到菜單中; for(nPos = nStartPos ; nPos < nEndPos ; ++nPos) { pMenu->AppendMenu(MF_STRING | MF_ENABLED, 0xe00 + nPos, astrFavorites[nPos]); } // 搜索子目錄 nLastDir = 0; h = FindFirstFile(strPath, &wfd); ASSERT(h != INVALID_HANDLE_VALUE); do { if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //對目錄進行搜索; if(lstrcmp(wfd.cFileName,_T(".")) == 0 || lstrcmp(wfd.cFileName, _T("..")) == 0) continue; for(nPos = 0 ; nPos < nLastDir ; ++nPos) { if(astrDirs[nPos].CompareNoCase(wfd.cFileName) > 0) break; } pSubMenu = new CMenu; pSubMenu->CreatePopupMenu(); // call this function recursively. nNewEndPos = BuildFavoritesMenu(strPath2 + wfd.cFileName, nEndPos, pSubMenu); if(nNewEndPos != nEndPos) { // 插入子菜單; nEndPos = nNewEndPos; pMenu->InsertMenu(nPos, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT)pSubMenu->m_hMenu, wfd.cFileName); pSubMenu->Detach(); astrDirs.InsertAt(nPos, wfd.cFileName); ++nLastDir; } delete pSubMenu; } } while(FindNextFile(h, &wfd)); FindClose(h); } return nEndPos; } |
3、顯示超文本
微軟ChtmView類的Navigate2函數可以實現超文本文件的顯示,GoBack()、GoForward()等函數可以分別實現網頁瀏覽的回退和前進操作。以響應"Favorite"菜單項爲例,需要在程序的CmainFrame類中添加消息映射ON_COMMAND_RANGE(0xe00, 0xfff, OnFavorite)和消息響應函數OnFavorite,來響應ID爲0xe00-0xfff範圍內的菜單單擊處理,具體實現代碼如下:
void CMainFrame::OnFavorite(UINT nID) { ((CMFCIEView*)GetActiveView())->Navigate2(m_astrFavoriteURLs[nID-0xe00], 0, NULL); } |
二、編程步驟
1、 啓動Visual C++6.0,生成一個單文檔視圖結構的應用程序,將該程序命名爲"mfcie",注意在生成項目的過程中不要選擇工具條和狀態條選項,項目的視圖類的基類設置爲ChtmlView;
2、 按照上文中的說明,使用Class Wizard在程序的項目中添加相應的消息響應函數;
3、 向程序的項目中添加AVI、位圖資源;
4、 添加代碼,編譯運行程序。