MFC对话框应用程序中谷歌CEF浏览器内核的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mushao999/article/details/37606189

MFC对话框应用程序中谷歌CEF浏览器内核的使用

       最近在做一个与浏览器相关的MFC项目,用户要求使用IE和谷歌双内核。对于IE内核可以直接使用MFC中的ACTIVEX控件,但是对于谷歌浏览器内核却并没有这么现成的控件可以使用。原来是想要自己编译WebKit做相关dll的,但是查阅相关资料后发现编译WebKit不是一个短期内能够完成的任务。后来无意间在网上找到了CEF。它是对WebKit的一个封装。想要了解详细信息可以直接在百度里面搜索相关资料。

       网上关于CEF在MFC中的使用看起来很多,其实它们几乎都是一个版本。而且如果直接按照他们的做法去做会出现这样或那样的错误。在这众多版本的参考资料中:http://blog.csdn.net/yhangleo/article/details/8482603是最大众化的一个,几乎一半以上的版本都和这个一样,也不知道谁引用谁的,不过拜托在发表之前先自己测试通过。http://blog.csdn.net/xuezhe521/article/details/9067035有点想法,但是只是从表面上解决了一些问题,按照他的做法做会为后面的使用埋下隐患。现在隆重推荐最好的一篇http://it.nittis.ru/mfc-cef.html虽然上面全是外文,只有代码可以看懂,但是却是这几篇中最好的。

       言归正传,开始编写。

1、下载CEF源代码。这里是第一个注意点。CEF分为cef1、cef2和cef3三个版本,其中cef1为单线程版本,cef2已经放弃开发,cef3为多线程版本。本教程适用于cef1,至于cef3没有测试过,不知道能不能使。

cef官方网站  cef下载地址(记得下载的cef1不是3!!!)

2、CEF编译。下载好CEF后,解压。解压后的图片如下


用VS打开.sln文件。界面如下


设置libcef_dll_wrapper为启动项分别生成debug和release两个版本的libcef_dll_wrapper.lib。在生成后VS会提示无法运行libcef_dll_wrapper.lib,不用理它,因为本来生成的就是lib文件怎么可能运行。运行生成后在生成目录下找到生成的debug和release版本的libcef_dll_wrapper.lib.注意生成路径为目录bulid,截图如下:(Debug版本同理)


此时,cef相关文件的生成工作已经完成。需要从程序中找出来以备后用的文件如下:

…\cef_binary_1.1364.1123_windows\include文件夹

…\cef_binary_1.1364.1123_windows\lib\Debug\ libcef_dll-wrapper.lib

…\cef_binary_1.1364.1123_windows\lib\Release\ libcef_dll-wrapper.lib(与上面同名用不同的文件夹分开放)

…\cef_binary_1.1364.1123_windows\Debug文件夹(要用的是除cefclient以外的所有文件)

  ...\cef_binary_1.1364.1123_windows\cef_binary_1.1364.1123_windows\lib\Release\libcef.lib

       最终准备文件截图如下:


      

至此准备工作完成。

3、在MFC对话框项目中使用CEF

(1)、建立一个MFC对话框项目

(2)、环境配置

将准备的libcef.lib、libcef_dll_wrapper.lib(在debug时工程目录下放的是debug版本的,当release时替换为release的)、include文件夹一同复制到工程目录下。

①          接下来设置工程属性。这里需要强调一下,工程属性的设置必须在“所有配置”模式下进行,以保证debug和release的情况完全一致。这一点必须注意,其截图如下:

 

②    在项目上vc++目录->包含目录->编辑双击后输入:$(ProjectDir)\include 

                                                                                               

③同样项目右键->属性->配置属性->链接器->输入->附加依赖项->编辑

输入:libcef.lib换行再输入libcef_dll_wrapper.lib


(细心的朋友会发现,如果在这里分开设置release和debug的包含lib就不用来回替换libcef_dll_wrapper.lib了。这个想法是正确的,我只是为了描述方便,实际采用分开设置对调试来说更方便)

④      配置属性->预编译头->预编译头->不适用预编译头。截图如下

 

⑤  配置属性->c/c++->代码生成->运行库->多线程调试(/MTd)(realse下选择多线程MT)。截图如下:


⑥  配置属性->常规->MFC的使用->在静态库中使用MFC.截图如下:


       (3)、添加cwebclient类

在菜单栏选择:项目->类向导->添加类->输入类名->完成。截图如下:



此处注意系统会自动把.h和cpp文件名的c去掉,建议加上防止混乱。

把CWebClient.h的内容改为如下(直接覆盖原来内容):注意此处与大多数的教程不同,大多数教程往往让把下列代码加入cpp中,这种做法是错误的。

#pragma once
#include <cef_client.h>


class CWebClient 
<span style="white-space:pre">	</span>: public CefClient
<span style="white-space:pre">	</span>, public CefLifeSpanHandler
{
protected:
<span style="white-space:pre">	</span>CefRefPtr<CefBrowser> m_Browser;


public:
<span style="white-space:pre">	</span>CWebClient(void){};
<span style="white-space:pre">	</span>virtual ~CWebClient(void){};


<span style="white-space:pre">	</span>CefRefPtr<CefBrowser> GetBrowser() { return m_Browser; }


<span style="white-space:pre">	</span>virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE
<span style="white-space:pre">	</span>{ return this; }


<span style="white-space:pre">	</span>virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;


<span style="white-space:pre">	</span>// 添加CEF的SP虚函数
<span style="white-space:pre">	</span>IMPLEMENT_REFCOUNTING(CWebClient);
<span style="white-space:pre">	</span>IMPLEMENT_LOCKING(CWebClient);
};


在cpp中加入如下代码(覆盖),这段代码只有那篇外文的文章中才有,但是这是一个很关键的代码,就是因为没有这句代码所以我见到的所有中文文章都是不完全正确的。

#include "stdafx.h"
#include "CWebClient.h"
void CWebClient::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
  m_Browser = browser;
}


有一篇文章通过将.h中的virtualvoid OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;改为irtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) {};通过这种方法来消除这句代码的错误,这种做法其实是错误的,它只是将本来要重写的虚函数仍保持为虚函数,这样在后面调用这个函数的时候就出错了。

而这段代码给出的就是这个虚函数的实体,从而从根源上解决了上面这个虚函数重写的问题。

(4)

在主窗体的.h文件中加入

#include <cef_client.h>

#include "CWebClient.h"

#include <cef_app.h>

在类的public成员中添加

CefRefPtr<CWebClient>  m_cWebClient;

(5)通过类向导为主窗体添加onsize()事件,如下图:



在onsize()函数体中写入如下代码

void CMfcBrowserDlg::OnSize(UINT nType, int cx,int cy)
{
         CDialogEx::OnSize(nType,cx,cy);
 
         //TODO: 在此处添加消息处理程序代码
         if(m_cWebClient.get())
         {
                   CefRefPtr<CefBrowser>browser = m_cWebClient->GetBrowser();
                   if(browser)
                   {
                            CefWindowHandle hwnd = browser->GetWindowHandle();
                            ::MoveWindow(hwnd,100,100,800,800, true); 
                   }
         }
}


其中movewindow中参数可自行更改,也可设置为动态更改(可以查阅movewindow相关资料)。

(6)给出创建浏览器的代码

CefRefPtr<CWebClient>client(new CWebClient());
m_cWebClient= client;
CefSettings cSettings;
CefSettingsTraits::init(&cSettings);
cSettings.multi_threaded_message_loop= true;
CefRefPtr<CefApp>spApp;
CefInitialize(cSettings, spApp);   
CefWindowInfo info;
RECT rect;
GetClientRect(&rect);
RECT rectnew=rect;
rectnew.top=rect.top+70;
rectnew.bottom=rect.bottom;
rectnew.left=rect.left;
rectnew.right=rect.right;
info.SetAsChild(GetSafeHwnd(),rectnew);
CefBrowserSettings browserSettings;
CefBrowser::CreateBrowser(info, static_cast<CefRefPtr<CefClient> >(client),
<span style="white-space:pre">		</span>"http://www.baidu.com",browserSettings);
</pre><p></p><p>所有查到的资料中都将浏览器的创建过程写在主窗体的oncreate()中。这样本身并没有什么不妥,可以在窗体被创建的初始就创建出浏览器。但是我想说的是,浏览器的创建可以在任何时候。只要在你需要的时候使用如下代码段即可。</p><p>至于oncreate事件的添加与onsize()相同。</p><p>(7)浏览器被创建出来后,可以使用类成员m_cwebclient实现对浏览器的控制。这里要要说一下。如果你前面说的第一种文章的描述进行操作,你在调试的时候会在virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser)OVERRIDE;报错。如果你按照第二种文章的说法,将virtual voidOnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;中的OVERRIDE改为{},那么好吧问题暂时得到解决,你也可以在窗体中创建出来浏览器控件,一切看似很和谐。但是当你想在软件运行过程中导航网址时,即采用m_cWebClient->GetBrowser()->GetMainFrame()->LoadURL(url);将会报错,报错的原因是virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser){};为虚函数没有函数实体。因此,最正确的方式就是文章3中提到的在cpp中加入该函数的实体。</p><p>下面给出一些浏览器被创建后再程序中可以使用的一些操作:</p><p>导航网址(网址从文本框获得)</p><p></p><pre name="code" class="cpp">CString str;
GetDlgItem(IDC_EDIT1)->GetWindowText(str);
const CefString url(str);
m_cWebClient->GetBrowser()->GetMainFrame()->LoadURL(url);


浏览器隐藏与显示

::ShowWindow(m_cWebClient->GetBrowser()->GetWindowHandle(),SW_HIDE);
::ShowWindow(m_cWebClient->GetBrowser()->GetWindowHandle(),SW_SHOW);

至于刷新、上一步、下一步等操作大同小异就不赘述了。

(8)代码的编写已经完成。如果你已正确地在debug模式下在工程下放入debug版的libcef_dll_wrapper.lib或在release模式下载工程下放入realse版的libcef_dll_wrapper.lib,那么运行时会报错缺少libcef.dll.此时恭喜你,你已经距离成功只差一步之遥了。如果你是在debug模式下,那么将你准备好的debug文件夹下的处理cefclient.exe的其它文件全部拷贝到工程debug文件夹下。如果你在release模式下,同样将debug文件夹下的这些文件拷贝到release文件夹下。此时运行,你会兴奋地发现,运行成功!截图如下:

Release文件夹



本来应该加载谷歌的但是谷歌最近被封了,一致登不上只好加载百度了


如需要交流可以发邮件至[email protected],我会尽快回复。

                                                                                    Mushao

                                                                                    2014-6-27


对应工程网址已上传至csdn:http://download.csdn.net/detail/mushao999/8060429      2014-10-20


猜你喜欢

转载自blog.csdn.net/mushao999/article/details/37606189