声明:文章来自http://www.codeproject.com/Articles/1105945/Embedding-a-Chromium-browser-in-an-MFC-application
先通过机翻然后人工大体修改,有不通的地方,但不影响理解,程序员看懂代码就好,英语慢慢来
在MFC应用程序中嵌入一个谷歌嵌入式框架(CEF)V3的浏览器。
CEF是一个开源项目,允许开发人员包括第三方应用的浏览器。基于CEF提供的API,用于C和C++应用程序。但外部项目(由CEF不执行)支持其他语言如C或Python #,java,delphi。本文介绍了如何将浏览器嵌入在MFC单文档界面的应用。
先做准备:
CEF是既可以作为一个二进制分布和源代码。从来源的建设可以在本地或与一个自动化的建立系统,但也需要建设铬,这是一个有点复杂。
CEF 3二进制分布在https://cefbuilds.com/可包括以下内容:
关于如何建立CEF 64位看了我的文章在Windows建立铬嵌入式框架的64位版本。
简单的MFC应用程序,我们将在本文中创建应位于同一输出位置为CEF的示例应用程序的主要CEF文件夹。
应用解剖
一个MFC SDI应用程序有几个组成部分:
#include "include/base/cef_lock.h"
#include "include/cef_client.h"
class ClientHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler
{
public:
// Implement this interface to receive notification of ClientHandler
// events. The methods of this class will be called on the main thread.
class Delegate
{
public:
// Called when the browser is created.
virtual void OnBrowserCreated(CefRefPtr browser) = 0;
// Called when the browser is closing.
virtual void OnBrowserClosing(CefRefPtr browser) = 0;
// Called when the browser has been closed.
virtual void OnBrowserClosed(CefRefPtr browser) = 0;
// Set the window URL address.
virtual void OnSetAddress(std::string const & url) = 0;
// Set the window title.
virtual void OnSetTitle(std::string const & title) = 0;
// Set fullscreen mode.
virtual void OnSetFullscreen(bool const fullscreen) = 0;
// Set the loading state.
virtual void OnSetLoadingState(bool const isLoading,
bool const canGoBack,
bool const canGoForward) = 0;
protected:
virtual ~Delegate() {}
};
public:
ClientHandler(Delegate* delegate);
~ClientHandler();
void CreateBrowser(CefWindowInfo const & info, CefBrowserSettings const & settings, CefString const & url);
// CefClient methods:
virtual CefRefPtr GetDisplayHandler() override { return this; }
virtual CefRefPtr GetLifeSpanHandler() override { return this; }
virtual CefRefPtr GetLoadHandler() override { return this; }
// CefDisplayHandler methods:
virtual void OnAddressChange(CefRefPtr browser, CefRefPtr frame, const CefString& url) override;
virtual void OnTitleChange(CefRefPtr browser, const CefString& title) override;
virtual void OnFullscreenModeChange(CefRefPtr browser, bool fullscreen) 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 OnLoadingStateChange(CefRefPtr browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override;
virtual void OnLoadError(CefRefPtr browser,
CefRefPtr frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) override;
// This object may outlive the Delegate object so it's necessary for the
// Delegate to detach itself before destruction.
void DetachDelegate();
private:
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(ClientHandler);
// Include the default locking implementation.
IMPLEMENT_LOCKING(ClientHandler);
private:
Delegate* m_delegate;
};
virtual CefRefPtr GetDisplayHandler() override { return this; }
virtual CefRefPtr GetLifeSpanHandler() override { return this; }
virtual CefRefPtr GetLoadHandler() override { return this; }
void ClientHandler::CreateBrowser(CefWindowInfo const & info, CefBrowserSettings const & settings, CefString const & url)
{
CefBrowserHost::CreateBrowser(info, this, url, settings, nullptr);
}
void ClientHandler::OnAddressChange(CefRefPtr browser, CefRefPtr frame, const CefString& url)
{
CEF_REQUIRE_UI_THREAD();
// Only update the address for the main (top-level) frame.
if(frame->IsMain())
{
if(m_delegate != nullptr)
m_delegate->OnSetAddress(url);
}
}
void ClientHandler::OnTitleChange(CefRefPtr browser, const CefString& title)
{
CEF_REQUIRE_UI_THREAD();
if(m_delegate != nullptr)
m_delegate->OnSetTitle(title);
}
void ClientHandler::OnFullscreenModeChange(CefRefPtr browser, bool fullscreen)
{
CEF_REQUIRE_UI_THREAD();
if(m_delegate != nullptr)
m_delegate->OnSetFullscreen(fullscreen);
}
void ClientHandler::OnAfterCreated(CefRefPtr browser)
{
CEF_REQUIRE_UI_THREAD();
if(m_delegate != nullptr)
m_delegate->OnBrowserCreated(browser);
}
bool ClientHandler::DoClose(CefRefPtr browser)
{
CEF_REQUIRE_UI_THREAD();
if(m_delegate != nullptr)
m_delegate->OnBrowserClosing(browser);
return false;
}
void ClientHandler::OnBeforeClose(CefRefPtr browser)
{
CEF_REQUIRE_UI_THREAD();
if(m_delegate != nullptr)
m_delegate->OnBrowserClosed(browser);
}
void ClientHandler::OnLoadError(CefRefPtr browser,
CefRefPtr frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl)
{
CEF_REQUIRE_UI_THREAD();
// Don't display an error for downloaded files.
if(errorCode == ERR_ABORTED)
return;
// Display a load error message.
std::stringstream ss;
ss << ""
"Failed to load URL " << std::string(failedUrl) <<
" with error " << std::string(errorText) << " (" << errorCode <<
").
";
frame->LoadString(ss.str(), failedUrl);
}
void ClientHandler::OnLoadingStateChange(CefRefPtr browser, bool isLoading, bool canGoBack, bool canGoForward)
{
CEF_REQUIRE_UI_THREAD();
if(m_delegate != nullptr)
m_delegate->OnSetLoadingState(isLoading, canGoBack, canGoForward);
}
委托的实例是在detachdelegate()构造函数初始化。
ClientHandler::ClientHandler(Delegate* delegate)
: m_delegate(delegate)
{
}
void ClientHandler::DetachDelegate()
{
m_delegate = nullptr;
}
void CefView::OnInitialUpdate()
{
CView::OnInitialUpdate();
InitStartUrl();
auto rect = RECT{0};
GetClientRect(&rect);
CefWindowInfo info;
info.SetAsChild(GetSafeHwnd(), rect);
CefBrowserSettings browserSettings;
browserSettings.web_security = STATE_DISABLED;
m_clientHandler = new ClientHandler(this);
m_clientHandler->CreateBrowser(info, browserSettings, CefString(m_startUrl));
}
void CefView::InitStartUrl()
{
TCHAR path_buffer[_MAX_PATH] = {0};
TCHAR drive[_MAX_DRIVE] = {0};
TCHAR dir[_MAX_DIR] = {0};
TCHAR fname[_MAX_FNAME] = {0};
TCHAR ext[_MAX_EXT] = {0};
::GetModuleFileName(NULL, path_buffer, sizeof(path_buffer));
auto err = _tsplitpath_s(path_buffer, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
if(err != 0) {}
auto s = CString{dir};
s += _T("html");
err = _tmakepath_s(path_buffer, _MAX_PATH, drive, (LPCTSTR)s, _T("index"), _T("html"));
if(err != 0) {}
m_startUrl = CString {path_buffer};
m_startUrl.Replace(_T('\\'),_T('/'));
m_startUrl = CString {_T("file:///")} + m_startUrl;
}
void CefView::OnBrowserCreated(CefRefPtr browser)
{
m_browser = browser;
}
void CefView::OnBrowserClosing(CefRefPtr browser)
{
}
void CefView::OnBrowserClosed(CefRefPtr browser)
{
if(m_browser != nullptr &&
m_browser->GetIdentifier() == browser->GetIdentifier())
{
m_browser = nullptr;
m_clientHandler->DetachDelegate();
}
}
void CefView::OnSetAddress(std::string const & url)
{
auto main = static_cast(m_wndMain);
if(main != nullptr)
{
auto newurl = CString {url.c_str()};
if(newurl.Find(m_startUrl) >= 0)
newurl = "";
main->SetUrl(newurl);
}
}
void CefView::OnSetTitle(std::string const & title)
{
::SetWindowText(m_hWnd, CefString(title).ToWString().c_str());
}
void CefView::OnSetFullscreen(bool const fullscreen)
{
if(m_browser != nullptr)
{
if(fullscreen)
{
CefWindowsHelpers::Maximize(m_browser);
}
else
{
CefWindowsHelpers::Restore(m_browser);
}
}
}
void CefView::OnSetLoadingState(bool const isLoading,
bool const canGoBack,
bool const canGoForward)
{
}
我们要做的一件事是在查看浏览器窗口大小每次查看更改大小。由于浏览器窗口是完全重叠的视图的客户区,它们的大小必须是相同的所有时间。所以在认为我们必须处理的wm_size消息和调整浏览器。
void CefView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if(m_clientHandler != nullptr)
{
if(m_browser != nullptr)
{
auto hwnd = m_browser->GetHost()->GetWindowHandle();
auto rect = RECT {0};
GetClientRect(&rect);
::SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
}
}
BOOL CefView::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN)
{
if(pMsg->wParam == VK_F5)
{
m_browser->Reload();
}
}
return CView::PreTranslateMessage(pMsg);
}
void CefMfcdDemoApp::InitializeCef()
{
CefMainArgs mainargs(m_hInstance);
CefSettings settings;
settings.multi_threaded_message_loop = false;
CefInitialize(mainargs, settings, m_app, nullptr);
}
void CefMfcdDemoApp::UninitializeCef()
{
CefShutdown();
}
BOOL CefMfcdDemoApp::InitInstance()
{
// various initialization
InitializeCef();
CWinApp::InitInstance();
// more initialization
return TRUE;
}
int CefMfcdDemoApp::ExitInstance()
{
AfxOleTerm(FALSE);
UninitializeCef();
return CWinApp::ExitInstance();
}
因为我们指定的浏览器进程不应该运行在一个单独的线程的消息循环(通过设置multi_threaded_message_loop 为假)我们需要从主线程的消息循环调用cefdomessageloopwork()。我们通过重写CWinApp:::pumpmessage()如下。
BOOL CefMfcdDemoApp::PumpMessage()
{
auto result = CWinApp::PumpMessage();
CefDoMessageLoopWork();
return result;
}