原文
介绍
本教程帮助你用IE
引擎构建
一个基于WTL
,并使用我编写的处理IWebBrowser2
接口包装类的迷你浏览器
.
因为知道代码可能很难读
,因此本教程帮助你逐步开发
一个迷你浏览器
.
背景
大部分项
都与互联网浏览
有关.我常用超文本
视图使用SDI
.
有时,我要用真正的浏览器函数
,因此我为IWebBrowser2
编写了一个包装器.
此包装类
可处理在IE
中嵌入的窗口
.它还可非常简单
的处理事件沉
(如:OnDocumentComplete
).
创建新项
首先创建一个新的WTL
项.我假设你已安装了WTL
文件.在向导的第一页
上,选择一个SDI
应用,并勾选生成.CPP
文件.
在第二页上,按超文本
视图更改默认视图
.
第一步是编辑stdafx.h
.请包括atlmisc.h
(会不时使用CString
)和atlctrlx.h
(CMultiPaneStatusBarCtrl
).
还需要注释掉_ATL_DLL
定义(不想可执行文件
依赖atl.dll
)并按5
版本更改IE
版本.
//更改这些值以使用不同版本
#define WINVER 0x0400
//`#define_WIN32_WINNT0x0400`
#define _WIN32_IE 0x0500
#define _RICHEDIT_VER 0x0100//这是在`ATL7`中`管理`浏览器所必需的
//#define _ATL_DLL#include <atlbase.h>
#include <atlapp.h>
extern CAppModule _Module;
#include <atlcom.h>
#include <atlhost.h>
#include <atlwin.h>
#include <atlctl.h>
#include <atlmisc.h>//..
#include <atlframe.h>
#include <atlctrls.h>
#include <atldlgs.h>
#include <atlctrlw.h>
#include <atlctrlx.h>//..
//`{{AFX_INSERT_LOCATION}}MicrosoftVisualC++`插入在上一行前立即其他声明.
更新视图
在视图类
中,需要包含browser.h
,并从它继承视图类
.还需要链接
它到消息映射
,这样该类
可处理多条消息(WM_CREATE,WM_DESTROY)
.
#include "browser.h"
class CWTLBrowserView : public CWindowImpl<CWTLBrowserView, CAxWindow>,public CWebBrowser2<CWTLBrowserView>//..
{
public:DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())BOOL PreTranslateMessage(MSG* pMsg);BEGIN_MSG_MAP(CWTLBrowserView)CHAIN_MSG_MAP(CWebBrowser2<CWTLBrowserView>)//..END_MSG_MAP()//处理器原型(如果需要,请取消注释参数):`LRESULTMessageHandler(UINT/*uMsg*/,WPARAM/*wParam*/,LPARAM/*lParam*/,BOOL&/*bHandled*/)LRESULTCommandHandler(WORD/*wNotifyCode*/,WORD/*wID*/,HWND/*hWndCtl*/,BOOL&/*bHandled*/)LRESULTNotifyHandler(int/*idCtrl*/,LPNMHDR/*pnmh*/,BOOL&/*bHandled*/)`
};
创建菜单
要在菜单
中添加一些新项
.典型的浏览器处理back,forward,home,stop(后退,前进,主页,停止)
和刷新
.在菜单和工具栏
中添加这些命令
.
因为有时禁止使用某些项
,因此需要处理它们的UI
(不能总是使用后退和前进
).首先,要在UI
更新映射中(在mainfrm.h
中)添加它们.
UPDATE_ELEMENT(ID_VIEW_GOTO_BACK, UPDUI_MENUPOPUP|UPDUI_TOOLBAR)
UPDATE_ELEMENT(ID_VIEW_GOTO_FORWARD, UPDUI_MENUPOPUP|UPDUI_TOOLBAR)
通过OnIdle
函数更新它们
.
UIEnable(ID_VIEW_GOTO_BACK,m_view.CanBack());
UIEnable(ID_VIEW_GOTO_FORWARD,m_view.CanForward());
CWebBrowser2
公开了2个函数(CanBack
和CanForward
),可确定后退和前进
动作的状态.
因为默认向导
在microsoft.com
启动,因此需要将按about:blank
更改代码,并从正常主页
开始.需要更改CMainFrame::OnCreate
中的代码.
m_hWndClient = m_view.Create(m_hWnd, rcDefault,_T("about:blank"),WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |WS_HSCROLL | WS_VSCROLL, WS_EX_CLIENTEDGE);
.
.
.
m_view.GoHome();
return 0;
创建地址栏
现在有个工作帧
,但仍无法在那里
输入URL
!为此,创建一个用户可输入URL
浏览的地址栏
.给主框架添加新的叫m_URL
的(CEdit)
成员变量.
从CMainFrame::OnCreate
创建并初化它
.因为还想地址栏
自动补全,因此在编辑
控件上使用了SHAutoComplete
函数.
CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
AddSimpleReBarBand(hWndCmdBar);
AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
CString szAddress;//<<
szAddress.LoadString(IDS_ADDRESS);
m_URL.Create(m_hWnd,CRect(0,0,0,18),NULL,WS_CHILD|WS_VISIBLE,WS_EX_STATICEDGE);
AddSimpleReBarBand(m_URL,szAddress.GetBuffer(0),TRUE);
m_URL.SetFont(AtlGetDefaultGuiFont());
SHAutoComplete(m_URL,SHACF_URLALL);//>>CreateSimpleStatusBar();
如果试编译项
,则在链接文件
时会出现错误.出现此错误的原因是SHAutoComplete
是从shlwapi.dll
导出的.要解决它,需要链接(shlwapi.lib)
库.
编译项后,看到新的编辑栏
等待输入.但是,嘿!如果试按回车
键,浏览器只会一直休息
!来修复它!
浏览
因为超文本
框架把所有键击转发
到超文本
文档,因此不能只等待WM_CHAR
消息.需要在预翻译消息
函数中添加一些代码
.
需要从地址栏
中取WM_CHAR
消息并处理VK_RETURN
符.
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))return TRUE;if (pMsg->message==WM_CHAR && m_URL==pMsg->hwnd){switch (pMsg->wParam){case VK_RETURN:{CString szURL;int nLength=m_URL.GetWindowTextLength();m_URL.GetWindowText(szURL.GetBuffer(nLength),nLength+1);szURL.ReleaseBuffer();m_view.Navigate(szURL);return TRUE;}}return FALSE;}return m_view.PreTranslateMessage(pMsg);
}
命令
现在,可输入URL
并浏览它,来观察浏览器
.但仍缺少.不可使用back,forward
和其他新命令
?在主框架
类中需要添加它们的函数
.
调整UI
测试
一下浏览器.试转到http://www.codeproject.com/
并点击WTL
部分.为什么在地址栏
中看不到新位置
,要修复它,需要在每次更改位置
时更新地址栏
.
一个好地方
是处理视图
类中的OnNavigateComplete2
.因为需要更新,在主框架
类中保存的m_URL
,因此在视图
类中创建它的新引用
,并给构造器
传递变量.
现在,可处理消息
并更新地址栏
.
void CWTLBrowserView::OnNavigateComplete2(IDispatch* pDisp, const String& szURL)
{m_URL.SetWindowText(GetLocationURL());
}
另一个有用调整
包括进度通知
,安全图标
和状态栏文本
.它们都在状态栏
上.该推出CMultiPaneStatusBarCtrl
!
在主框架
类中创建一个新变量
,并在视图
类中创建一个引用
.状态栏
应包含3个部分
:默认文本,安全图标和进度通知
.
因为默认文本
有唯一的ID(ID_DEFAULT_PANE)
,因此只需要创建另外两个标识
.在"View->ResourceSymbols"
菜单中,需要创建新符号
:IDR_LOCK
和IDR_PROGRESS
.
创建它们
后,可从CMainFrame::OnCreate
函数初化新状态栏
.
CreateSimpleStatusBar();
m_StatusBar.SubclassWindow(m_hWndStatusBar);
int nPanes[]={ID_DEFAULT_PANE,IDR_LOCK,IDR_PROGRESS};
m_StatusBar.SetPanes(nPanes,sizeof(nPanes)/sizeof(int),false);
m_StatusBar.SetPaneWidth(IDR_LOCK,30);
m_StatusBar.SetPaneWidth(IDR_PROGRESS,50);
还需要给项添加新(IDI_LOCK)
图标,并在(m_hSecured)
变量中加载它
.为了正确更新UI
,在UI
更新映射中添加一行新行
:
UPDATE_ELEMENT(0, UPDUI_STATUSBAR)
在视图类
中,添加一个新的(m_bSecured)
变量和一些代码
来处理更新状态栏
:
void CWTLBrowserView::OnStatusTextChange(const String& szText)
{m_StatusBar.SetPaneText(ID_DEFAULT_PANE,szText);
}
void CWTLBrowserView::OnProgressChange(long nProgress, long nProgressMax)
{CString szText;if (nProgressMax>0)szText.Format(_T("%d%%"),nProgress*100/nProgressMax);m_StatusBar.SetPaneText(IDR_PROGRESS,szText);
}
void CWTLBrowserView::OnSetSecureLockIcon(long nSecureLockIcon)
{m_bSecured=nSecureLockIcon>0;
}
最后,需要给CMainFrame::OnIdle
添加一些代码
:
m_StatusBar.SetPaneIcon(IDR_LOCK,m_view.IsSecured()? m_hSecured : NULL);
UIUpdateToolBar();
UIUpdateStatusBar();
return FALSE;
文件
命令
要允许浏览器保存和打印文件
,需要处理文件消息
.要给浏览器发送命令
,要用ExecWB
函数.要(对UI
映射)查询命令状态
,可用QueryStatusWB
函数.
为了保存/打印
,需要添加适当的函数
,更新UI
更新映射,并在OnIdle
函数处理它们的UI
.
编辑命令
编辑命令
是一个特例.因为可与浏览器
及地址栏
一起使用,因此需要在每次使用这些命令
时检查焦点窗口
.
首先,在UI
更新映射中添加它们
,然后通过OnIdle
函数处理更新
:
if (GetFocus()==m_URL)
{DWORD dwSelection=m_URL.GetSel();BOOL bEnable=HIWORD(dwSelection)!=LOWORD(dwSelection);UIEnable(ID_EDIT_CUT,bEnable);UIEnable(ID_EDIT_COPY,bEnable);if (m_URL.OpenClipboard()){UIEnable(ID_EDIT_PASTE,IsClipboardFormatAvailable(CF_TEXT));CloseClipboard();}elseUIEnable(ID_EDIT_PASTE,FALSE);UIEnable(ID_EDIT_UNDO,m_URL.CanUndo());
}
else
{UIEnable(ID_EDIT_CUT,m_view.QueryStatusWB(OLECMDID_CUT) & OLECMDF_ENABLED);UIEnable(ID_EDIT_COPY,m_view.QueryStatusWB(OLECMDID_COPY) & OLECMDF_ENABLED);UIEnable(ID_EDIT_PASTE,m_view.QueryStatusWB(OLECMDID_PASTE) & OLECMDF_ENABLED);UIEnable(ID_EDIT_UNDO,m_view.QueryStatusWB(OLECMDID_UNDO) & OLECMDF_ENABLED);
}
还需要在执行实际命令
时区分它们
:
LRESULT CMainFrame::OnEditCut(WORD /*`wNotifyCode`*/, WORD /*`wID`*/, HWND /*`hWndCtl`*/, BOOL& /*b已处理*/)
{if (GetFocus()==m_URL)m_URL.Cut();elsem_view.ExecWB(OLECMDID_CUT,OLECMDEXECOPT_DONTPROMPTUSER,NULL,NULL);return 0;
}
LRESULT CMainFrame::OnEditCopy(WORD /*`wNotifyCode`*/, WORD /*`wID`*/, HWND /*`hWndCtl`*/, BOOL& /*b已处理*/)
{if (GetFocus()==m_URL)m_URL.Copy();elsem_view.ExecWB(OLECMDID_COPY,OLECMDEXECOPT_DONTPROMPTUSER,NULL,NULL);return 0;
}
LRESULT CMainFrame::OnEditPaste(WORD /*`wNotifyCode`*/, WORD /*`wID`*/, HWND /*`hWndCtl`*/, BOOL& /*b已处理*/)
{if (GetFocus()==m_URL)m_URL.Paste();elsem_view.ExecWB(OLECMDID_PASTE,OLECMDEXECOPT_DONTPROMPTUSER,NULL,NULL);return 0;
}
LRESULT CMainFrame::OnEditUndo(WORD /*`wNotifyCode`*/, WORD /*`wID`*/, HWND /*`hWndCtl`*/, BOOL& /* b已处理*/)
{if (GetFocus()==m_URL)m_URL.Undo();elsem_view.ExecWB(OLECMDID_UNDO,OLECMDEXECOPT_DONTPROMPTUSER,NULL,NULL);return 0;
}
见,wtlbrowser_demo
.