原文链接:http://blog.youkuaiyun.com/mfcing/article/details/43973377
概述
wke是基于谷歌chrome浏览器源代码的裁剪版本,大小仅仅只有10M左右,无需依赖其他的扩展库(跟CEF的一大堆大约40M的DLL来比简直爽呆了),就可以在本地使用谷歌内核快速加载网页。网上也有基于wke在Duilib 上扩展的控件代码,其实wke头文件挺清楚的了,接口一目了然,特别是JS与C++交互的函数更是容易看懂,也没什么难的,你也可以做到的。
代码
毕竟是裁剪库,有的功能还是没有接口来处理的(比如网页加载前、页面跳转、菜单消息……),头文件代码:
#ifndef __UIWKEWEBKIT_H_
#define __UIWKEWEBKIT_H_
#pragma once
#include "wke.h"
#include <string>
using std::wstring;
namespace DuiLib
{
///
//网页加载状态改变的回调
class CWkeWebkitLoadCallback
{
public:
virtual void OnLoadFailed()=0;
virtual void OnLoadComplete()=0;
virtual void OnDocumentReady()=0;
};
///
//网页标题、地址改变的回调
class CWkeWebkitClientCallback
{
public:
virtual void OnTitleChange(const CDuiString& strTitle)=0;
virtual void OnUrlChange(const CDuiString& strUrl)=0;
};
class CWkeWebkitUI :
public CControlUI,
public wkeBufHandler
{
public:
CWkeWebkitUI(void);
~CWkeWebkitUI(void);
virtual void onBufUpdated (const HDC hdc,int x, int y, int cx, int cy);
virtual LPCTSTR GetClass()const;
virtual LPVOID GetInterface(LPCTSTR pstrName);
virtual void DoEvent(TEventUI& event);
virtual void DoPaint(HDC hDC, const RECT& rcPaint);
virtual void SetPos(RECT rc);
virtual void DoInit();
virtual void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);
//
const wstring& GetUrl()const ;
bool CanGoBack() const;
bool GoBack();
bool CanGoForward() const;
bool GoForward();
void StopLoad();
void Refresh();
wkeWebView GetWebView();
void SetLoadCallback(CWkeWebkitLoadCallback* pCallback);
CWkeWebkitLoadCallback* GetLoadCallback();
void Navigate(LPCTSTR lpUrl);
void LoadFile(LPCTSTR lpFile);
void LoadHtml(LPCTSTR lpHtml);
protected:
void StartCheckThread();
void StopCheckThread();
static void OnTitleChange(const struct _wkeClientHandler* clientHandler, const wkeString title);
static void OnUrlChange(const struct _wkeClientHandler* clientHandler, const wkeString url);
private:
static int m_bWebkitCount;
HANDLE m_hCheckThread;
wstring m_strUrl;
wkeWebView m_pWebView;
wkeClientHandler m_ClientHandler;
CWkeWebkitLoadCallback* m_pLoadCallback;
CWkeWebkitClientCallback* m_pClientCallback;
};
}
#endif//__UIWKEWEBKIT_H_
实现部分代码:
#include "StdAfx.h"
#include "UIWkeWebkit.h"
#pragma comment(lib, "DuiEx/wke/wke")
namespace DuiLib{
///
//网页加载状态监测线程
DWORD WINAPI CheckThreadFun(LPVOID lpParam)
{
CWkeWebkitUI* pWebkitUI=(CWkeWebkitUI*)lpParam;
wkeWebView pWebView=pWebkitUI->GetWebView();
if ( NULL == pWebView )
return 1;
CWkeWebkitLoadCallback* pCallback=pWebkitUI->GetLoadCallback();
if ( NULL == pCallback )
return 1;
bool bOver=false;
while( !pWebView->isLoaded() )
{
if ( !bOver && pWebView->isDocumentReady() )
{
pCallback->OnDocumentReady();
bOver=true;
}
if ( pWebView->isLoadFailed() )
{
pCallback->OnLoadFailed();
return 1;
}
::Sleep(30);
}
if ( pWebView->isLoadComplete() )
pCallback->OnLoadComplete();
return 0;
}
//
int CWkeWebkitUI::m_bWebkitCount=0;
CWkeWebkitUI::CWkeWebkitUI(void)
:m_pWebView(NULL)
,m_hCheckThread(NULL)
,m_pLoadCallback(NULL)
,m_pClientCallback(NULL)
{
if ( 0 == m_bWebkitCount )
wkeInit();
m_pWebView=wkeCreateWebView();
m_pWebView->setBufHandler(this);
m_ClientHandler.onTitleChanged =&CWkeWebkitUI::OnTitleChange;
m_ClientHandler.onURLChanged =&CWkeWebkitUI::OnUrlChange;
m_bWebkitCount++;
}
CWkeWebkitUI::~CWkeWebkitUI(void)
{
StopCheckThread();
m_pManager->KillTimer(this);
wkeDestroyWebView(m_pWebView);
m_bWebkitCount--;
if ( 0 == m_bWebkitCount )
wkeShutdown();
}
LPCTSTR CWkeWebkitUI::GetClass() const
{
return L"WkeWebkitUI";
}
LPVOID CWkeWebkitUI::GetInterface( LPCTSTR pstrName )
{
if( _tcscmp(pstrName, _T("WkeWebkit")) == 0 )
return static_cast<CWkeWebkitUI*>(this);
return CControlUI::GetInterface(pstrName);
}
void CWkeWebkitUI::DoEvent( TEventUI& event )
{
switch( event.Type )
{
case UIEVENT_SETFOCUS:
if ( m_pWebView ) m_pWebView->focus(); break;
case UIEVENT_KILLFOCUS:
if ( m_pWebView ) m_pWebView->unfocus(); break;
case UIEVENT_WINDOWSIZE:
if ( m_pWebView ) m_pWebView->resize(GET_X_LPARAM(event.lParam), GET_Y_LPARAM(event.lParam)); break;
case UIEVENT_CHAR:
{
if ( NULL == m_pWebView ) break;
unsigned int charCode = event.wParam;
unsigned int flags = 0;
if (HIWORD(event.lParam) & KF_REPEAT)
flags |= WKE_REPEAT;
if (HIWORD(event.lParam) & KF_EXTENDED)
flags |= WKE_EXTENDED;
bool bHandled=m_pWebView->keyPress(charCode, flags, false);
if ( bHandled )
return ;
break;
}
case UIEVENT_KEYDOWN:
{
if ( NULL == m_pWebView ) break;
unsigned int flags = 0;
if (HIWORD(event.lParam) & KF_REPEAT)
flags |= WKE_REPEAT;
if (HIWORD(event.lParam) & KF_EXTENDED)
flags |= WKE_EXTENDED;
bool bHandled=m_pWebView->keyDown(event.wParam, flags, false);
if ( event.wParam == VK_F5 )
Refresh();
if ( bHandled )
return ;
break;
}
case UIEVENT_KEYUP:
{
if ( NULL == m_pWebView ) break;
unsigned int flags = 0;
if (HIWORD(event.lParam) & KF_REPEAT)
flags |= WKE_REPEAT;
if (HIWORD(event.lParam) & KF_EXTENDED)
flags |= WKE_EXTENDED;
bool bHandled=m_pWebView->keyUp(event.wParam, flags, false);
if ( bHandled )
return ;
break;
}
case UIEVENT_CONTEXTMENU:
{
if ( NULL == m_pWebView ) break;
unsigned int flags = 0;
if (event.wParam & MK_CONTROL)
flags |= WKE_CONTROL;
if (event.wParam & MK_SHIFT)
flags |= WKE_SHIFT;
if (event.wParam & MK_LBUTTON)
flags |= WKE_LBUTTON;
if (event.wParam & MK_MBUTTON)
flags |= WKE_MBUTTON;
if (event.wParam & MK_RBUTTON)
flags |= WKE_RBUTTON;
bool handled = m_pWebView->contextMenuEvent(event.ptMouse.x, event.ptMouse.y, flags);
if ( handled )
return ;
break;
}
case UIEVENT_MOUSEMOVE:
case UIEVENT_BUTTONDOWN:
case UIEVENT_BUTTONUP:
case UIEVENT_RBUTTONDOWN:
case UIEVENT_DBLCLICK:
{
HWND hWnd=m_pManager->GetPaintWindow();
if ( event.Type == UIEVENT_BUTTONDOWN )
{
::SetFocus(hWnd);
SetCapture(hWnd);
}
else if ( event.Type == UIEVENT_BUTTONUP )
ReleaseCapture();
unsigned int flags = 0;
if (event.wParam & MK_CONTROL)
flags |= WKE_CONTROL;
if (event.wParam & MK_SHIFT)
flags |= WKE_SHIFT;
if (event.wParam & MK_LBUTTON)
flags |= WKE_LBUTTON;
if (event.wParam & MK_MBUTTON)
flags |= WKE_MBUTTON;
if (event.wParam & MK_RBUTTON)
flags |= WKE_RBUTTON;
UINT uMsg=0;
switch ( event.Type )
{
case UIEVENT_BUTTONDOWN: uMsg=WM_LBUTTONDOWN; break;
case UIEVENT_BUTTONUP: uMsg=WM_LBUTTONUP; break;
case UIEVENT_RBUTTONDOWN: uMsg=WM_RBUTTONDOWN; break;
case UIEVENT_DBLCLICK: uMsg=WM_LBUTTONDBLCLK; break;
case UIEVENT_MOUSEMOVE: uMsg=WM_MOUSEMOVE; break;
}
bool bHandled = m_pWebView->mouseEvent(uMsg, event.ptMouse.x-m_rcItem.left, \
event.ptMouse.y-m_rcItem.top, flags);
if ( bHandled )
return ;
break;
}
case UIEVENT_TIMER:
if ( m_pWebView )
m_pWebView->tick();
break;
case UIEVENT_SCROLLWHEEL:
{
POINT pt;
pt.x = LOWORD(event.lParam);
pt.y = HIWORD(event.lParam);
int nFlag=GET_X_LPARAM(event.wParam);
int delta = (nFlag==SB_LINEDOWN)?-120:120;
unsigned int flags = 0;
if (event.wParam & MK_CONTROL)
flags |= WKE_CONTROL;
if (event.wParam & MK_SHIFT)
flags |= WKE_SHIFT;
if (event.wParam & MK_LBUTTON)
flags |= WKE_LBUTTON;
if (event.wParam & MK_MBUTTON)
flags |= WKE_MBUTTON;
if (event.wParam & MK_RBUTTON)
flags |= WKE_RBUTTON;
bool handled = m_pWebView->mouseWheel(pt.x, pt.y, delta, flags);
if ( handled )
return ;
break;
}
default:
CControlUI::DoEvent(event); break;
}
}
void CWkeWebkitUI::DoPaint( HDC hDC, const RECT& rcPaint )
{
if ( m_pWebView )
{
RECT rcInsert;
IntersectRect(&rcInsert, &m_rcItem, &rcPaint);
m_pWebView->paint(hDC, rcInsert.left, rcInsert.top, \
rcInsert.right-rcInsert.left, rcInsert.bottom-rcInsert.top, \
rcInsert.left-m_rcItem.left, rcInsert.top-m_rcItem.top, true);
}
}
void CWkeWebkitUI::onBufUpdated( const HDC hdc,int x, int y, int cx, int cy )
{
RECT rcValide={ x, y, x+cx, y+cy };
::OffsetRect(&rcValide, m_rcItem.left, m_rcItem.top);
HWND hWnd=m_pManager->GetPaintWindow();
::InvalidateRect(hWnd, &rcValide, TRUE);
}
void CWkeWebkitUI::Navigate( LPCTSTR lpUrl )
{
if ( m_pWebView )
{
m_pWebView->loadURL(lpUrl);
StartCheckThread();
}
}
void CWkeWebkitUI::SetPos( RECT rc )
{
CControlUI::SetPos(rc);
if ( m_pWebView )
m_pWebView->resize(rc.right-rc.left, rc.bottom-rc.top);
}
void CWkeWebkitUI::DoInit()
{
if ( !m_strUrl.empty() )
Navigate(m_strUrl.c_str());
m_pManager->SetTimer(this, 100, 100);
}
void CWkeWebkitUI::StartCheckThread()
{
StopCheckThread();
m_hCheckThread=::CreateThread(NULL, 0, CheckThreadFun, this, 0, NULL);
}
void CWkeWebkitUI::StopCheckThread()
{
if ( m_hCheckThread )
{
if ( ::WaitForSingleObject(m_hCheckThread, 10) != WAIT_OBJECT_0 )
::TerminateThread(m_hCheckThread, 0);
::CloseHandle(m_hCheckThread);
m_hCheckThread = NULL;
}
}
bool CWkeWebkitUI::CanGoBack() const
{
return m_pWebView?m_pWebView->canGoBack():false;
}
bool CWkeWebkitUI::GoBack()
{
return m_pWebView?m_pWebView->goBack():false;
}
bool CWkeWebkitUI::CanGoForward() const
{
return m_pWebView?m_pWebView->canGoForward():false;
}
bool CWkeWebkitUI::GoForward()
{
return m_pWebView?m_pWebView->goForward():false;
}
void CWkeWebkitUI::StopLoad()
{
if ( m_pWebView )
m_pWebView->stopLoading();
}
void CWkeWebkitUI::Refresh()
{
if ( m_pWebView )
{
StopCheckThread();
m_pWebView->reload();
StartCheckThread();
}
}
wkeWebView CWkeWebkitUI::GetWebView()
{
return m_pWebView;
}
void CWkeWebkitUI::SetLoadCallback( CWkeWebkitLoadCallback* pCallback )
{
m_pLoadCallback=pCallback;
}
CWkeWebkitLoadCallback* CWkeWebkitUI::GetLoadCallback()
{
return m_pLoadCallback;
}
void CWkeWebkitUI::OnTitleChange( const struct _wkeClientHandler* clientHandler, const wkeString title )
{
}
void CWkeWebkitUI::OnUrlChange( const struct _wkeClientHandler* clientHandler, const wkeString url )
{
}
void CWkeWebkitUI::LoadFile( LPCTSTR lpFile )
{
if ( m_pWebView )
m_pWebView->loadFile(lpFile);
}
void CWkeWebkitUI::LoadHtml( LPCTSTR lpHtml )
{
if ( m_pWebView )
m_pWebView->loadHTML(lpHtml);
}
const wstring& CWkeWebkitUI::GetUrl() const
{
return m_strUrl;
}
void CWkeWebkitUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue )
{
if ( _tcscmp(pstrName, _T("url")) == 0 )
m_strUrl = pstrValue;
else
CControlUI::SetAttribute(pstrName, pstrValue);
}
}
解析:
主要处理的就是消息部分,把这个区域的那种消息分发给wke的接口去处理。另外就是加了个线程,检测网页加载状态,通过回调通知网页加载完成、失败、DOC完成(需要用户先初始化回调指针才会通知用户)。
控件定义完成后,我们需要来配置XML了:
<WkeWebkit name="webkit1" url="http://192.168.1.20:92/?mainctl=chrome&version=1.2.3"/>
放在一个布局里面,指定URL即可,你也可以加上其他属性然后在SetAttribute中对这些属性进行初始化。
运行程序,一个无窗口的chrome内核网页控件就展示出来了:
后记
wke加载网页只能用于展示以及JS交互处理,毕竟裁剪后还是要失去很多其他功能的,对于chrome内核10M的大小已经是相当的不错了,另外还有一个EaWebkit也是基于chrome的扩展,编译成DLL后只有6M左右,也是开源的,大家可以网上看看。