Qter 发表于 2021-8-12 17:28:53

Windows上使用CEF嵌入基于chrome内核浏览器小例

https://blog.csdn.net/mfcing/article/details/43953433

浏览器代码已开源:欢迎收藏 https://github.com/JelinYao/MyChrome

CEF出来很久了,使用的也很广泛的,QQ里面很多地方都是嵌入的CEF浏览器(个人资料、微博、查找……),网上的资料也挺多的,大家可以搜搜看。

首先是下载CEF代码编译,通过里面的那两个例子你也可以依葫芦画瓢的。官方下载地址:http://cefbuilds.com/

这里推荐一个很详细的解说:http://www.cnblogs.com/think/archive/2011/10/06/CEF-Introduce.html

重载CEF的各种“消息”处理类,当你需要自己处理或者自定义这些消息时,才需要重载它们,重载后一定要重写相应的虚函数返回this指针。估计内部是有这些个回调类对象指针,不返回this的话默认就是NULL了,就会执行内部默认的那套机制。



#pragma once
#include "include/cef_client.h"
#include <list>
#include <string>
using std::wstring;

class CCefHandler :
        public CefClient,
    public CefDisplayHandler,
    public CefLifeSpanHandler,
        public CefLoadHandler,
        public CefRequestHandler,
        public CefContextMenuHandler,
        public CefDownloadHandler
{
public:
        CCefHandler(const wstring& strUrl=L"");
        virtual ~CCefHandler();
        //自定义方法
        CefRefPtr<CefBrowser> GetBrowser() { return m_pBrowser; }
        CefRefPtr<CefFrame>        GetMainFram() { return m_pBrowser.get()?m_pBrowser->GetMainFrame():NULL; }
        HWND        GetBrowserHostWnd() { return m_pBrowser.get()?m_pBrowser->GetHost()->GetWindowHandle():NULL; }
        void        SetHomePage(const wstring& strUrl) { m_strHomePage=strUrl; }
        const wstring& GetHomePage()const { return m_strHomePage; }
        wstring GetLoadingUrl();
        void        Navigate(const wstring& strUrl);
        void        CreateBrowser(HWND hParentWnd, const RECT& rect);
        bool        IsClosing() const { return m_bIsClose; }

//凡是继承了的处理功能都在这里返回this指针
        virtual CefRefPtr<CefDisplayHandler>                GetDisplayHandler()                { return this; }
        virtual CefRefPtr<CefLifeSpanHandler>                GetLifeSpanHandler()        { return this; }
        virtual CefRefPtr<CefLoadHandler>                        GetLoadHandler()                { return this; }
        virtual CefRefPtr<CefContextMenuHandler>        GetContextMenuHandler()        { return this; }
        virtual CefRefPtr<CefDownloadHandler>                GetDownloadHandler()        { return this; }
// CefDisplayHandler methods:
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title);
virtual void OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url);
virtual bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString& text);
virtual void OnStatusMessage(CefRefPtr<CefBrowser> browser, const CefString& value);

// CefLifeSpanHandler methods:
virtual bool OnBeforePopup(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
          const CefString& target_url, const CefString& target_frame_name, const CefPopupFeatures& popupFeatures, \
          CefWindowInfo& windowInfo, CefRefPtr<CefClient>& client, CefBrowserSettings& settings, \
          bool* no_javascript_access);
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser);
virtual bool DoClose(CefRefPtr<CefBrowser> browser);
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser);

// CefLoadHandler methods:
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame);
virtual void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode);
virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl);

// Request that all existing browser windows close.
void CloseAllBrowsers(bool force_close);
//
virtual bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
          CefRefPtr<CefRequest> request, bool is_redirect)
{
                  //return true;
                  return false;
}

virtual bool OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
          CefRefPtr<CefFrame> frame,
          CefRefPtr<CefRequest> request) {
                  return false;
}

//菜单处理
virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
          CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model);

virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
          CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags);

//下载处理
virtual void OnBeforeDownload( CefRefPtr<CefBrowser> browser, CefRefPtr<CefDownloadItem> download_item, \
          const CefString& suggested_name, CefRefPtr<CefBeforeDownloadCallback> callback);

virtual void OnDownloadUpdated( CefRefPtr<CefBrowser> browser, CefRefPtr<CefDownloadItem> download_item, \
          CefRefPtr<CefDownloadItemCallback> callback);
private:
        typedef std::list<CefRefPtr<CefBrowser> > BrowserList;
        BrowserList browser_list_;
        CefRefPtr<CefBrowser>        m_pBrowser;
        bool        m_bIsClose;
        wstring        m_strHomePage;
        static int        m_nBrowserCount;
        // Include the default reference counting implementation.
        IMPLEMENT_REFCOUNTING(CCefHandler);
        IMPLEMENT_LOCKING(CCefHandler);
};

对应的实现代码:





#include "stdafx.h"
#include "CefHandler.h"
#include <sstream>
#include "util.h"
#include "../include/cef_app.h"
#include "../include/cef_runnable.h"


int                CCefHandler::m_nBrowserCount = 0;
CCefHandler::CCefHandler( const wstring& strUrl/*=L""*/ )
: m_bIsClose(false)
, m_strHomePage(strUrl)
{

}


CCefHandler::~CCefHandler()
{

}



void CCefHandler::CloseAllBrowsers(bool force_close)
{
        if (!CefCurrentlyOn(TID_UI))
        {
                // Execute on the UI thread.
                CefPostTask(TID_UI, NewCefRunnableMethod(this, &CCefHandler::CloseAllBrowsers, force_close));
                return;
        }

if (browser_list_.empty())
    return;

BrowserList::const_iterator it = browser_list_.begin();
for (; it != browser_list_.end(); ++it)
    (*it)->GetHost()->CloseBrowser(force_close);
}

wstring CCefHandler::GetLoadingUrl()
{
        CefRefPtr<CefFrame> pMainFram=GetMainFram();
        return pMainFram.get()?pMainFram->GetURL():L"";
}

void CCefHandler::Navigate( const wstring& strUrl )
{
        CefRefPtr<CefFrame> pMainFram=GetMainFram();
        if ( pMainFram.get() )
                pMainFram->LoadURL(strUrl.c_str());
}

void CCefHandler::CreateBrowser( HWND hParentWnd, const RECT& rect )
{
        CefWindowInfo info;
        CefBrowserSettings settings;
        static wchar_t* pCharset = L"GB2312";
        settings.default_encoding.str = pCharset;
        settings.default_encoding.length = wcslen(pCharset);
        info.SetAsChild(hParentWnd, rect);
        CefBrowserHost::CreateBrowser(info, this, m_strHomePage.c_str(), settings, NULL);
}

//****************************************************
//菜单加载接口
void CCefHandler::OnBeforeContextMenu( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
                                                                          CefRefPtr<CefContextMenuParams> params, CefRefPtr<CefMenuModel> model )
{
        //在这里,我添加了自己想要的菜单
        cef_context_menu_type_flags_t flag =   params->GetTypeFlags();
        if ( flag & CM_TYPEFLAG_PAGE )
        {//普通页面的右键消息
                model->SetLabel(MENU_ID_BACK, L"后退");
                model->SetLabel(MENU_ID_FORWARD, L"前进");
                model->SetLabel(MENU_ID_VIEW_SOURCE, L"查看源代码");
                model->SetLabel(MENU_ID_PRINT, L"打印");
                model->SetLabel(MENU_ID_RELOAD, L"刷新");
                model->SetLabel(MENU_ID_RELOAD_NOCACHE, L"强制刷新");
                model->SetLabel(MENU_ID_STOPLOAD, L"停止加载");
                model->SetLabel(MENU_ID_REDO, L"重复");
        }
        if ( flag & CM_TYPEFLAG_EDITABLE)
        {//编辑框的右键消息
                model->SetLabel(MENU_ID_UNDO, L"撤销");
                model->SetLabel(MENU_ID_REDO, L"重做");
                model->SetLabel(MENU_ID_CUT, L"剪切");
                model->SetLabel(MENU_ID_COPY, L"复制");
                model->SetLabel(MENU_ID_PASTE, L"粘贴");
                model->SetLabel(MENU_ID_DELETE, L"删除");
                model->SetLabel(MENU_ID_SELECT_ALL, L"全选");
        }
}

bool CCefHandler::OnContextMenuCommand( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
                                                                           CefRefPtr<CefContextMenuParams> params, int command_id, EventFlags event_flags )
{
        return false;
}


//****************************************************
//网页加载状态回调接口
void CCefHandler::OnLoadStart( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame )
{
       
}

void CCefHandler::OnLoadEnd( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode )
{
        //CefRefPtr<CefV8Context> v8 = frame->GetV8Context();
}

void CCefHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
                                                          ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl)
{
        REQUIRE_UI_THREAD();
        // Don't display an error for downloaded files.
        if (errorCode == ERR_ABORTED)
                return;
        // Display a load error message.
        std::wstringstream ss;
        //std::wstring
        ss << L"<html><body bgcolor=\"white\">"
                L"<h2>Failed to load URL " << std::wstring(failedUrl) <<
                L" with error " << std::wstring(errorText) << L" (" << errorCode <<
                L").</h2></body></html>"<<'\0';
        frame->LoadString(ss.str(), failedUrl);
}

//****************************************************
//状态改变回调接口
void CCefHandler::OnTitleChange( CefRefPtr<CefBrowser> browser, const CefString& title )
{

}

void CCefHandler::OnAddressChange( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url )
{

}

bool CCefHandler::OnTooltip( CefRefPtr<CefBrowser> browser, CefString& text )
{
        return false;
}

void CCefHandler::OnStatusMessage( CefRefPtr<CefBrowser> browser, const CefString& value )
{
       
}

//****************************************************
//网页生命周期回调接口
bool CCefHandler::OnBeforePopup( CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, \
                const CefString& target_url, const CefString& target_frame_name, const CefPopupFeatures& popupFeatures, \
                CefWindowInfo& windowInfo, CefRefPtr<CefClient>& client, CefBrowserSettings& settings, \
                bool* no_javascript_access )
{
        //这里使用默认浏览器打开网页,避免CEF重新创建窗口
        ShellExecute(NULL, L"open", target_url.c_str(), NULL, NULL, SW_SHOW);
        //return false;//创建新窗口
        return true; //禁止创建新的窗口
}

void CCefHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
        REQUIRE_UI_THREAD();
        // Add to the list of existing browsers.
        browser_list_.push_back(browser);
        AutoLock scopLock(this);
        if ( ! m_pBrowser.get() )
                m_pBrowser=browser;
        m_nBrowserCount++;
}

bool CCefHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
        REQUIRE_UI_THREAD();
        // Closing the main window requires special handling. See the DoClose()
        // documentation in the CEF header for a detailed destription of this
        // process.
        if (browser_list_.size() == 1) {
                // Set a flag to indicate that the window close should be allowed.
                m_bIsClose = true;
        }

        // Allow the close. For windowed browsers this will result in the OS close
        // event being sent.
        return false;
}

void CCefHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
        REQUIRE_UI_THREAD();
        // Remove from the list of existing browsers.
        BrowserList::iterator bit = browser_list_.begin();
        for (; bit != browser_list_.end(); ++bit) {
                if ((*bit)->IsSame(browser)) {
                        browser_list_.erase(bit);
                        break;
                }
        }
        if (browser_list_.empty()) {
                // All browser windows have closed. Quit the application message loop.
                CefQuitMessageLoop();
        }
}

void CCefHandler::OnBeforeDownload( CefRefPtr<CefBrowser> browser, CefRefPtr<CefDownloadItem> download_item, \
                const CefString& suggested_name, CefRefPtr<CefBeforeDownloadCallback> callback )
{
        int i=0;
}

void CCefHandler::OnDownloadUpdated( CefRefPtr<CefBrowser> browser, CefRefPtr<CefDownloadItem> download_item, \
                CefRefPtr<CefDownloadItemCallback> callback )
{
        //取消CEF内部下载文件,使用默认浏览器打开链接去下载,下载过程就不需要我们关心了,毕竟不是做浏览器
        callback->Cancel();
        CefString strUrl = download_item->GetURL();
        ShellExecute(NULL, L"open", strUrl.c_str(), NULL, NULL, SW_SHOW);
        int i = 0;
}


然后是实现CefApp类的重载,没有实际的处理功能,在此忽略。



使用时,创建一个win32项目,然后修改WinMain函数:

CefRefPtr<CCefHandler> g_handler;
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
        // TODO: 在此放置代码。
        CefMainArgs main_args(hInstance);
        CefRefPtr<CCefAppEx> app(new CCefAppEx);
        int exit_code = CefExecuteProcess(main_args, app.get());
        if (exit_code >= 0) {
                return exit_code;
        }
        CefSettings settings;
        settings.single_process = true;
        //settings.multi_threaded_message_loop = true;
        CefInitialize(main_args, settings, app.get());
        //if ( strCmd.size() == 0 )
        HACCEL hAccelTable;
        // 初始化全局字符串
        LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadString(hInstance, IDC_CEFDEMO, szWindowClass, MAX_LOADSTRING);
        MyRegisterClass(hInstance);
        // 执行应用程序初始化:
        if (!InitInstance (hInstance, nCmdShow))
        {
                return FALSE;
        }
        hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFDEMO));
        CefRunMessageLoop();
        CefShutdown();
        return 0;
}
消息处理主要是创建、销毁CEF对象,窗口大小改变时通知CEF窗口等:





LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        int wmId, wmEvent;
        PAINTSTRUCT ps;
        HDC hdc;

        switch (message)
        {
        case WM_CREATE:
                {//http://blog.csdn.net/wongson/article/details/6210854
                        g_handler=new CCefHandler(L"www.qq.com");
                        RECT rect;
                        ::GetClientRect(hWnd, &rect);
                        g_handler->CreateBrowser(hWnd, rect);
                        break;
                }
        case WM_COMMAND:
                wmId    = LOWORD(wParam);
                wmEvent = HIWORD(wParam);
                // 分析菜单选择:
                switch (wmId)
                {
                case IDM_EXIT:
                        DestroyWindow(hWnd);
                        break;
                default: break;
                }
                break;
        case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                // TODO: 在此添加任意绘图代码...
                EndPaint(hWnd, &ps);
                break;
        case WM_SIZE:
                {
                        if ( wParam == SIZE_MINIMIZED
                                || NULL == g_handler
                                || NULL == g_handler->GetBrowserHostWnd() )
                                break;
                        HWND hBrowserWnd=g_handler->GetBrowserHostWnd();
                        RECT rect;
                        ::GetClientRect(hWnd, &rect);
                        ::MoveWindow(hBrowserWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
                        break;
                }
        case WM_CLOSE:
                {
                        if ( g_handler.get() && !g_handler->IsClosing() )
                        {
                                wstring strUrl=g_handler->GetLoadingUrl();
                                CefRefPtr<CefBrowser> pBrowser=g_handler->GetBrowser();
                                if ( pBrowser.get() )
                                        pBrowser->GetHost()->CloseBrowser(true);
                        }
                        break;
                }
        default: break;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}
编译,运行程序,一个简单的网页就出来了,加载速度比IE那垃圾快多了,关键是不用理会兼容性问题了。






最后记得带上CEF的一大堆DLL,这个是必须的,如果网页需要播放视频,需要新建一个plugins文件夹,放入视频播放插件NPSWF32.dll。

需要链接的CEF动态链接库:



#ifdef _DEBUG
        #pragma comment(lib, "..\\LibCef\\Debug\\libcef")
#pragma comment(lib, "..\\LibCef\\Debug\\libcef_dll_wrapper")
#else
        #pragma comment(lib, "..\\LibCef\\Release\\libcef")
#pragma comment(lib, "..\\LibCef\\Release\\libcef_dll_wrapper")
#endif
libcef使用范例之4399网页小游戏,git源码:https://github.com/JelinYao/4399WebGames

使用libCef+Duilib开发属于自己的浏览器已开源:https://github.com/JelinYao/MyChrome




————————————————
版权声明:本文为CSDN博主「疯狂-的-蜗牛」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mfcing/article/details/43953433

页: [1]
查看完整版本: Windows上使用CEF嵌入基于chrome内核浏览器小例