用Visual C++打造IE瀏覽器
前言
IE瀏覽器作為微軟Windows系統(tǒng)捆綁銷售的一個(gè)瀏覽工具,用來瀏覽千姿百態(tài)的網(wǎng)頁(yè),目前它已經(jīng)占據(jù)了瀏覽器市場(chǎng)的半壁江山,成為Windows用戶不可或缺的工具。首先,它的界面設(shè)計(jì)的很漂亮,如扁平按紐(按鈕上的圖像為灰色,當(dāng)鼠標(biāo)放在按鈕上時(shí),按鈕突起,這種狀態(tài)稱為手柄,并且其上的圖像變得鮮艷醒目)、按鈕上的文字說明以及按鈕邊 上的小黑三角形狀的下拉箭頭(單擊時(shí)顯示下拉菜單)、工具條上的地址輸入欄等,都體現(xiàn)了Windows2000的風(fēng)格;其次,它的收藏欄可以收藏用戶喜愛的網(wǎng)絡(luò)地 址,這一切都為IE的流行打下了堅(jiān)實(shí)的基礎(chǔ)。說了那么多,也許讀者朋友們感覺到IE實(shí)現(xiàn)起來一定非常困難,其實(shí)IE也是個(gè)"紙老虎",實(shí)現(xiàn)它的難點(diǎn)主要是 在界面效果和顯示收藏夾上,筆者在本文中有針對(duì)性的敘述了IE界面、收藏網(wǎng)頁(yè)的顯示、網(wǎng)頁(yè)的瀏覽等功能的實(shí)現(xiàn),仔細(xì)看過這篇文章后,相信讀者朋友們一定可以打造出一個(gè)屬于自己的瀏覽器。本文中的代碼在Windows2000、Visual C++6.0環(huán)境下編譯通過,程序運(yùn)行正常。程序運(yùn)行界面如下:
圖一、瀏覽器的運(yùn)行界面
一、瀏覽器的界面實(shí)現(xiàn)
首先啟動(dòng)Visual C++6.0,生成一個(gè)名為MYIE單文檔項(xiàng)目,注意在此過程中不要選擇工具條和狀態(tài)條選項(xiàng),這樣才能更方便我們?cè)诤罄m(xù)工作中用代碼實(shí)現(xiàn)Windwos2000風(fēng)格的工具條、狀態(tài)條;在工具條中添加地址欄;項(xiàng)目的視圖類的基類為ChtmlView,該類的Navigate2()成員函數(shù)專門用來現(xiàn)實(shí)超文本格式的文檔。在主框架類CmainFrame中定義CStatusBar m_wndStatusBar(狀態(tài)條對(duì)象)、CToolBar m_wndToolBar(工具欄對(duì)象)、CReBar m_wndReBar(、CComboBoxEx m_wndAddress(擴(kuò)展的組合框?qū)ο螅脕碜鳛榈刂窓冢?、CAnimateCtrl m_wndAnimate(動(dòng)畫控件,用來在工具欄上顯示動(dòng)畫)、圖像列表對(duì)象CImageList img(存放顯示在工具欄上的圖標(biāo))等對(duì)象。向當(dāng)前項(xiàng)目AVI資源文件,ID標(biāo)志IDR_MFCAVI,添加Bitmap(位圖)資源,ID標(biāo)志分別為 IDB_COLDTOOLBAR、IDB_HOTTOOLBAR,分別如下所示:
圖二、包含按鈕圖標(biāo)的位圖
1)IE風(fēng)格工具條
IE風(fēng)格界面的實(shí)現(xiàn)主要在主框架類的CMainFrame.:OnCreate()函數(shù)中實(shí)現(xiàn),它的主要思想如下:CReBar對(duì)象用來作為工具條、地址欄、動(dòng)畫控件的容器,CImageList對(duì)象,然后分別裝載工具欄上按鈕的熱點(diǎn)圖像和正常狀態(tài)下顯示的圖像,并將該對(duì)象附給工具條對(duì)象,使之建立關(guān)聯(lián)。為了顯示扁平工具欄,需要用CreateEx()函數(shù)創(chuàng)建CToolBar對(duì)象m_wndToolBar,用ModifyStyle()函數(shù)將工具欄的風(fēng)格設(shè)為扁平類型,注意這里不能用CToolBar::Create()或CToolBar:: SetBarStyle()設(shè)置這種新風(fēng)格。CToolBar類不支持TBSTYLE_FLAT,要解決這個(gè)問題,必須繞過CToolBar類,使用CWnd::ModifyStyle()。要將某一個(gè)工具欄按鈕設(shè) 置為附帶有下拉按鈕,可以調(diào)用SetButtonInfo()設(shè)置按鈕的風(fēng)格為TBSTYLE_DROPDOWN。至于按鈕帶有中文提示,用工具欄的SetButtonText()就可以輕松實(shí)現(xiàn)了。下面是實(shí)現(xiàn)IE風(fēng)格界面的代碼和注釋:
int CMainFrame.:OnCreate(LPCREATESTRUCT lpCreateStruct)
{
CImageList img; //圖像列表對(duì)象;
CString str; //字符串對(duì)象;
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndReBar.Create(this)) //創(chuàng)建CReBar對(duì)象;
{
TRACE0("Failed to create rebar ");
return -1;
}
if (!m_wndToolBar.CreateEx(this)) //使用CreateEx()函數(shù)創(chuàng)建工具條對(duì)象;
{
TRACE0("Failed to create toolbar ");
return -1;
}
//設(shè)置工具欄中的按鈕最大最小尺寸;
m_wndToolBar.GetToolBarCtrl().SetButtonWidth(50, 150);
//設(shè)置工具欄上的按鈕支持下拉箭頭風(fēng)格;
m_wndToolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
//向圖像列表裝載熱點(diǎn)圖像資源,IDB_HOTTOOLBAR為熱點(diǎn)圖像資源ID
img.Create(IDB_HOTTOOLBAR, 22, 0, RGB(255, 0, 255));
m_wndToolBar.GetToolBarCtrl().SetHotImageList(&img);
img.Detach();
//圖象列表裝載正常狀態(tài)的圖像資源,IDB_COLDTOOLBAR為圖像資源ID
img.Create(IDB_COLDTOOLBAR, 22, 0, RGB(255, 0, 255));
m_wndToolBar.GetToolBarCtrl().SetImageList(&img);
img.Detach();
//設(shè)置工具條為扁平風(fēng)格
m_wndToolBar.ModifyStyle(0, TBSTYLE_FLAT | TBSTYLE_TRANSPARENT);
//設(shè)置工具條上的按鈕個(gè)數(shù)為9個(gè);
m_wndToolBar.SetButtons(NULL, 9);
// 裝載字符串資源,設(shè)置按鈕上的文本和按鈕的標(biāo)識(shí)號(hào);
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 |
BSTYLE_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 | BSTYLE_DROPDOWN, 8);
str.LoadString(IDS_FONT);
m_wndToolBar.SetButtonText(8, str);
// 設(shè)置工具欄上的按鈕尺寸和顯示在按鈕上的圖標(biāo)尺寸;
CRect rectToolBar;
m_wndToolBar.GetItemRect(0, &rectToolBar);
m_wndToolBar.SetSizes(rectToolBar.Size(), CSize(30,20));
//創(chuàng)建組合框,用來作為地址欄;
if (!m_wndAddress.Create(CBS_DROPDOWN | WS_CHILD, CRect(0, 0, 200, 120), this, AFX_IDW_TOOLBAR + 1))
{
TRACE0("Failed to create combobox ");
return -1;
}
//創(chuàng)建動(dòng)畫控件對(duì)象,并打開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);
//將工具條、地址欄、動(dòng)畫控件等添加到CReBar對(duì)象中;
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);
//再次設(shè)置工具條風(fēng)格,使之有工具欄提示功能;
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle()|CBRS_TOOLTIPS| BRS_FLYBY | CBRS_SIZE_FIXED);
//設(shè)置狀態(tài)條;
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT)))
{
TRACE0("Failed to create status bar ");
return -1;
}
…….//實(shí)現(xiàn)"Favorites"菜單的部分,該部分在第二部分介紹;
return 0;
}
2)工具條上的下拉菜單
當(dāng)用戶點(diǎn)擊按鈕上的下拉箭頭時(shí),將出現(xiàn)相應(yīng)的菜單,為了實(shí)現(xiàn)這個(gè)功能,手下需要在CMainFrame.cpp文件的消息映射中添加消息映射:ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnDropDown);在CmainFrame.h文件中添加消息映射函數(shù)聲明: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)
{
//判斷是否為顯示收藏網(wǎng)頁(yè)的下拉箭頭;
CMenu* pPopup;
pPopup = GetMenu()->GetSubMenu(3);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, rect.left, rect.top + 1, AfxGetMainWnd());
}
*pResult = TBDDRET_DEFAULT;
}
3)工具條上的動(dòng)畫實(shí)現(xiàn)
為了美化程序的界面,程序的復(fù)合工具條上放置了一個(gè)動(dòng)畫控件,用來在適當(dāng)?shù)臅r(shí)機(jī)播放一個(gè)動(dòng)畫片段,實(shí)現(xiàn)動(dòng)畫效果。下面的代碼實(shí)現(xiàn)了創(chuàng)建動(dòng)畫控件對(duì)象,并打開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()等函數(shù)用來為播放視頻文件服務(wù),它們使用起來都非常簡(jiǎn)單,這里就不再贅述了。
4)地址欄的操作
當(dāng)用戶在地址欄上輸入網(wǎng)頁(yè)地址并按下回車鍵后,瀏覽器將顯示該網(wǎng)頁(yè)的內(nèi)容,并將在地址欄中記錄下該地址。因?yàn)榛剀囨I按下后對(duì)應(yīng)的消息ID為IDOK,為 此,需要在CmainFrame類中添加消息映射ON_COMMAND(IDOK, OnNewAddressEnter)和消息響應(yīng)函數(shù)afx_msg void OnNewAddressEnter()。該函數(shù)實(shí)現(xiàn)代碼如下:
void CMainFrame.:OnNewAddressEnter()
{
CString str;
//獲取地址欄中的字符串;
m_wndAddress.GetEditCtrl()->GetWindowText(str);
((CMfcieView*)GetActiveView())->Navigate2(str, 0, NULL);//顯示該網(wǎng)頁(yè);
//將該網(wǎng)址添加到地址欄對(duì)應(yīng)的組合框中;
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)和消息響應(yīng)函數(shù)OnNewAddress,用來處理用戶從地址欄組合框中選擇網(wǎng)址的操作,該函數(shù)的實(shí)現(xiàn)代碼如下:
void CMainFrame.:OnNewAddress()
{
CString str;
m_wndAddress.GetLBText(m_wndAddress.GetCurSel(), str);
((CMYIEView*)GetActiveView())->Navigate2(str, 0, NULL);
}
二、實(shí)現(xiàn)收藏菜單
一般IE的用戶都有個(gè)習(xí)慣,那就是將自己喜歡的網(wǎng)址保存起來,以方便今后快速的登陸,為了使我們的瀏覽器能夠顯示IE收藏過的網(wǎng)址,程序中設(shè)置了一個(gè)"Favorites"菜單,通過RegOpenKey()、RegQueryValueEx()等函數(shù)操作Windows的注冊(cè)表中的HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorerUser Shell Folders項(xiàng),將收藏的網(wǎng)址顯示到菜單上。為此,筆者定義了兩個(gè)函數(shù),實(shí)現(xiàn)代碼如下所示:
TCHAR GetDir( ) //得到存放用戶收藏網(wǎng)址的目錄;
{
TCHAR sz[MAX_PATH];
TCHAR szPath[MAX_PATH];
HKEY hKey;
DWORD dwSize;
CMenu* pMenu;
// 得到"Favorites"菜單,并刪除空白的子菜單項(xiàng);
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("SoftwareMicrosoftWindowsCurrentVersionExplorerUser Shell Folders"), &hKey) != ERROR_SUCCESS)
{
TRACE0("Favorites folder not found ");
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 += "*.*";
// 掃描當(dāng)前目錄,首先搜索*.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);
// 判斷是否已經(jīng)重復(fù);
for(nPos = nStartPos ; nPos < nEndPos ; ++nPos)
{
if(str.CompareNoCase(astrFavorites[nPos]) < 0)
break;
}
astrFavorites.InsertAt(nPos, str);//添加該字符串;
m_astrFavoriteURLs.InsertAt(nPos, buf);//保留相應(yīng)的地址
++nEndPos;
}
}
} while(FindNextFile(h, &wfd));
FindClose(h);
// 將找到的項(xiàng)目添加到菜單中;
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)
{
//對(duì)目錄進(jìn)行搜索;
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;
}
三、顯示超文本
微軟ChtmView類的Navigate2函數(shù)可以實(shí)現(xiàn)超文本文件的顯示,GoBack()、GoForward()等函數(shù)可以分別實(shí)現(xiàn)網(wǎng)頁(yè)瀏覽的回退和前進(jìn)操作。以響應(yīng)"Favorite"菜單項(xiàng)為例,需要在程序的CmainFrame類中添加消息映射 ON_COMMAND_RANGE(0xe00, 0xfff, OnFavorite)和消息響應(yīng)函數(shù)OnFavorite,來響應(yīng)ID為0xe00-0xfff范圍內(nèi)的菜單單擊處理,具體實(shí)現(xiàn)代碼如下:
void CMainFrame.:OnFavorite(UINT nID)
{
((CMYIEView*)GetActiveView())->Navigate2(m_astrFavoriteURLs[nID-0xe00],0,NULL);
}
四、小結(jié)
上面的代碼相對(duì)較多,對(duì)某些函數(shù)的使用不清楚的話,請(qǐng)參考MSDN,它包含了高級(jí)界面處理、注冊(cè)表的操作等內(nèi)容,也許剛開始看起來可能感到有些困難,但是如果讀者朋友細(xì)細(xì)品味的話,一定可以學(xué)到一些東西,對(duì)今后程序的界面開發(fā)有所幫助。
來源:Etta0次