Using version 102.0.10, which is added to our project using NuGet.
Project is a Win32 64 bit application, using Windows Template Library.
Running CEF with multi_threaded_message_loop = 1
We have a "lightweight" window class CCEFWindow, which derives from CWindowImpl, CefClient, CefLoadHandler and CefLifespanHandler
CCEFWindow has the following header:
- Code: Select all
/// <summary>
/// Holds a CEF Browser instance, and implements various CEF callback intefaces
/// </summary>
class CCEFWindow :
public CWindowImpl<CCEFWindow>,
public CefClient,
public CefLoadHandler,
public CefLifeSpanHandler
{
public:
CCEFWindow(const char *pName);
virtual ~CCEFWindow();
BEGIN_MSG_MAP(CCEFWindow)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_SIZE, OnClose)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
END_MSG_MAP()
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
// CefClient Overrides to provide handler interfaces
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override;
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
// CefLoadHandler
virtual void OnLoadStart(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, TransitionType transition_type);
// CefLifeSpanHandler
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) override;
void SetBackgroundColor(COLORREF bgColor);
void LoadURL(const char *pURI);
std::string GetURL();
// Call this when you are getting ready to shutdown
void Destroy();
bool IsDestroyed();
void WaitForTerminate();
private:
void UpdateLayout();
std::string m_name;
std::string m_initialURL;
CefRefPtr<CefBrowser> m_browser;
std::optional<int> m_browserID;
HANDLE m_browserOnBeforeCloseEvent;
bool m_isBrowserClosing;
bool m_hasDoCloseRun;
bool m_hasOnBeforeClose;
CefBrowserSettings m_browserSettings;
CefWindowInfo m_cefWinInfo;
IMPLEMENT_REFCOUNTING(CCEFWindow);
};
We have another window which holds onto a refcounted pointer to a CCEFWindow, which is created this way:
[code]
m_pwndCEFWindow = new CCEFWindow(AppLocation.path().c_str());
m_pwndCEFWindow->SetBackgroundColor(APP_COLOR_BACKGROUND);
m_pwndCEFWindow->Create(m_hWnd, CRect(CBrowserDefs::MarginX, GetBrowserY(), 800, 600), NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE);[/code]
In the OnCreate method of the CCefWindow, we create the browser this way:
- Code: Select all
LRESULT CCEFWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandled)
{
RECT rc = RECT{ 0 };
GetClientRect(&rc);
auto rect = TO_CEF_RECT(rc);
m_cefWinInfo.SetAsChild(m_hWnd, rect);
CefBrowserHost::CreateBrowser(m_cefWinInfo, this, "", m_browserSettings, nullptr, nullptr);
return 0;
}
When we get a close message in the most top level window, we make a call which eventually makes its way down to our CCefWindow::Destroy() method, which looks like this:
- Code: Select all
void CCEFWindow::Destroy()
{
if (m_browser && ! m_isBrowserClosing)
{
m_browser->GetHost()->CloseBrowser(true);
m_isBrowserClosing = true;
}
}
We see that our CCefWindow::DoClose method is getting called, which we return false from:
- Code: Select all
bool CCEFWindow::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
m_hasDoCloseRun = true;
return false;
}
We see that DoClose has been called, and exit the main message when we get the second WM_CLOSE, see that DoClose was called, and then exit the message loop.
We call CefShutdown, and after a little time get and exception. With the stack:
App.exe!cef::logging::LogMessage::~LogMessage() Line 188 C++
App.exe!shutdown_checker::AssertNotShutdown() Line 32 C++
App.exe!`anonymous namespace'::life_span_handler_on_before_close(_cef_life_span_handler_t * self, _cef_browser_t * browser) Line 211 C++
If I breakpoint right before calling CefShutdown, I'm finding that the thread "CrBrowserMain" , which I believe is the UI thread, is stuck on NtUserMsgWaitForMultipleObjects.
I believe that CrBrowserMain is holding on to a reference to the CCCEFWindow which implements the callbacks because it hasn't called OnBeforeClose yet.
Note, this only happens in debug, and I can loop forever with a one second sleep call, waiting for OnBeforeClose to get called, and it never does.
It seems like the UI thread is waiting for something, but I don't know what.