修改这些源码:cefclient_win.cc、cefclient_handler.cc、main_message_loop_multithreaded_win.cc。
第一步,封装出 bwCreateBrowser 方法,用于创建浏览器控件。
HINSTANCE g_hInstance;
HWND hwnd;
client::MainContextImpl * pMainContextImpl;
client::MainMessageLoopMultithreadedWin* looper;
extern "C" __declspec(dllexport) HWND bwGetHWNDForBrowser(void* pBrowser)
{
CefBrowser* browser = (CefBrowser*)pBrowser;
return browser?browser->GetHost()->GetWindowHandle():0;
}
extern "C" __declspec(dllexport) int bwCreateBrowser(HWND hParent, CHAR* URL, BrowserCallback bwCallback) {
if(IsWindow(hParent))
{
if(!looper)
{
// Enable High-DPI support on Windows 7 or newer.
//CefEnableHighDPISupport();
CefMainArgs main_args(g_hInstance);
void* sandbox_info = nullptr;
#if defined(CEF_USE_SANDBOX)
// Manage the life span of the sandbox information object. This is necessary
// for sandbox support on Windows. See cef_sandbox_win.h for complete details.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Parse command-line arguments.
CefRefPtr command_line = CefCommandLine::CreateCommandLine();
CefRefPtr app = new ClientAppBrowser();
TCHAR cmd_extra[MAX_PATH];
decorateCommandLine(cmd_extra, 0); // 修改命令行参数
// Execute the secondary process, if any.
int exit_code = CefExecuteProcess(main_args, app, sandbox_info);
if (exit_code >= 0)
return exit_code;
pMainContextImpl = new MainContextImpl(command_line, true); // Create the main context object.
CefSettings settings;
settings.Set({}, 0);
CefString(&settings.application_client_id_for_file_scanning).FromString("9A8DE24D-B822-4C6C-8259-5A848FEA1E68");
//context->PopulateSettings(&settings);
settings.command_line_args_disabled=0;
settings.no_sandbox = false;
settings.log_severity=LOGSEVERITY_DISABLE;
settings.multi_threaded_message_loop=1;
// Initialize CEF.
pMainContextImpl->Initialize(main_args, settings, app, sandbox_info);
// Initialize CEF.
looper = new MainMessageLoopMultithreadedWin();
looper->Run();
}
CefRefPtr g_handler = new ClientHandlerStd(0, URL);
CefBrowserSettings browser_settings;
CefWindowInfo window_info;
RECT rc;
GetWindowRect(hParent, &rc);
//设置父窗口信息
window_info.SetAsChild(hParent, rc);
CefRefPtr extra_info;
CefRefPtr request_context;
g_handler->bwCallback = bwCallback;
//最终创建控件
CefBrowserHost::CreateBrowser(window_info, g_handler, URL, browser_settings, extra_info, request_context);
}
}
使用 CefBrowserHost::CreateBrowser
也就是 cef_browser_host_create_browser
创建浏览器时,无法直接得到浏览器指针,需要在OnAfterCreated
回调中处理:
typedef const void * (__cdecl * BrowserCallback)(CefBrowser*);
void ClientHandler::OnAfterCreated(CefRefPtr browser) {
……
if(bwCallback)
{
bwCallback(browser);
}
NotifyBrowserCreated(browser);
}
关于 ClientHandler,每一个浏览器控件都会新建一个。兼容原生win32的while(GetMessage)消息循环,是通过MainMessageLoopMultithreadedWin
实现的,多个浏览器控件可以共用一个MainMessageLoopMultithreadedWin
,所以在封装bwCreateBrowser
之时我用looper
变量判断cef是否已经加载并初始化。
MainMessageLoopMultithreadedWin
实现的原理是看不懂,不过它会调用PostQuitMessage
终止消息循环,需要修改掉这一点。PostQuitMessage
怎么可能由一个控件调用?一般都是在主窗口WNDPROC
的case WM_DESTORY
中调用的。
主窗口WNDPROC
不能瞎写,比如始终返回true,或者在case WM_PAINT
返回true,会导致CPU占用飙升。
封装就是这个样子,如下在.exe中调用,很简单吧!
在wWinMain
入口中,先新建主窗口,得主窗口hwnd
。
regWndClass(L"TESTWND", CS_HREDRAW | CS_VREDRAW);
hwnd = ::CreateWindowEx(WS_EX_APPWINDOW , L"TESTWND" , NULL
, WS_OVERLAPPEDWINDOW | WS_VISIBLE , 0 , 0 , 840 , 680 , NULL , NULL , ::GetModuleHandle(NULL), NULL);
ShowWindow(hwnd, true);
再加载 cefclient.dll
,新建浏览器控件。
typedef const int (__cdecl * BWCREATEBROWSER)(HWND, const CHAR*, LONG_PTR);
typedef const HWND (__cdecl * BWGETHWNDFORBROWSER)(void*);
BWCREATEBROWSER bwCreateBrowser;
BWGETHWNDFORBROWSER bwGetHWNDForBrowser;
void onBrowserPrepared(void* browserPtr)
{
hBrowser = bwGetHWNDForBrowser(browserPtr);
// 拿到了浏览器控件的HWND!
}
// 加载cefclient.dll
// 首先拼接出库目录,现只支持与exe同路径,
// 未来考虑通过配置文件/环境变量来查找库文件
TCHAR buffer[MAX_PATH];
GetModuleFileName(NULL, buffer, MAX_PATH);
PathRemoveFileSpec(buffer);
PathAppend(buffer, L"cefclient.dll");
TCHAR LibBwgtPath[MAX_PATH];
PathCanonicalize(LibBwgtPath, buffer);
if(PathFileExists(LibBwgtPath))
{
const DWORD dwFlags = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "AddDllDirectory") != NULL ? LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0;
auto hLibBwgt = ::LoadLibraryEx(LibBwgtPath, NULL, dwFlags);
if(hLibBwgt) // 库文件加载成功
{
bwCreateBrowser = (BWCREATEBROWSER)GetProcAddress(hLibBwgt, "bwCreateBrowser");
bwGetHWNDForBrowser = (BWGETHWNDFORBROWSER)GetProcAddress(hLibBwgt, "bwGetHWNDForBrowser");
// 新建控件
bwCreateBrowser(hwnd, "www.bing.com", (LONG_PTR)onBrowserPrepared);
// 甚至可以在同一窗口再开一个浏览器控件 :
bwCreateBrowser(hwnd, "www.baidu.com", (LONG_PTR)onBrowserPrepared2);
}
}
最后是常规的消息循环。
MSG msg;
{
while (GetMessage(&msg, NULL, 0, 0)) {
//if ((looper->dialog_hwnd_ && IsDialogMessage(looper->dialog_hwnd_ , &msg)))
// continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
https://github.com/KnIfER/BrowserWidget/blob/master/BrowserUI/BrowserUI.cpp
https://github.com/KnIfER/BrowserWidget/blob/master/tests/cefclient/cefclient_win.cc
https://github.com/KnIfER/BrowserWidget/blob/master/tests/cefclient/browser/main_message_loop_multithreaded_win.cc
未完待续……
上一篇 初识Libcef
下一篇 资源拦截替换、JS调用C++ Native、首次运用