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後,解壓。解壓後的圖檔如下
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcVzaU9kdGdUY6ZFWiZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zNzkTOzQTMwITOwcDM0EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
用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