MFC对话框中调用CEF浏览器控件(使用2018年新版本的CEF SDK)

如果你是一个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  g_BrowsersHandler = new SimpleHandler(false);

该指针变量指向的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

你可能感兴趣的:(windows开发,CEF浏览器,网页插件)