如果你是一个Windows程序开发员,要实现在一个对话框中显示网页,那很自然会想到用IE控件。在MFC类库里,有CHTMLView类和CHTMLDialog类专门用来加载网页的。但是很多时候,我们觉得用IE控件很不爽,主要是渲染网页速度慢,还有安全规则比较多,有时候会弹出令人恼火的警告提示框出来。那不用IE控件有没有其他的浏览器插件可以代替呢?那当然有,这里就推荐一个用得比较广泛的插件---CEF,这个插件基于谷歌开发的浏览器内核,渲染网页速度绝对不用怀疑,并且支持很多浏览器的新特性,比如对HTML5支持比较好,能跨平台,对系统兼容性也比较好。
CEF控件是开源的,有完善的SDK接口。但是,很多程序员像我一样开始没有使用CEF的经验,想找到一些入门资料或例子来引导开发。关于例子,我当时参考了这篇文章:https://blog.csdn.net/mushao999/article/details/37606189,首先非常感谢这个大神的贡献,给出一个调用CEF控件的MFC程序例子。但是,它的例子针对的CEF版本太老了,我下载了新的CEF SDK,把例子里的Include和Lib目录用新SDK的替换掉,编译出来有很多错误,分析后发现是因为:有些接口已经不能用了,或者参数变了。于是,我又在网上搜索相关的例子,发现简单易懂的并且能用的例子比较少,后来我就从SDK入手,看官方SDK里面的几个例子。然后,参考它的例子修改原来的代码,最后终于把程序改好了。
下面说一下使用CEF控件几个重要的步骤:
第一,初始化CEF浏览器,启动后台相关服务进程。由于CEF新版本采用了多进程架构,一个Tab页面是一个进程,所以为了提高加载网页速度,有些进程是需要预先启动的。下面是初始化环境的代码:
// Enable High-DPI support on Windows 7 or newer.
CefEnableHighDPISupport();
CefMainArgs main_args(m_hInstance);
int exit_code = CefExecuteProcess(main_args, NULL, NULL);
if (exit_code >= 0)
{
return FALSE;
}
CefSettings cSettings;
//CefSettingsTraits::init(&cSettings);
cSettings.multi_threaded_message_loop = true;
cSettings.no_sandbox = true;
#if 1
CefRefPtrspApp;
CefInitialize(main_args, cSettings, spApp, NULL);
#else
// SimpleApp implements application-level callbacks for the browser process.
// It will create the first browser instance in OnContextInitialized() after
// CEF has initialized.
CefRefPtr app(new SimpleApp);
CefInitialize(main_args, cSettings, app.get(), NULL);
#endif
注意:CefSettings的 multi_threaded_message_loop 变量必须设置为true(当你开发的工程是基于Win32窗口),只有控制台程序才可以设置为false。
第二,定义一个全局变量g_BrowsersHandler:
CefRefPtr
该指针变量指向的SimpleHandler对象是用来管理打开的所有网页的,里面有个链表记录每个打开网页的实例指针。SimpleHandler类的类声明如下:
class SimpleHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler {
public:
explicit SimpleHandler(bool use_views);
~SimpleHandler();
// Provide access to the single global instance of this object.
static SimpleHandler* GetInstance();
// CefClient methods:
virtual CefRefPtr GetDisplayHandler() OVERRIDE {
return this;
}
virtual CefRefPtr GetLifeSpanHandler() OVERRIDE {
return this;
}
virtual CefRefPtr GetLoadHandler() OVERRIDE { return this; }
// CefDisplayHandler methods:
virtual void OnTitleChange(CefRefPtr browser,
const CefString& title) OVERRIDE;
// CefLifeSpanHandler methods:
virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE;
virtual bool DoClose(CefRefPtr browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE;
// CefLoadHandler methods:
virtual void OnLoadError(CefRefPtr browser,
CefRefPtr frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) OVERRIDE;
// Request that all existing browser windows close.
void CloseAllBrowsers(bool force_close);
bool IsClosing() const { return is_closing_; }
CefRefPtr GetBrowser(HWND hParentWnd); //根据窗口句柄获取对应的浏览器对象
private:
// Platform-specific implementation.
void PlatformTitleChange(CefRefPtr browser,
const CefString& title);
// True if the application is using the Views framework.
const bool use_views_;
// List of existing browser windows. Only accessed on the CEF UI thread.
typedef std::list> BrowserList;
BrowserList browser_list_;
bool is_closing_;
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleHandler);
};
第三,在你的MFC对话框类的初始化函数里创建一个Browser Host(相当于打开一个页面),代码如下:
BOOL CMFCBrowserDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
CefWindowInfo info;
RECT rect;
GetClientRect(&rect);
RECT rectnew=rect;
rectnew.top=rect.top+60;
rectnew.bottom=rect.bottom;
rectnew.left=rect.left;
rectnew.right=rect.right;
info.SetAsChild(GetSafeHwnd(),rectnew);
CefBrowserSettings browserSettings;
CefBrowserHost::CreateBrowser(info, g_BrowsersHandler.get(),
"http://www.baidu.com",browserSettings, NULL);
//注意: 上面的CreateBrowser函数是异步调用的,调用完成后,事实上CefBrowser可能还没有创建,创建是在SimpleHandler::OnAfterCreated回调事件完成
CefRefPtrbrowser = g_BrowsersHandler->GetBrowser(m_hWnd);
if (browser == NULL) //这里暂时返回空指针是正常的,CefBrowser对象过一会才能获取(是不是有点不人性化?)
{
OutputDebugString(_T("Warning: CefBrowser object is not created \n"));
}
// ...
// TODO: 在此添加额外的初始化代码
return TRUE;
}
上面调用的CefBrowserHost::CreateBrowser函数的第3个参数为打开的网页URL字符串,可以传一个空值,表示打开一个空的页面(即暂时不定向到任何网站),有时这样做是有意义的,因为我们可能是先创建对话框(已经创建了Browser实例),URL后面才赋值,比如用户在某个时候在对话框的编辑框里输入一个URL按回车才打开指定网页内容。实际上,CEF 插件也支持在一个Browser视图里重定向到不同URL。
第四,重定向URL。OnBnLoadURL是用户点击加载按钮或按回车时触发的事件处理函数。
void CMFCBrowserDlg::OnBnLoadURL()
{
CString str;
GetDlgItem(IDC_EDITURL)->GetWindowText(str);
if(str=="")
{
::MessageBox(NULL,_T("请输入网址"),_T("错误信息"),MB_OK);
return;
}
const CefString url(str);
g_BrowsersHandler->GetBrowser(m_hWnd)->GetMainFrame()->LoadURL(url);
}
第五,在对话框的OnSize处理函数里加上控件随窗口大小改变的代码:
void CMFCBrowserDlg::OnSize(UINT nType, int cx,int cy)
{
CDialogEx::OnSize(nType,cx,cy);
//TODO: 在此处添加消息处理程序代码
if(g_BrowsersHandler.get())
{
CefRefPtrbrowser = g_BrowsersHandler->GetBrowser(m_hWnd);
if(browser)
{
CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();
::MoveWindow(hwnd,0,60,cx,cy-60, true);
}
}
}
程序写得比较简单,可能还不是很完善,但是作为一个有简单功能的浏览器例子已经足够了。
这个MFC程序的下载地址:https://download.csdn.net/download/toshiba689/10905635