222 lines
6.0 KiB
C++
222 lines
6.0 KiB
C++
#include "stdafx.h"
|
||
#include "cef_manager.h"
|
||
#include "include/wrapper/cef_closure_task.h"
|
||
#include "include/base/cef_bind.h"
|
||
|
||
#include "cef_control/app/client_app.h"
|
||
#include "cef_control/handler/browser_handler.h"
|
||
|
||
namespace nim_comp
|
||
{
|
||
|
||
BOOL CefMessageLoopDispatcher::IsIdleMessage(const MSG* pMsg)
|
||
{
|
||
switch (pMsg->message)
|
||
{
|
||
case WM_MOUSEMOVE:
|
||
case WM_NCMOUSEMOVE:
|
||
case WM_PAINT:
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
bool CefMessageLoopDispatcher::Dispatch(const MSG &msg)
|
||
{
|
||
static BOOL bDoIdle = TRUE;
|
||
|
||
TranslateMessage(&msg);
|
||
DispatchMessage(&msg);
|
||
|
||
if (IsIdleMessage(&msg))
|
||
{
|
||
bDoIdle = TRUE;
|
||
}
|
||
|
||
while (bDoIdle && !::PeekMessage(const_cast<MSG*>(&msg), NULL, 0, 0, PM_NOREMOVE))
|
||
{
|
||
CefDoMessageLoopWork();
|
||
bDoIdle = FALSE;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// 发现一个非常奇葩的bug,离屏渲染+多线程消息循环模式下,在浏览器对象上右击弹出菜单,是无法正常关闭的
|
||
// 翻cef源码后发现菜单是用TrackPopupMenu函数创建的,在MSDN资料上查看后发现调用TrackPopupMenu前
|
||
// 需要给其父窗口调用SetForegroundWindow。但是在cef源码中没有调用
|
||
// 最终翻cef源码后得到的解决方法是在cef的UI线程创建一个窗口,这个窗体的父窗口必须是在主程序UI线程创建的
|
||
// 这样操作之后就不会出现菜单无法关闭的bug了,虽然不知道为什么但是bug解决了
|
||
void FixContextMenuBug(HWND hwnd)
|
||
{
|
||
CreateWindow(L"Static", L"", WS_CHILD, 0, 0, 0, 0, hwnd, NULL, NULL, NULL);
|
||
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
||
}
|
||
|
||
///////////////////////////////////////////////////////////////////////////////////
|
||
CefManager::CefManager()
|
||
{
|
||
browser_count_ = 0;
|
||
is_enable_offset_render_ = true;
|
||
}
|
||
|
||
void CefManager::AddCefDllToPath()
|
||
{
|
||
#if !defined(SUPPORT_CEF)
|
||
return;
|
||
#endif
|
||
|
||
TCHAR path_envirom[4096] = { 0 };
|
||
GetEnvironmentVariable(L"path", path_envirom, 4096);
|
||
|
||
std::wstring cef_path = nbase::win32::GetCurrentModuleDirectory();
|
||
#ifdef _DEBUG
|
||
//cef_path += L"cef_debug"; // 现在即使在debug模式下也使用cef release版本的dll,为了屏蔽掉cef退出时的中断,如果不需要调试cef的功能不需要使用debug版本的dll
|
||
cef_path += L"cef";
|
||
#else
|
||
cef_path += L"cef";
|
||
#endif
|
||
if (!nbase::FilePathIsExist(cef_path, true))
|
||
{
|
||
MessageBox(NULL, L"请解压Cef.rar压缩包", L"提示", MB_OK);
|
||
exit(0);
|
||
}
|
||
std::wstring new_envirom(cef_path);
|
||
new_envirom.append(L";").append(path_envirom);
|
||
SetEnvironmentVariable(L"path", new_envirom.c_str());
|
||
|
||
// 解决播放flash弹出黑框的问题
|
||
// https://blog.csdn.net/zhuhongshu/article/details/77482985
|
||
std::wstring cmd_path = cef_path + L"\\dummy_cmd.exe";
|
||
SetEnvironmentVariable(L"ComSpec", cmd_path.c_str());
|
||
}
|
||
|
||
// Cef的初始化接口,同时备注了使用各个版本的Cef时遇到的各种坑
|
||
// Cef1916版本较稳定,各个功能使用正常,但是某些在debug模式网页打开时会出中断警告(但并不是错误),可能是因为对新html标准支持不够,但是在release模式下正常使用
|
||
// Cef2357版本无法使用,当程序处理重定向信息并且重新加载页面后,渲染进程会崩掉
|
||
// Cef2526、2623版本对各种新页面都支持,唯一的坑就是debug模式在多线程消息循环开启下,程序退出时会中断,但是release模式正常。
|
||
// (PS:如果开发者不使用负责Cef功能的开发,可以切换到release模式的cef dll文件,这样即使在deubg下也不会报错,修改AddCefDllToPath代码可以切换到release目录)
|
||
bool CefManager::Initialize(const std::wstring& app_data_dir, CefSettings &settings, bool is_enable_offset_render /*= true*/)
|
||
{
|
||
#if !defined(SUPPORT_CEF)
|
||
return true;
|
||
#endif
|
||
is_enable_offset_render_ = is_enable_offset_render;
|
||
|
||
CefMainArgs main_args(GetModuleHandle(NULL));
|
||
CefRefPtr<ClientApp> app(new ClientApp);
|
||
|
||
// 如果是在子进程中调用,会堵塞直到子进程退出,并且exit_code返回大于等于0
|
||
// 如果在Browser进程中调用,则立即返回-1
|
||
int exit_code = CefExecuteProcess(main_args, app.get(), NULL);
|
||
if (exit_code >= 0)
|
||
return false;
|
||
|
||
GetCefSetting(app_data_dir, settings);
|
||
|
||
bool bRet = CefInitialize(main_args, settings, app.get(), NULL);
|
||
|
||
if (is_enable_offset_render_)
|
||
{
|
||
HWND hwnd = CreateWindow(L"Static", L"", WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
|
||
CefPostTask(TID_UI, base::Bind(&FixContextMenuBug, hwnd));
|
||
}
|
||
|
||
return bRet;
|
||
}
|
||
|
||
void CefManager::UnInitialize()
|
||
{
|
||
#if !defined(SUPPORT_CEF)
|
||
return;
|
||
#endif
|
||
CefShutdown();
|
||
}
|
||
|
||
bool CefManager::IsEnableOffsetRender() const
|
||
{
|
||
return is_enable_offset_render_;
|
||
}
|
||
|
||
nbase::Dispatcher* CefManager::GetMessageDispatcher()
|
||
{
|
||
return &message_dispatcher_;
|
||
}
|
||
|
||
void CefManager::AddBrowserCount()
|
||
{
|
||
browser_count_++;
|
||
}
|
||
|
||
void CefManager::SubBrowserCount()
|
||
{
|
||
browser_count_--;
|
||
ASSERT(browser_count_ >= 0);
|
||
}
|
||
|
||
int CefManager::GetBrowserCount()
|
||
{
|
||
return browser_count_;
|
||
}
|
||
|
||
void CefManager::PostQuitMessage(int nExitCode)
|
||
{
|
||
#if !defined(SUPPORT_CEF)
|
||
::PostQuitMessage(nExitCode);
|
||
return;
|
||
#endif
|
||
|
||
// 当我们需要结束进程时,千万不要直接调用::PostQuitMessage,这是可能还有浏览器对象没有销毁
|
||
// 应该等所有浏览器对象都销毁后再调用::PostQuitMessage
|
||
if (browser_count_ == 0)
|
||
{
|
||
nbase::ThreadManager::PostTask(kThreadUI, [nExitCode]()
|
||
{
|
||
::PostQuitMessage(nExitCode);
|
||
});
|
||
}
|
||
else
|
||
{
|
||
auto cb = [nExitCode]()
|
||
{
|
||
CefManager::GetInstance()->PostQuitMessage(nExitCode);
|
||
};
|
||
|
||
nbase::ThreadManager::PostDelayedTask(kThreadUI, cb, nbase::TimeDelta::FromMilliseconds(500));
|
||
}
|
||
}
|
||
|
||
void CefManager::GetCefSetting(const std::wstring& app_data_dir, CefSettings &settings)
|
||
{
|
||
if (false == nbase::FilePathIsExist(app_data_dir, true))
|
||
nbase::CreateDirectory(app_data_dir);
|
||
|
||
settings.no_sandbox = true;
|
||
|
||
// 设置localstorage,不要在路径末尾加"\\",否则运行时会报错
|
||
CefString(&settings.cache_path) = app_data_dir + L"CefLocalStorage";
|
||
|
||
// 设置debug log文件位置
|
||
CefString(&settings.log_file) = app_data_dir + L"cef.log";
|
||
|
||
// 调试模型下使用单进程,但是千万不要在release发布版本中使用,官方已经不推荐使用单进程模式
|
||
// cef1916版本debug模式:在单进程模式下程序退出时会触发中断
|
||
#ifdef _DEBUG
|
||
settings.single_process = true;
|
||
#else
|
||
settings.single_process = false;
|
||
#endif
|
||
|
||
// cef2623、2526版本debug模式:在使用multi_threaded_message_loop时退出程序会触发中断
|
||
// 加入disable-extensions参数可以修复这个问题,但是会导致一些页面打开时报错
|
||
// 开启Cef多线程消息循环,兼容nbase库消息循环
|
||
settings.multi_threaded_message_loop = true;
|
||
|
||
// 开启离屏渲染
|
||
settings.windowless_rendering_enabled = is_enable_offset_render_;
|
||
}
|
||
|
||
} |